在operator=或拷贝构造中对所有数据成员赋值

来源:互联网 发布:linux c 过去系统时间 编辑:程序博客网 时间:2024/05/23 13:15


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:3Derived Object 2: Base:4  Derived:4After Assignment: Base:3  Derived:4

如果将CBase::operator=(another);这一行的注释去掉,则可以成功将基类对象部分的成员进行赋值:

Derived Object 1: Base:3  Derived:3Derived Object 2: Base:4  Derived:4After 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:3After Copy Construction: Base:0 Derived:3

基类部分的变量base没有被拷贝,实际上,基类部分的初始化是通过调用基类默认构造函数完成的,因此base的值为0

如果派生类拷贝构造函数采用上例中Version2版本,则输出为:

Derived Object 1: Base:3        Derived:3After Copy Construction: Base:3 Derived:3

这样就达到了拷贝对象的目的。


总结


对于生成对象的三个函数:构造函数,拷贝构造函数和operator=函数,我们不仅仅要关注派生类成员变量的赋值或初始化,也要关注基类成员变量,不能遗漏。