派生类拷贝构造函数、赋值运算符

学习资料

派生类的赋值运算符/赋值构造函数也必须处理它的基类成员的赋值

• C++ 基类构造函数带参数的继承方式及派生类的初始化

 

定义拷贝构造函数

【注意】对派生类进行拷贝构造时,如果想让基类的成员也同时拷贝,就一定要在派生类拷贝构造函数初始化列表中显示调用基类拷贝构造函数(当然在函数体内将基类部分的值拷贝也是可以的,只不过它是先用默认构造函数初始化后再修改的基类成员变量的值,效率比较低),否则它会调用基类的默认构造函数,而不会对基类的成员变量拷贝值,这样生成的对象,它的派生类部分和被拷贝的对象派生类部分一样,而基类部分则是默认构造函数的初始化结果。

代码例子1:

 1 #include <iostream>
 2 using namespace std;
 3 
 4 class A
 5 {
 6 public:
 7     A() { cout << "A default constructor" << endl; }
 8     A(A&) { cout << "A copy constructor" << endl; }
 9 };
10 class B : public A
11 {
12 public:
13     B() { cout << "A default constructor" << endl; }
14     B(B &b) { cout << "B copy constructor" << endl; }
15 };
16 
17 int main()
18 {
19     B b;
20     B c = b;
21     return 0;
22 }

输出结果:

 

 

代码例子2:

 1 #include <iostream>
 2 using namespace std;
 3 
 4 class A
 5 {
 6 public:
 7     A() { cout << "A default constructor" << endl; }
 8     A(A&) { cout << "A copy constructor" << endl; }
 9 };
10 class B : public A
11 {
12 public:
13     B() { cout << "A default constructor" << endl; }
14     B(B &b) : A(b) { cout << "B copy constructor" << endl; }
15 };
16 
17 int main()
18 {
19     B b;
20     B c = b;
21     return 0;
22 }

输出结果:

 

 

C++ 基类构造函数带参数的继承方式及派生类的初始化

在定义类的时候,会遇到基类的构造函数带参数,而子类子类构造函数不带参数,这时候如果以代码 a 的方式建立派生类则会出错。

 1 class A
 2 {
 3     public:
 4         A(int x, int y): i(x), j(y) {}
 5     private:
 6         int i, j;
 7 };
 8 
 9 class B : public A
10 {
11     public:
12         B() { cout << "init B" << endl; }
13 };

在建立B类对象时,编译出错: 
 C:\Documents and Settings\admin\桌面\Text1.cpp(104) : error C2512: ‘A’ : no appropriate default constructor available 

 

解决这个问题应该在A的构造函数中显式调用基类的带参构造函数。因为在基类中定义了带参构造函数,编译器不会提供默认构造函数。(或者可以在基类中增加一个不带参数的构造函数)这个问题将解决。 
代码 b 采用的是调用基类带参构造函数的方式:

代码 b:

 1 class A
 2 {
 3     public:
 4         A(int x, int y): i(x), j(y) {}
 5     private:
 6         int i, j;
 7 };
 8 
 9 class B : public A
10 {
11     public:
12         B() A(10,20) { cout << "init B" << endl; }
13 };

 

通过在基类中增加一个不带参数的构造函数: 
代码 c:

 1 class A
 2 {
 3     public:
 4         A(int x, int y): i(x), j(y) {}
 5         A();   //不带参数的构造函数
 6     private:
 7         int i, j;
 8 };
 9 
10 class B:public A
11 {
12     public:
13         B(): A(10,20) { cout << "init B" << endl; }
14 };

 

 

 1 #include <iostream>
 2 #include <cmath>
 3 using namespace std;
 4 
 5 class A
 6 {
 7 public:
 8   A() { cout << "Construct A" << endl; }
 9   ~A() { cout << "Destruct A" << endl; }
10 };
11 
12 class C
13 {
14 public:
15   C() { cout << "Construct C" << endl; }
16   ~C() { cout << "Destruct C" << endl; }
17 };
18 
19 class B : public A, public C
20 {
21 public:
22   B() { cout << "Construct B" << endl; }
23   ~B() { cout << "Destruct B" << endl; }
24 };
25 
26 int main(int argc, char const *argv[])
27 {
28   B b;
29   return 0;
30 }

输出:

Construct A
Construct C
Construct B
Destruct B
Destruct C
Destruct A

【注意】

这里涉及到构造以及析构的顺序,首先看看构造。构造函数执行的顺序是,先构造父类,再构造子类,其中父类的构造顺序是从左到右。然后析构函数执行的顺序是跟构造函数正好相反的,先执行自身的析构函数,然后再依次从右到左执行父类的析构函数。

上面说的是不涉及列表初始化,以及虚拟继承这些的。那接下来看看加上列表初始化会怎么样。

定义派生类赋值运算符

与拷贝和移动构造函数一样,派生类的赋值运算符也必须为其基类部分赋值。

1 // Base::operator=(const Base&) 不会被自动调用
2 D& D::operator=(const D &rhs)
3 {
4     Base::operator=(rhs);  //为基类部分赋值
5     //按照过去的方式为派生类的成员赋值
6     return *this;
7 }

 举例说明:

 1 class base 
 2 {
 3 public:
 4   base(int initialvalue = 0): x(initialvalue) {}
 5 
 6 private:
 7   int x;
 8 };
 9 
10 class derived : public base 
11 {
12 public:
13   derived(int initialvalue): base(initialvalue), y(initialvalue) {}
14   derived& operator=(const derived& rhs);
15 
16 private:
17   int y;
18 };
19 
20 逻辑上说,derived的赋值运算符应该象这样:
21 derived& derived::operator = (const derived& rhs) // 错误的赋值运算符
22 {                                                 // 请注意d1的base部分没有被赋值操作改变。
23   if (this == &rhs) 
24     return *this;  
25   y = rhs.y;                                                          
26   return *this;                      
27 }
28 
29 不幸的是,它是错误的,因为derived对象的base部分的数据成员x在赋值运算符中未受影响。例如,考虑下面的代码段:
30 
31 void assignmenttester()
32 {
33   derived d1(0);      // d1.x = 0, d1.y = 0
34   derived d2(1);      // d2.x = 1, d2.y = 1
35 
36   d1 = d2;            // d1.x = 0, d1.y = 1
37 }
38 
39 
40 derived& derived::operator = (const derived& rhs) // 正确的赋值运算符
41 {
42   if (this == &rhs) 
43     return *this;
44 
45   base::operator = (rhs);    // 调用this->base::operator=
46   y = rhs.y;
47 
48   return *this;
49 }

 

#include <iostream>
using namespace std;

class A
{
public:
    A() { cout << "A default constructor" << endl; }
    A(A&) { cout << "A copy constructor" << endl; }
};
class B : public A
{
public:
    B() { cout << "A default constructor" << endl; }
    B(B &b) : A(b) { cout << "B copy constructor" << endl; }
};

int main()
{
    B b;
    B c = b;
    return 0;
}

 

参考资料

1. C++奇奇怪怪的题目之构造析构顺序

posted @ 2018-06-22 20:05  苏格拉底的落泪  阅读(2144)  评论(1编辑  收藏  举报