问题聚焦:
负责拷贝的两个操作:拷贝构造函数和重载赋值操作符。
一句话总结,确保被拷贝对象的所有成员变量都做一份拷贝。

Demo
 
void logCall(const std::string& funcName);  // log函数

class Date{ ... };

class Customer {
public:
....
Customer(const Customer& rhs);
Customer& operator=(const Customer& rhs);
private:
std::string name;
Date lastTransaction;
}; Customer::Customer(const Customer& rhs)
: name(rhs.name) // 忽略了lastTransaction成员变量
{
logCall("Customer copy constructor");
} Customer& Customer::operato=(const Customer& rhs)
{
logCall("Customer copy assignment operator");
name = rhs.name; // 忽略了lastTransaction成员变量
return *this;
}
上述代码的问题很明显,就是没有拷贝lastTransaction成员变量,更为严重的是,编译器并不会提醒你这个错误。
结论就是,如果为class添加一个成员变量,必须同时修改copying函数(包括拷贝构造函数和重载赋值操作符)

如果发生了继承,会带来什么样的危险呢?
class PriorityCustomer: public Custoer {
public:
...
PriorityCustomer(const PriorityCustoer& rhs);
PriorityCustomer& operato=(const PriorityCustomer& rhs);
...
private:
int priority;
}; PriorityCustomer&
PriorityCustomer::PriorityCustomer (const PriorityCustomer& rhs)
: priority(rhs.priority)
{
logCall("PriorityCustoer copy constructor");
} PriorityCustomer&
PriorityCustomer::operator=(const PriorityCustomer& rhs)
{
logCall("PriorityCustomer copy assignment operator");
priority = rhs.priority;
return *this;
}

问题:

PriorityCustomer的拷贝函数们拷贝了它声明的成员变量,却忽略了它所继承的Customer成员变量的拷贝,也没有指定实参传递给Customer的构造函数,因此,PriorityCustomer对象的Customer成分会被不带实参的Customer默认构造函数初始化。

改进:为子类编写拷贝函数时,必须要复制其基类部分。
PriorityCustomer&
PriorityCustomer::PriorityCustomer (const PriorityCustomer& rhs)
: Customer(rsh), priority(rhs.priority)
{
logCall("PriorityCustoer copy constructor");
} PriorityCustomer&
PriorityCustomer::operator=(const PriorityCustomer& rhs)
{
logCall("PriorityCustomer copy assignment operator");
Customer::operator=(rhs);
priority = rhs.priority;
return *this;
}

结论

确保赋值所有local成员变量

调用所有base classes内的适当的拷贝函数

需要注意的一点是:
虽然重载赋值操作符和拷贝函数的代码长得很像,但是很难复用它们,因为重载赋值操作符时调用拷贝构造函数就像在构造一个已经存在的对象;
同样地,令拷贝构造函数调用重载赋值操作符一样没有意义。

所以,复用这两个代码的方式就是写一个新的成员函数给两者调用。

小结:
拷贝构造函数和重载赋值操作符应该确保复制“对象内的所有成员变量”及“所有base class成分”;
不要尝试拷贝构造函数调用重载赋值操作符或是反过来。
参考资料:
《Effective C++ 3rd》

