effective C++ 第三章:C++构造、析构和赋值

来源:互联网 发布:恒大社会招聘网络面试 编辑:程序博客网 时间:2024/06/10 19:13

一、为动态内存分配的类提供拷贝构造与赋值操作符,以避免使用缺省的拷贝构造与缺省赋值操作时由于类的成员变量间的内存拷贝造成程序crash。

拷贝构造函数和赋值运算符,官方的规则是:缺省拷贝构造函数(赋值运算符)对类的非静态数据成员进行 “以成员为单位的” 逐一拷贝构造(赋值)。即,如果m是类C中类型为T的非静态数据成员,并且C没有声明拷贝构造函数(赋值运算符),m将会通过类型T的拷贝构造函数(赋值运算符)被拷贝构造(赋值)—-如果T有拷贝构造函数(赋值运算符)的话。如果没有,规则递归应用到m的数据成员,直至找到一个拷贝构造函数(赋值运算符)或固定类型(例如,int,double,指针,等)为止。默认情况下,固定类型的对象拷贝构造(赋值)时是从源对象到目标对象的”逐位”拷贝。对于从别的类继承而来的类来说,这条规则适用于继承层次结构中的每一层,所以,用户自定义的构造函数和赋值运算符无论在哪一层被声明,都会被调用。具体例子,见 effective C++ 第七章:杂项

二、尽量使用初始化而不要在构造函数里赋值
1.因为在构造函数中赋值之前,已经调用了类的成员变量的缺省构造函数将成员变量构造了一遍。这造成了效率的地下。
2.从纯实际应用的角度来看,有些情况下必须用初始化。特别是const和引用数据成员只能用初始化,不能被赋值。

三、必须保证初始化列表中成员列出的顺序和它们在类中声明的顺序相同(类成员是按照它们在类里被声明的顺序进行初始化的,和它们在成员初始化列表中列出的顺序没一点关系)
如果成员变量存在依赖关系,但是初始化列表顺序写反了,那么会造成不可预期的结果。

class  Salary{    Salary(int a):age(a),salary(age*1500){}private:    int salary;    int age;};Salary s(55);//本意是salary = 55*1500,但实际不确定。因为会先运算salary(age*1500),然后age才会被赋值为55。在此之前,age的值是不确定的。

(条款13:)实际上,如果你深究一下的话,会发现只是非静态数据成员的初始化遵守以上规则。静态数据成员的行为有点象全局和名字空间对象,所以只会被初始化一次(详见条款47)。另外,基类数据成员总是在派生类数据成员之前被初始化,所以使用继承时,要把基类的初始化列在成员初始化列表的最前面。(如果使用多继承,基类被初始化的顺序和它们被派生类继承的顺序一致,它们在成员初始化列表中的顺序会被忽略。使用多继承有很多地方要考虑。

四、确定基类有虚析构函数(为了在使用多态的时候,所有资源能够被正常的释放(子类和基类的析构函数都会被调用,主要是为了保证子类的析构被调用))
c++语言标准关于这个问题的阐述非常清楚:当通过基类的指针去删除派生类的对象,而基类又没有虚析构函数时,结果将是不可确定的。

五、让operator=返回*this的引用(为了让operator=的返回值必须可以作为另一个operator=的输入参数,达到连续操作的目的)
c++程序员经常犯的一个错误是让operator=返回void,这好象没什么不合理的,但它妨碍了连续(链式)赋值操作,所以不要这样做。另一个常犯的错误是让operator=返回一个const对象的引用

六、在operator=中对所有数据成员赋值(条款16)

七、在operator=中检查自赋值的情况
1.效率
2.保证正确性。一个赋值运算符必须首先释放掉一个对象的资源(去掉旧值),然后根据新值分配新的资源。在自己给自己赋值的情况下,释放旧的资源将是灾难性的,因为在分配新的资源时会需要旧的资源。

0 0
原创粉丝点击