C++Primer笔记之复制控制
复制控制这一节需要注意的地方不多,主要有以下几点:
1、定义自己的复制构造函数
什么时候需要定义自己的复制构造函数,而不用系统提供的,主要遵循以下的经验说明:
某些类必须对复制对象时发生的事情加以控制,这样的类(1)经常有一个数据成员是指针,(2)有成员在构造函数中分配的其他资源;
而另一些类在创建对象时必须做一些特定的工作。
2、禁止复制
有些类是需要禁止复制的,如iostream类就不允许复制,但编译器始终都会默认合成一个,但还是有办法的:
为了防止复制,类只要显示声明其复制构造函数为private就行了。
然而,这样,类的友元和成员仍可以进行复制,如果想要连友元和成员中的复制也禁止,就可以声明一个private复制构造函数但不对其定义,这是合法的。
3、析构函数的异同
与复制构造函数或赋值操作符不同,编译器总是会为我们合成一个析构函数,合成析构函数按照对象创建时的逆序撤销每个非static成员。
析构函数与复制构造函数或赋值操作符之间的一个重要区别是:即使我们编写了自己的析构函数,合成析构函数仍然会运行。合成析构函数。
4、智能指针
智能指针是由于在有指针成员的类中,指针所指向的对象是共享的,防止出现悬垂指针而提出的一种管理指针的办法。
为了阐述智能指针,我们来看一个例子:
class HasPtr{
public:
HasPtr(int *p, int i):uptr(p), val(i) {} //p是指向int型数组的指针
HasPtr& operator = (const HasPtr& rhs);
int *getPtr() {
return uptr->ip;
}
int getValue() {
return val;
}
void setPtr(int *p) {
uptr->ip = p;
}
void setValue(int i) {
val = i;
}
int getPtrValue() const {
return *uptr->ip;
}
void setPtrValue(int i) {
*uptr->ip = i;
}
private:
int *uptr;
int val;
};
如上一个类,如果我像这样调用:
int obj = 0;
HasPtr ptr1(&obj, 42);
HasPtr ptr2(ptr1);
ptr1和ptr2的值相同,改变任意一个的值都可以改变其共享对象的值。
再看,可能出现悬垂指针的情况:
int *ip = new int(42);
HasPtr ptr(ip, 10);
delete ip;
ptr.set_ptr_val(0); //Disaster!!!
这里ip和ptr中的指针指向了同一对象,删除了该对象时,ptr中的指针不再指向有效对象,但是你又不知道该对象不在了,所以,这样就出现了悬垂指针。
所以,定义智能指针能有效地解决这个问题,为了避免多个指针共享一个对象时撤销出现的悬垂指针问题,定义智能指针类的主要功能就是来保证在撤销指向对象的最后一个指针时才删除该对象。
为了统计指向共享对象的指针的数量,引入使用计数,用其跟踪该类有多少个对象共享同一指针,但使用计数为0时,删除对象。在设计上,将使用计数设计成一个单独的类,用来封装使用计数和相关指针。
如下:
//仅由HasPtr使用的U_Ptr类,用于封装使用计数和相关指针
class U_Ptr {
friend class HasPtr; //定义成友元
size_t use;
int *ip;
U_Ptr(int *p):ip(p), use() {}
~U_Ptr() { delete ip; }
};
引用上面的那个类,不同的是,让HasPtr类保存一个指向U_Ptr对象的指针,U_Ptr对象再指向实际的int基础对象。如下:
class HasPtr{
public:
HasPtr(int *p, int i):uptr(new U_Ptr(p)), val(i) {} //p是指向int型数组的指针
HasPtr(const HasPtr& orig):uptr(orig.uptr),val(orig.val) {
++uptr->use; //复制完成将使用计数加1
}
HasPtr& operator = (const HasPtr& rhs);
~HasPtr() {
if(--uptr->use == 0) //检查假如只有一个对象在共享该指针,则删除
delete uptr;
}
int *getPtr() {
return uptr->ip;
}
int getValue() {
return val;
}
void setPtr(int *p) {
uptr->ip = p;
}
void setValue(int i) {
val = i;
}
int getPtrValue() const {
return *uptr->ip;
}
void setPtrValue(int i) {
*uptr->ip = i;
}
private:
U_Ptr *uptr;
int val;
};
其中,红色部分是改动过的。赋值操作符像下面这样:
HasPtr& HasPtr::operator =(const HasPtr &rhs)
{
++ rhs.uptr->use;
if (--uptr->use == )
delete uptr;
uptr = rhs.uptr;
val = rhs.val;
return *this;
}
还有一种方法是定义值类型:
这种思路很简单,就是给指针成员提供值语义,复制值型对象时,会得到一个不同的新副本,对副本所做的改变不会反映在原有对象上。如下,可以对赋值操作符做点改变:
HasPtr& HasPtr::operator =(const HasPtr &rhs)
{
*uptr = *rhs.uptr;
val = rhs.val;
return *this;
}
C++Primer笔记之复制控制的更多相关文章
- C++ Primer 有感(复制控制)
1.不管类是否定义了自己的析构函数,编译器都 自动执行类中非static数据成员的析构函数. 2.如果我们没有定义复制构造函数,编译器就会为我们合成一个.合成复制构造函数的行为是,执行逐个成员初始化, ...
- C++ Primer 学习笔记_67_面向对象编程 --转换与继承、复制控制与继承
面向对象编程 --转换与继承.复制控制与继承 I.转换与继承 引言: 由于每一个派生类对象都包括一个基类部分,因此能够像使用基类对象一样在派生类对象上执行操作. 对于指针/引用,能够将派生类对象的指针 ...
- C++ Primer 随笔 Chapter 13 复制控制
1.复制控制包含的内容:复制构造函数.赋值操作符.析构函数 2.复制构造函数: a. 定义:只有单个形参,而且该形参是对本类类型的引用,这样的构造函数被成为复制构造函数 b. 适用情况: (1)根据一 ...
- 稍微深入点理解C++复制控制【转】
通过一个实例稍微深入理解C++复制控制过程,参考资料<C++ primer>,介绍点基本知识: 1.在C++中类通过特殊的成员函数:复制构造函数.赋值操作符和析构函数来控制复制.赋值和撤销 ...
- C++ Primer笔记
C++ Primer笔记 ch2 变量和基本类型 声明 extern int i; extern int i = 3.14;//定义 左值引用(绑定零一变量初始值,别名) 不能定义引用的引用:引用必须 ...
- C++ 复制控制之复制构造函数
7月26日更新: 过了这么长的时间回过头来看,发现文章中有几个点说错(用红字标出): 构造函数不是只有唯一一个参数,它也可以是多参数形式,其第二参数及后继以一个默认值供应. 不是没有声明复制控制函数时 ...
- TJI读书笔记09-访问控制权限
TJI读书笔记09-访问控制权限 包,package和import 权限修饰符 接口和实现 类的访问权限控制 首先问一个问题,为什么要有访问控制权限? 安全,这当然是一个很重要的原因. 让类库的使用者 ...
- OGG学习笔记04-OGG复制部署快速参考
OGG学习笔记04-OGG复制部署快速参考 源端:Oracle 10.2.0.5 RAC + ASM 节点1 Public IP地址:192.168.1.27 目标端:Oracle 10.2.0.5 ...
- 强化学习读书笔记 - 10 - on-policy控制的近似方法
强化学习读书笔记 - 10 - on-policy控制的近似方法 学习笔记: Reinforcement Learning: An Introduction, Richard S. Sutton an ...
随机推荐
- 做u盘启动重装系统 进winPE 出现 cdboot:couldn't find ntldr 解决办法
公司的QA本来用的ubuntu系统 觉得不是很好使 就找我重装win10系统 之前有重装过系统 就信心满满的答应了 我拿出U盘 把U盘格式化了下 去下载了个雨林木风的win10 系统(ISO文件) ...
- Could not GET 'https://dl.google.com/dl/android/maven2/com/android/tools/build/gradle/3.1.2/gradle-3
参考 https://blog.csdn.net/verkery6/article/details/80797705
- Linux基础知识回顾及BASH学习
2019-2020-030189224 <网络攻防技术与实践>第一周学习总结 Linux基础知识错题回顾 1.Linux中使用(B)命令新建空白文件. A .mkdir B .touch ...
- linux下的音量控制器alsamixer 桌面v7
转载 http://blog.sina.com.cn/s/blog_0ca103850102vpml.html 耳机 插后边 line out 耳机插前边 模拟耳机 声卡自带工具 linux下的音量控 ...
- 关于弹性布局的 flex-grow的用法和flex-shrink的用法
1.首先 flex-grow设置在子项目上 2.flex-grow默认值为0,如果为值1的时候就会撑满 3.flex-grow还可以给其中的一个子元素单独设置,设置为2,其它的则为1或者2都可以,具体 ...
- Myisam 和 Innodb 区别
MySQL默认采用的是MyISAM. MyISAM不支持事务,而InnoDB支持.InnoDB的AUTOCOMMIT默认是打开的,即每条SQL语句会默认被封装成一个事务,自动提交,这样会影响速度,所以 ...
- solr7.7.0搜索引擎使用(一)(下载安装)
一.下载安装 可以直接在官网下载地址:https://lucene.apache.org/solr/ 解压之后,目录结构如下图,bin里边提供部署的文件,contrib提供额外的jar包,docs提供 ...
- ubuntu16.04 下安装 visual studio code 以及利用 g++ 运行 c++程序
参考链接:1. http://www.linuxidc.com/Linux/2016-07/132798.htm(安装vs code) 2.https://blog.csdn.net/qq_28598 ...
- ABP框架系列之九:(Abp-Session-会话)
Introduction ASP.NET Boilerplate provides IAbpSession interface to obtain current user and tenant wi ...
- mysql8.0 linux 安装
1.下载 mysql-8.0.15-linux-glibc2.12-x86_64.tar.xz 2.解压 tar -xvf mysql-8.0.15-linux-glibc2.12-x86_64.ta ...