分享
 
 
 

Effective C++ 2e Item16

王朝c/c++·作者佚名  2006-01-08
窄屏简体版  字體: |||超大  

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

条款45说明了如果没写赋值运算符的话,编译器就会为你生成一个,条款11则说明了为什么你会经常不喜欢编译器为你生成的这个赋值运算符,所以你会想能否有个两全其美的办法,让编译器生成一个缺省的赋值运算符,然后可以有选择地重写不喜欢的部分。这是不可能的!只要想对赋值过程的某一个部分进行控制,就必须负责做赋值过程中所有的事。

实际编程中,这意味着写赋值运算符时,必须对对象的每一个数据成员赋值:

template<class T> // 名字和指针相关联的类的模板

class NamedPtr { // (源自条款12)

public:

NamedPtr(const string& initName, T *initPtr);

NamedPtr& operator=(const NamedPtr& rhs);

private:

string name;

T *ptr;

};

template<class T>

NamedPtr<T>& NamedPtr<T>::operator=(const NamedPtr<T>& rhs)

{

if (this == &rhs)

return *this; // 见条款17

// assign to all data members

name = rhs.name; // 给name赋值

*ptr = *rhs.ptr; // 对于ptr,赋的值是指针所指的值,

// 不是指针本身

return *this; // 见条款15

}

初写这个类时当然很容易记住上面的原则,但同样重要的是,当类里增加新的数据成员时,也要记住更新赋值运算符函数。例如,打算升级NamedPtr模板使得名字改变时附带一个时间标记,那就要增加一个新的数据成员,同时需要更新构造函数和赋值运算符。但现实中,因为忙于升级类的具体功能和增加新的成员函数等,这一点往往很容易被忘记。

当涉及到继承时,情况就会更有趣,因为派生类的赋值运算符也必须处理它的基类成员的赋值!看看下面:

class Base {

public:

Base(int initialValue = 0): x(initialValue) {}

private:

int x;

};

class Derived: public Base {

public:

Derived(int initialValue)

: Base(initialValue), y(initialValue) {}

Derived& operator=(const Derived& rhs);

private:

int y;

};

逻辑上说,Derived的赋值运算符应该象这样:

// erroneous assignment operator

Derived& Derived::operator=(const Derived& rhs)

{

if (this == &rhs) return *this; // 见条款17

y = rhs.y; // 给Derived仅有的

// 数据成员赋值

return *this; // 见条款15

}

不幸的是,它是错误的,因为Derived对象的Base部分的数据成员x在赋值运算符中未受影响。例如,考虑下面的代码段:

void assignmentTester()

{

Derived d1(0); // d1.x = 0, d1.y = 0

Derived d2(1); // d2.x = 1, d2.y = 1

d1 = d2; // d1.x = 0, d1.y = 1!

}

请注意d1的Base部分没有被赋值操作改变。

解决这个问题最显然的办法是在Derived::operator=中对x赋值。但这不合法,因为x是Base的私有成员。所以必须在Derived的赋值运算符里显式地对Derived的Base部分赋值。

也就是这么做:

// 正确的赋值运算符

Derived& Derived::operator=(const Derived& rhs)

{

if (this == &rhs) return *this;

Base::operator=(rhs); // 调用this->Base::operator=

y = rhs.y;

return *this;

}

这里只是显式地调用了Base::operator=,这个调用和一般情况下的在成员函数中调用另外的成员函数一样,以*this作为它的隐式左值。Base::operator=将针对*this的Base部分执行它所有该做的工作——正如你所想得到的那种效果。

但如果基类赋值运算符是编译器生成的,有些编译器会拒绝这种对于基类赋值运算符的调用(见条款45)。为了适应这种编译器,必须这样实现Derived::operator=:

Derived& Derived::operator=(const Derived& rhs)

{

if (this == &rhs) return *this;

static_cast<Base&>(*this) = rhs; // 对*this的Base部分

// 调用operator=

y = rhs.y;

return *this;

}

