1. operator=赋值操作符
Effective C++条款16中描述到,在派生类中重写赋值操作符函数时,要注意不要忘记对基类对象部分也要赋值,而这一点恐怕是许多新手容易忘记的,他们很自然地不会想到这一点(例如我),比如下述代码:
class CBase
{
public:
CBase& operator=(const CBase& another)
{
if (this!=&another)
{
base=another.base;
}
return *this;
}
CBase(int a):base(a){}
int Value(){return base;}
protected:
int base;
};
class CDerived : public CBase
{
public:
CDerived& operator=(const CDerived& another)
{
if (this == &another)
{
return *this;
}
// call baseclass's operator=
//CBase::operator=(another);
derived=another.derived;
return *this;
}
CDerived(int b):derived(b),CBase(b){}
int Value(){return derived;}
int BaseValue(){return base;}
private:
int derived;
};
int main()
{
CDerived d1(3);
cout<<"Derived Object 1: Base:"<<d1.BaseValue()<<"\t"<<"Derived:"<<d1.Value()<<"\n";
CDerived d2(4);
cout<<"Derived Object 1: Base:"<<d2.BaseValue()<<"\t"<<"Derived:"<<d2.Value()<<"\n";
d1=d2;
cout<<"After Assignment: Base:"<<d1.BaseValue()<<"\t"<<"Derived:"<<d1.Value()<<"\n";
return 0;
}
上述例子中,派生类CDerived的赋值操作符函数没有将基类CBase对象部分的成员赋值,则基类对象的成员base仍然维持原值, 输出为:
Derived Object 1: Base:3 Derived:3
Derived Object 2: Base:4 Derived:4
After Assignment: Base:3 Derived:4
如果将CBase::operator=(another);这一行的注释去掉,则可以成功将基类对象部分的成员进行赋值:
Derived Object 1: Base:3 Derived:3
Derived Object 2: Base:4 Derived:4
After Assignment: Base:4 Derived:4
2. 拷贝构造函数
同样地,拷贝构造函数中也要注意调用基类版本的拷贝构造函数,这样才可以达到完全复制的目的:
class CBase
{
public:
CBase& operator=(const CBase& another)
{
if (this!=&another)
{
base=another.base;
}
return *this;
}
CBase(const CBase& another):base(another.base){}
CBase(int a=0):base(a){}
int Value(){return base;}
protected:
int base;
};
class CDerived : public CBase
{
public:
//Version1:CDerived(const CDerived& another):derived(another.derived){}
//Version2:CDerived(const CDerived& another):derived(another.derived),CBase(another){}
CDerived& operator=(const CDerived& another)
{
if (this == &another)
{
return *this;
}
// call baseclass's operator=
//CBase::operator=(another);
derived=another.derived;
return *this;
}
CDerived(int b=1):derived(b),CBase(b){}
int Value(){return derived;}
int BaseValue(){return base;}
private:
int derived;
};
int main()
{
CDerived d1(3);
cout<<"Derived Object 1: Base:"<<d1.BaseValue()<<"\t"<<"Derived:"<<d1.Value()<<"\n";
CDerived d2(d1);
cout<<"After Copy Construction: Base:"<<d2.BaseValue()<<"\t"<<"Derived:"<<d2.Value()<<"\n";
return 0;
}
如果派生类拷贝构造函数采用上例中Version1版本,则输出为:
Derived Object 1: Base:3 Derived:3
After Copy Construction: Base:0 Derived:3
基类部分的变量base没有被拷贝,实际上,基类部分的初始化是通过调用基类默认构造函数完成的,因此base的值为0
如果派生类拷贝构造函数采用上例中Version2版本,则输出为:
Derived Object 1: Base:3 Derived:3
After Copy Construction: Base:3 Derived:3
这样就达到了拷贝对象的目的。
总结
对于生成对象的三个函数:构造函数,拷贝构造函数和operator=函数,我们不仅仅要关注派生类成员变量的赋值或初始化,也要关注基类成员变量,不能遗漏。