C++ Primer笔记(十一)复制控制

1、如果没有显式定义复制构造函数、赋值操作符,编译器通常为我们定义。

2、复制构造函数是一种特殊的构造函数,具有单个形参,该形参(常用const修饰)为对该类类型的引用。

当定义一个新对象并用一个同类型的对象对它进行初始化时,将显式使用复制构造函数;当将该类型的对象传递给函数或从函数返回该类型的对象时将隐式使用复制构造函数。


3、复制构造函数、赋值操作符和析构函数总称为复制控制。

编译器自动实现这些操作,但类也可以定义自己的版本。通常编译器合成的复制控制函数是非常精练的,它们只做必需的工作。但对某些类而言,依赖于默认定义会导致灾难。

4、有一种特别常见的情况需要类定义自己的复制控制成员:类具有指针成员。


5、复制构造函数可以用于:

1)根据另一个同类型的对象显式或隐式初始化一个对象。

2)复制一个对象,将它作为实参传给一个函数。

3)从函数返回时复制一个对象。

4)初始化顺序容器中的元素。

5)根据元素初始化式列表初始化数组元素。


6、复制构造函数可用于初始化顺序容器中的元素,如vector<string> svec(5);

此方式使用了默认构造函数和复制构造函数。编译器首先使用string的默认构造函数创建一个临时值来初始化 svec然后使用复制构造函数将临时值复制到svec的每一个元素。


7、对于类类型对象,只有指定单个实参或显式创建一个临时对象用于复制时,才使用复制初始化。

与默认构造函数不同,即使我们定义了其他构造函数,编译器也会合成复制构造函数。合成复制构造函数的行为是:执行逐个成员的初始化,将对新对象初始化为原对象的副本。但定义复制构造函数后,编译器就不在合成默认构造函数。

8、逐个成员指的是:

编译器将现有对象的每个非static成员,依次复制到正创建的对象。只有一个例外,每个成员的类型决定了复制该成员的含义。合成复制构造函数直接复制内置类型成员的值,类类型成员使用该类的复制构造函数进行复制。

数组成员是个例外,如果类具有数组成员,合成复制构造函数将复制数组,它将复制数组的每一个元素。注意指针仅仅复制字面值。


9、复制构造函数在向函数传递该类型的对象和从函数返回该类对象时隐式调用,因此不应该将复制构造函数指定为explicit。

只包含类类型或内置类型(但不包括指针类型的)的类无需显式的定义复制构造函数。当类有一个成员为指针类型或有成员在构造函数中分配的其他资源,这两种情况下都必须定义复制构造函数。

10、有些类需要完全禁止复制,如iostream类。

如果想禁止复制,类必须显式声明其复制构造函数为private。此时编译器将拒绝用户代码,任何进行复制的尝试。但是此时在友元和类的成员中还可以进行复制,如果也想禁止它们,可以声明一个private复制构造函数,但不对其定义。

注意是定义,而不是把它定义为一个空的复制构造函数。


11、声明而不定义成员函数是合法的。

但是使用未定义成员函数的任何尝试都将导致链接失败。声明private的复制构造函数后,用户代码任何复制类类型对象的尝试都将导致语法错误,而在友元和成员函数的调用将导致链接错误。

12、如果定义了复制构造函数,编译器就不会合成默认构造函数了。

13、类定义了对该类类型赋值时会发生什么。如果类没有定义自己的赋值操作符,则编译器会合成一个。

14、赋值操作符的返回类型返回对左/右操作数的引用。且赋值操作符函数必须为类的成员函数。


15、合成赋值操作符函数与合成复制构造函数操作类似,它也会执行逐个成员赋值。

16、一般而言如果类需要定义复制构造函数,它也会需要赋值操作符。

17、构造函数的一个用途是自动获取资源,如,构造函数可以分配一个堆空间或打开一个文件。析构函数则用于在对象销毁时完成资源的回收。


18、动态分配的对象只有在指向该对象的指针被删除时才撤销。当动态分配对象的引用或指针超出作用域时,不会运行析构函数。只有显式调用delete才会调用析构函数。但进程结束时会撤销。

19、如果类需要析构函数,则它也需要赋值操作符和复制构造函数,这被称为三法则。

编译器总会为我们合成一个析构函数,合成的析构函数按对象创建时的逆序撤销每个非static成员。对于类类型的成员,合成析构函数会调用该成员的析构函数来撤销对象。

20、合成构造函数并不删除指针成员所指向的对象。所以一般定义自己的析构函数以释放那些资源。


21、析构函数与复制构造函数或赋值运算符的一个重要区别是:即使我们编写了自己的析构函数,合成构造函数仍然运行。析构函数只能有一个。

注意:这里使用的是运行。合成构造函数调用成员为类类型的析构函数,用于撤销成员。

22、包含指针的类需要特别注意复制控制,原因是复制指针时只复制指针中的地址,而不会复制指针指向的对象。


23、大多数C++类采用以下三种方法之一管理指针成员:

1)指针成员采取常规指针型行为。即不处理。

2)使用智能指针。采取引用计数来控制来管理贡献对象。

3)指针指向的对象唯一,每个对象有自己的副本。


智能指针代码:

[cpp]  view plain copy
  1. class Ptr  
  2. {  
  3. public:  
  4.     size_t use;  
  5.     int *p;  
  6. public:  
  7.     Ptr(int *pp)  
  8.         :use(1),p(pp)  
  9.     {  
  10.   
  11.     }  
  12.     ~Ptr()  
  13.     {  
  14.         delete p;  
  15.     }  
  16. };  
  17. class auto_ptr  
  18. {  
  19. public:  
  20.     Ptr *ptr;  
  21. public:  
  22.     auto_ptr(int *p)  
  23.     {  
  24.         ptr=new Ptr(p);  
  25.     }  
  26.     auto_ptr(auto_ptr& a)  
  27.     {  
  28.         ptr=a.ptr;  
  29.         a.ptr->use++;  
  30.     }  
  31.     auto_ptr &operator=(auto_ptr &rhs)  
  32.     {  
  33.         rhs.ptr->use++;  
  34.         if(--ptr->use==0)  
  35.             delete ptr;  
  36.         ptr=rhs.ptr;  
  37.         return *this;  
  38.     }  
  1. ~auto_ptr()  
  1. {  
  1. if(--ptr->use==0)  
  1. delete ptr;  
  1. }  
  1. };  
  2. int main(int argc,char**argv)  
  3. {  
  4.     auto_ptr ap=new int(3);  
  5.     cout<<"ap:"<<ap.ptr->use<<endl;  
  6.     auto_ptr ap2(ap);  
  7.     auto_ptr ap3(ap);  
  8.     cout<<"ap:"<<ap.ptr->use<<endl;  
  9.     cout<<"ap2:"<<ap2.ptr->use<<endl;  
  10.     cout<<"ap3:"<<ap3.ptr->use<<endl;  
  11.     auto_ptr ad1=new int(4);  
  12.     cout<<"ad1:"<<ad1.ptr->use<<endl;  
  13.     ap=ad1;  
  14.     ap2=ad1;  
  15.     cout<<"赋值之后:"<<endl;  
  16.     cout<<"ad1:"<<ad1.ptr->use<<endl;  
  17.     cout<<"ap:"<<ap.ptr->use<<endl;  
  18.     cout<<"ap2:"<<ap2.ptr->use<<endl;  
  19.     cout<<"ap3:"<<ap3.ptr->use<<endl;  
  20.     return 0;  
  21. }  

以上参考自http://blog.youkuaiyun.com/ithzhang/article/details/7595295
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值