【C++ Primer】复制控制
十三、复制控制
1. 复制构造函数
类中的成员函数都默认为inline类型,所以即使在类定义体内的函数声明显示定义为inline类型,在进行函数定义时也能够将inline进行省略。
// 复制构造函数应该为常量引用类型,如果允许传值参数会造成无限循环调用从而导致内存溢出。
CopyConstruct(const CopyConstruct& a){value = a.value;}
复制构造函数可用于初始化顺序容器中的元素,如vector<string> svec(5);
这种方式使用了默认构造函数和复制构造函数。编译器首先使用string的默认构造函数创建一个临时值来初始化svec然后使用复制构造函数将临时值复制到svec的每一个元素。
数组成员是个例外,如果类具有数组成员,合成复制构造函数将复制数组,它将复制数组的每一个元素。注意指针仅仅复制字面值。
复制构造函数在向函数传递该类型的对象和从函数返回该类对象时隐式调用,因此不应该将复制构造函数指定为explicit。
只包含类类型或内置类型(但不包括指针类型的)的类无需显式的定义复制构造函数。当类有一个成员为指针类型或有成员在构造函数中分配的其他资源,这两种情况下都必须定义复制构造函数。
有些类需要完全禁止复制,如iostream类。如果想禁止复制,类必须显式声明其复制构造函数为private。此时编译器将拒绝用户代码,任何进行复制的尝试。但是此时在友元和类的成员中还可以进行复制,如果也想禁止它们,可以声明一个private复制构造函数,但不对其定义。注意是定义。而不是把它定义为一个空的复制构造函数。
声明而不定义成员函数是合法的。但是使用未定义成员函数的任何尝试都将导致链接失败。声明private的复制构造函数后,用户代码任何复制类类型对象的尝试都将导致语法错误,而在友元和成员函数的调用将导致链接错误。
如果定义了复制构造函数,编译器就不会合成默认构造函数了。
2. 赋值操作符
如果类没有定义自己的赋值操作符,则编译器会合成一个。
赋值操作符的返回类型返回对左/右操作数的引用。且赋值操作符函数必须为类的成员函数,因为赋值必须是类的成员。参数列表中第一个形参为this指针,被省略了。
合成赋值操作符函数与合成复制构造函数操作类似,它也会执行逐个成员赋值。
一般而言如果类需要定义复制构造函数,它也会需要赋值操作符。
3. 析构函数
动态分配的对象只有在该对象的指针被删除时才撤销。当动态分配对象的引用或指针超出作用域时,不会运行析构函数。只有显式调用delete才会调用析构函数。但进程结束时会撤销。
如果类需要析构函数,则它也需要赋值操作符和复制构造函数,这被称为三法则。
编译器总会为我们合成一个析构函数,合成的析构函数按对象创建时的逆序撤销每个非static成员。对于类类型的成员,合成析构函数会调用该成员的析构函数来撤销对象。
合成析构函数并不删除指针成员所指向的对象。所以一般定义自己的析构函数以释放那些资源。
析构函数与复制构造函数或赋值运算符的一个重要区别是:即使我们编写了自己的析构函数,合成构造函数仍然运行。析构函数只能有一个。注意:这里使用的是运行。合成构造函数调用成员为类类型的析构函数,用于撤销成员。
4. 管理指针成员
大多数C++类采用以下三种方法之一管理指针成员:
1:指针成员采取常规指针型行为。即不处理。
2:使用智能指针。采取引用计数来控制来管理贡献对象。
class U_Ptr {
friend class HasPtr;
int *ip;
size_t use;
U_Ptr(int *p): ip(p), use(1) { }
~U_Ptr() { delete ip; }
};
3:指针指向的对象唯一,每个对象有自己的副本。
郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。