Effective C++(12) 复制对象时要复制每一个成员的更多相关文章

  1. Effective C++ 条款11,12 在operator= 中处理“自我赋值” || 复制对象时不要忘记每一个成分

    1.潜在的自我赋值     a[i] = a[j];     *px = *py; 当两个对象来自同一个继承体系时,他们甚至不需要声明为相同类型就可能造成别名. 现在担心的问题是:假如指向同一个对象, ...

  2. EC笔记:第二部分:12、复制对象时勿忘其每一个成分

    EC笔记:第二部分:12.复制对象时勿忘其每一个成分 1.场景 某些时候,我们不想使用编译器提供的默认拷贝函数(包括拷贝构造函数和赋值运算符),考虑以下类定义: 代码1: class Point{ p ...

  3. Effective C++ -----条款12: 复制对象时勿忘其每一个成分

    Copying函数应该确保复制“对象内的所有成员变量”及“所有base class成分”. 不要尝试以某个copying函数实现另一个copying函数.应该将共同机能放进第三个函数中,并由两个cop ...

  4. Effective C++ 条款12:复制对象时勿忘其每一个成分

    void logCall(const std::string& funcName); class Customer { public: ... Customer (const Customer ...

  5. Effective C++_笔记_条款12_复制对象时勿忘其每一个成分

    (整理自Effctive C++,转载请注明.整理者:华科小涛@http://www.cnblogs.com/hust-ghtao/) 编译器会在必要时候为我们的classes创建copying函数, ...

  6. [Effective C++ --012]复制对象时勿忘其每一个成分

    引言: 在深拷贝和浅拷贝的理解中,我们知道了“拷贝构造函数”一词,并且也了解了它的构成. A(const A& r); // 形式有多种,在这里只列出一个 因此,在值传递的应用场景里,我们可以 ...

  7. EC读书笔记系列之7:条款12 复制对象时勿忘其每一个成分

    记住: ★copying函数应确保复制“对象内的所有成员变量”及“所有base class成分” ★不要尝试以某个copying函数实现另一个copying函数.应该将共同机能放进第三个函数中,并由两 ...

  8. 条款12:复制对象时勿忘其每一个成分(Copy all parts of an object)

    NOTE: 1.Copying 函数应该确保复制“对象内的所有成员变量”及“所有base class成分”. 2.不要尝试以某个copying函数实现另一个copying函数.应该将共同机能放进第三个 ...

  9. C++复制对象时勿忘每一部分

    现看这样一个程序: void logCall(const string& funcname) //标记记录 { cout <<funcname <<endl; } cl ...

随机推荐

  1. Android项目外接高德地图代码混淆注意事项

    如今好多项目中都加入了第三方jar包,可是最大的问题就是打包的时候代码混淆报错,下面是高德地图混淆报错解决方式: 在proguard-project.txt中加入例如以下代码: -libraryjar ...

  2. 写你自己 android 多通道打包工具 可以包libs和.so文件

    android上传应用程序,需要区分各个信道. 通常更改配置文件中的一个通道id,假设有多个通道,手动更改并生成apk这将是非常麻烦的,及增加误差的概率. 在这个课堂上分享一个打包工具.也可在网上类似 ...

  3. JavaScript阻止事件冒泡(兼容IE、Chrome、FF)

    这里仅仅是一个简单代码demo,因为时间问题并未做深入研究,因为今天做项目时要用到阻止事件冒泡的内容,找了好多才找到一个可以使用的,特记录之. <!DOCTYPE HTML> <ht ...

  4. JS基础——数组总结

    JS中数组被觉得是一种对象,慢慢的,怎么忽然感觉,JS中仅仅要能够独立出来的概念怎么都能够当成对象来解释呢?有点儿怀疑.继续学吧.先来总结一下JS中数组是怎样详细使用的. 一.创建 数组的创建在JS中 ...

  5. L轻松学习inux教程5 知识与学习bash

    本系列文章由@超人爱因斯坦出品,转载请注明出处.          文章链接:          http://hpw123.net/a/Linux/Linuxjichu/2014/1031/101. ...

  6. FFmpeg 协议初步学习

    typedef struct URLContext { const AVClass *av_class; /**< information for av_log(). Set by url_op ...

  7. cer证书签名验证

    一个cer还需要一个签名的证书本身,这是为了防止cer证书被篡改. 有两种类型的证书: 1. 根证书 2. 由根证书颁发子证书. 特根证书.它是自签名. 而其它子证书的签名公钥都保存在它的上级证书里面 ...

  8. CII-2.4指针常量和常量指针

    #define T Stack_T typedef struct T *T; struct T { int count; struct elem { void *x; struct elem *lin ...

  9. 前端学习笔记(zepto或jquery)——对li标签的相关操作(一)

    对li标签的相关操作——点击li标签进行样式切换的两种方式 Demo演示: 1 2 3 4 // 详解: 第一种方式(以ul为基础): $("ul").bind("cli ...

  10. 从头开始学JavaScript (四)——操作符

    原文:从头开始学JavaScript (四)--操作符 一.一元操作符 1.自增自减操作符:分为前置型和后置型: 前置型:++a;--a; 后置型:a++;a--; 例: <script typ ...