这段怪异的代码将*this强制转换为Base的引用,然后对其转换结果赋值。这里只是对Derived对象的Base部分赋值。还要注意的重要一点是,转换的是Base对象的引用,而不是Base对象本身。如果将*this强制转换为Base对象,就要导致调用Base的拷贝构造函数,创建出来的新对象(见条款M19)就成为了赋值的目标,而*this保持不变。这不是所想要的结果。

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

另一个经常发生的和继承有关的类似问题是在实现派生类的拷贝构造函数时。看看下面这个构造函数,其代码和上面刚讨论的类似:

class Base {

public:

Base(int initialValue = 0): x(initialValue) {}

Base(const Base& rhs): x(rhs.x) {}

private:

int x;

};

class Derived: public Base {

public:

Derived(int initialValue)

: Base(initialValue), y(initialValue) {}

Derived(const Derived& rhs) // 错误的拷贝

: y(rhs.y) {} // 构造函数

private:

int y;

};

类Derived展现了一个在所有C++环境下都会产生的bug:当Derived的拷贝创建时,没有拷贝其基类部分。当然,这个Derived对象的Base部分还是创建了,但它是用Base的缺省构造函数创建的,成员x被初始化为0(缺省构造函数的缺省参数值),而没有顾及被拷贝的对象的x值是多少!

为避免这个问题,Derived的拷贝构造函数必须保证调用的是Base的拷贝构造函数而不是Base的缺省构造函数。这很容易做,只要在Derived的拷贝构造函数的成员初始化列表里对Base指定一个初始化值:

class Derived: public Base {

public:

Derived(const Derived& rhs): Base(rhs), y(rhs.y) {}

...

};

现在,当用一个已有的同类型的对象来拷贝创建一个Derived对象时,它的Base部分也将被拷贝了。

 
 
 
免责声明:本文为网络用户发布,其观点仅代表作者个人观点,与本站无关,本站仅提供信息存储服务。文中陈述内容未经本站证实,其真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
2023年上半年GDP全球前十五强
 百态   2023-10-24
美众议院议长启动对拜登的弹劾调查
 百态   2023-09-13
上海、济南、武汉等多地出现不明坠落物
 探索   2023-09-06
印度或要将国名改为“巴拉特”
 百态   2023-09-06
男子为女友送行,买票不登机被捕
 百态   2023-08-20
手机地震预警功能怎么开?
 干货   2023-08-06
女子4年卖2套房花700多万做美容:不但没变美脸,面部还出现变形
 百态   2023-08-04
住户一楼被水淹 还冲来8头猪
 百态   2023-07-31
女子体内爬出大量瓜子状活虫
 百态   2023-07-25
地球连续35年收到神秘规律性信号,网友:不要回答!
 探索   2023-07-21
全球镓价格本周大涨27%
 探索   2023-07-09
钱都流向了那些不缺钱的人,苦都留给了能吃苦的人
 探索   2023-07-02
倩女手游刀客魅者强控制(强混乱强眩晕强睡眠)和对应控制抗性的关系
 百态   2020-08-20
美国5月9日最新疫情:美国确诊人数突破131万
 百态   2020-05-09
荷兰政府宣布将集体辞职
 干货   2020-04-30
倩女幽魂手游师徒任务情义春秋猜成语答案逍遥观:鹏程万里
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案神机营:射石饮羽
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案昆仑山:拔刀相助
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案天工阁:鬼斧神工
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案丝路古道:单枪匹马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:与虎谋皮
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:李代桃僵
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:指鹿为马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:小鸟依人
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:千金买邻
 干货   2019-11-12
 
推荐阅读
 
 
 
>>返回首頁<<
 
靜靜地坐在廢墟上,四周的荒凉一望無際,忽然覺得,淒涼也很美
© 2005- 王朝網路 版權所有