您的位置:首页 > 编程语言 > C语言/C++

Effective C++ 第二版 15) operator=返回值 16) operator=赋值

2013-10-04 19:33 393 查看
条款15: 让operator=返回*this的引用

试图让用户自定义类型尽可能和固定类型的工作方式相似;

固定类型赋值可以:

用户自定义类型也可以:

赋值运算符结合性默认是由右向左: 上面的赋值可以认为是: w = (x = (y = (z = "Hello")));

等价的函数形式: w.operator=(x.operator=(y.operator=(z.operator=("Hello"))));

w.operator=, x.operator=和y.operator=的参数是前一个operator=调用的返回值; 所以operator=的返回值必须作为一个输入参数被函数本身接受;

缺省版本的operator=的形式: C& C::operator=(const C&);

一般情况下, operator=输入和返回的都是类对象的引用, 有时候需要重载operator=使它接受不同类型的参数;

e.g. string
>即使是重载, 返回类型也要是类的对象的引用;

Note C++dev 经常犯的错误是将operator=返回void, 这样妨碍了连续(链式)赋值操作;

另一个常犯的错误是让operator=返回const对象的引用:
>这样做通常是为了防止程序以下的无意义操作:

但是对于固定类型, 这样操作也是可以的; 所以没必要改变设定, 和固定类型的常规做法不兼容;

缺省形式定义的赋值运算符里, 对象返回值有两个候选, 1)赋值语句左边的对象(this指针指向的对象) 2) 赋值语句右边的对象(参数表中被命名的对象);

返回rhs不会通过编译, rhs是const String&, 要返回的是String&;

如果你想通过重新声明operator=来解决这个问题: String& String::operator=(String& rhs)
{ ... }, 调用时又会出现问题:
>赋值语句的右边参数是一个字符数组, 不是String, 编译器会产生一个临时String对象(通过String构造, 除非显式地定义了需要的构造函数):

>临时值是一个const [由常量构造], 这样的情况, 如果String的operator=声明的参数是非const的String参数, 编译无法通过: 将const对象传递给非const参数是非法的;

结论: 当定义赋值运算符时, 必须返回赋值运算符左边参数的引用, *this; 否则将不能连续赋值, 或导致调用时的隐式类型转换不能进行;

条款16: 在operator=中对所有数据成员赋值

编译器会自动生成缺省的赋值运算符; 当重写赋值运算符时, 必须对对象的每一个数据成员赋值;

>当类里增加新的数据成员时, 需要更新赋值运算符函数和构造函数;

当涉及继承时, 派生类的赋值运算符也必须处理基类成员的赋值:

逻辑上说, Derived的赋值运算符是这样:
>因为Derived对象的Base部分的数据成员x在赋值运算符中未更新, 所以这是错误的;

e.g.

>d1的Base部分没有被赋值操作所更新;

>解决问题最显然的方法是在Derived::operator=中对x赋值, 但是这样不合法, x是Base的私有成员, 必须在Derived的赋值运算符里显式地对Derived的Base部分赋值;

>显式调用Base::operator=, 和一般情况下在成员函数中调用其他成员函数一样, *this作为它的隐式左值; Base::operator=将针对*this的Base部分执行该做的工作;

如果基类赋值运算符是编译器自动生成的, 有些编译器会拒绝对于基类赋值运算的调用; 为了适应这种情况, 必须实现Derived::operator=

>这段代码将*this强转为Base的引用[No Slice, it is reference], 然后对转换结果赋值; 这里只是对Derived对象的Base部分赋值; 转换的是Base对象的引用, 不是Base对象本身; 如果将*this强制转换为Base对象, 就要调用Base的拷贝构造函数, 创建出新的对象成为赋值的目标, 而*this保持不变, 这不是预期的结果;

不管是哪种方法, 给Derived对象的Base部分赋值后, 接着的是Derived本身的赋值, 即对Derived的所有数据成员赋值;

另一个经常发生的和继承有关的问题是在实现派生类的拷贝构造函数时;

>Derived类展现了一个在所有C++环境下都会产生的bug: 当Derived的拷贝创建时, 没有拷贝基类部分; 这个Derived对象的Base部分是用缺省构造函数创建的, 成员x被初始化为0(缺省构造函数的缺省参数值), 没有把实际拷贝对象的x值拷贝过去;

Note 为避免这个问题, Derived的拷贝构造函数必须保证调用Base的拷贝构造函数而不是Base的缺省构造函数;

要在Derived的拷贝构造函数的成员初始化列表里对Base指定一个初始化值:
>这样, 当用已有的同类型对象来拷贝创建一个Derived对象时, Base部分也会被拷贝
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