实现拷贝构造函数和赋值运算符时,我们不能遗忘任何一个成员,当我们自定义以上成员时,如果我们没有将其中的某个成员进行复制,编译器是没法检测出来的。这种情况下我们就会得到不正确的对象。所以当我们为某个类添加任何成员时,我们必须记得修改我们的拷贝构造函和复制运算符重载。
派生类的拷贝和赋值
在派生类中实现拷贝构造函数和复制运算符时我们不能遗忘基类成分,我们必须考虑我们派生类中的隐藏的基类成员的赋值。
考虑以下的案例:
class A {
public:
A() : Num(0), Name("") {}
A(int num, string name) :Num(num), Name(name) {}
A(const A& a) :Num(a.Num), Name(a.Name) {};
A& operator=(const A& a) {
Num = a.Num;
Name = a.Name;
return *this;
}
void print()
{
cout << Num << " " << Name << " ";
}
private:
int Num;
string Name;
};
class B:public A
{
public:
B() :Dbl(0.0) {}
B(int num,string name,double d) :A(num,name),Dbl(d) {}
//拷贝构造
B(const B& b):Dbl(b.Dbl) {}
B& operator=(const B &b)
{
Dbl = b.Dbl;
return *this;
}
void print()
{
A::print();
cout << Dbl << endl;
}
private:
double Dbl;
};
案例中B继承A,但是在B的拷贝成员中,没有考虑B中的A成员,于是当我们有如下的调用时,
B b(1, "Bob", 1.0);
B b1(b);
b1.print();
我们将会在打印这样结果:
我们值是将b的Dbl成员拷贝给了b1,因此,对于b1的基类部分将会调用默认构造函数初始化。
所以我们的解决方案应该这样的,在派生类的拷贝构造函数初始化列表中调用基类的拷贝构造函数,在派生类的赋值运算符重载函数中调用基类的赋值运算符。
如下:
//拷贝构造,调用基类的拷贝构造函数
B(const B& b):A(b),Dbl(b.Dbl) {}
B& operator=(const B &b)
{
A::operator=(b);//显示的调用基类的赋值运算符
Dbl = b.Dbl;
return *this;
}
运行以下代码将会得到这样的结果:
B b(1, "Bob", 1.0);
B b1(b);
B b2;
b2 = b;
b1.print();
b2.print();
我们不能在赋值运算符中调用基类的拷贝构造函数,因为我们试图构造一个已经存在的对象,同样在拷贝构造函数中调用赋值运算符也没多大意义。