一. 概述

通过几个简单的实验,回顾下派生类中拷贝构造的相关知识。

环境:Centos7 64位, g++ 4.8.5

在继承中,构造器与析构器均没有被继承下来。拷贝构造,也是一种构造,也没有被继承下来。

父类中,一部分成员需要拷贝构造来完成,子类,也有一部分成员需要拷贝构造来完成。子类中的内嵌子对象中的成员也需要拷贝构造来完成。

二. 实验过程

1.无自实现(系统默认)

派生类中,不自实现拷贝构造函数,看下系统默认的拷贝构造情况。

基类A,派生类C继承了基类A,派生类C中有一个内嵌子对象B bb。

通过下面的运行情况,对象c2拷贝了c1,派生类中调用了父类中默认的拷贝构造函数,内嵌子对象也是一样。

 1 #include <iostream>
2
3 using namespace std;
4
5 class A
6 {
7 public:
8 A(int x = 10)
9 :a(x)
10 {
11 cout<<"constructor A()"<<endl;
12 }
13
14 int a;
15 };
16
17 class B
18 {
19 public:
20 B(int y = 20)
21 :b(y)
22 {
23 cout<<"constructor B()"<<endl;
24 }
25
26 int b;
27 };
28
29 class C: public A
30 {
31 public:
32 C(int x, int y, int z = 30)
33 :A(x), bb(y)
34 {
35 c = z;
36 cout<<"cosntructor C()"<<endl;
37 }
38
39 int c;
40 B bb;
41 };
42
43 int main()
44 {
45 C c1(42, 21, 14);
46 cout<<"c1: "<<c1.a<<" "<<c1.bb.b<<" "<<c1.c<<endl;
47
48 cout<<"----------"<<endl;
49 C c2(c1);
50 cout<<"c2: "<<c2.a<<" "<<c2.bb.b<<" "<<c2.c<<endl;
51
52 return 0;
53 }

运行结果如下:

达到了预期结果。

c2成功地拷贝了c1。根据打印结果,可知,c1对象在生成时,先调用了基类A的构造函数,然后是内嵌子对象bb的构造函数,最后是C自己的构造函数。

2.派生类中自实现拷贝构造函数,不显示调用父类、内嵌子对象中的拷贝构造函数

派生类C中,自实现拷贝构造函数,第11行-第14行,如下。

通过打印结果发现,派生类调用了基类的构造函数,而不是默认的拷贝构造函数。内嵌子对象也是一样。

对象c1和c2中的两个成员a, b结果均不一致,也就是说父类和内嵌子对象中的成员都没有被拷贝过来,c2中的a、b的值是父类、内嵌子对象中调用构造函数进行初始化而来的。此时,拷贝构造也没有什么意义了。无法达到拷贝的效果。

 1 class C: public A
2 {
3 public:
4 C(int x, int y, int z = 30)
5 :A(x), bb(y)
6 {
7 c = z;
8 cout<<"cosntructor C()"<<endl;
9 }
10
11 C(const C &another)
12 {
13 c = another.c;
14 }
15
16 int c;
17 B bb;
18 };

运行结果如下:

3.派生类中自实现拷贝构造,显示调用父类、内嵌子对象中的拷贝构造函数

派生类C中添加显示调用,第12行代码。

注:A(another),将派生类对象赋值给父类的引用,用到了赋值兼容。

此时,派生类中的拷贝构造函数调用了基类中默认的拷贝构造函数。此时,浅拷贝也可以满足需求(关于浅拷贝与深拷贝)。

 1 class C: public A
2 {
3 public:
4 C(int x, int y, int z = 30)
5 :A(x), bb(y)
6 {
7 c = z;
8 cout<<"cosntructor C()"<<endl;
9 }
10
11 C(const C &another)
12 :A(another), bb(another.bb)
13 {
14 c = another.c;
15 }
16
17 int c;
18 B bb;
19 };

运行结果如下:

运行结果符合预期,实现了拷贝的目的。

4.在3的基础上,如果需要实现深拷贝的目的,则父类中也需要自实现拷贝构造

类A,增加第10行-第14行代码

 1 class A
2 {
3 public:
4 A(int x = 10)
5 :a(x)
6 {
7 cout<<"constructor A()"<<endl;
8 }
9
10 A(const A &another)
11 {
12 a = another.a;
13 cout<<"A(const A &another)"<<endl;
14 }
15
16 int a;
17 };

类B,增加第10行-第14行代码

 1 class B
2 {
3 public:
4 B(int y = 20)
5 :b(y)
6 {
7 cout<<"constructor B()"<<endl;
8 }
9
10 B(const B &another)
11 {
12 b = another.b;
13 cout<<"B(const B &another)"<<endl;
14 }
15
16 int b;
17 };

运行结果如下:

根据打印结果可知, 对象c2在拷贝c1时,调用了基类和内嵌子对象的拷贝构造函数。

四. 总结

当派生类中不自实现拷贝构造时,默认调用父类的拷贝构造函数;

当派生类中自实现拷贝构造时,不做特殊处理(显示地调用父类的拷贝构造函数,包括系统默认和自实现拷贝构造函数),此时,只会调用父类的构造函数。此时,也失去了拷贝的意义,无法实现拷贝;

当派生类自实现拷贝构造,进行特殊处理(显示地调用父类的拷贝构造函数,包括系统默认和自实现的拷贝构造函数),此时,会调用父类的拷贝构造函数。

内嵌子对象与上面类似。

参考材料:

《C++基础与提高》  王桂林

C++派生类的拷贝构造的更多相关文章

  1. C++程序设计方法3:派生类对象的构造和析构过程

    基类中的数据成员,通过继承成为派生类对象的一部分,需要在构造派生类对象的过程中调用基类构造函数来正确初始化: 若没有显示调用,则编译器会自动生成一个对基类的默认构造函数的调用. 若想要显示调用,则只能 ...

  2. c++学习笔记4,调用派生类的顺序构造和析构函数(一个)

    测试源代码: //測试派生类的构造函数的调用顺序何时调用 //Fedora20 gcc version=4.8.2 #include <iostream> using namespace ...

  3. 【C++ Primer 第15章】定义派生类拷贝构造函数、赋值运算符

    学习资料 • 派生类的赋值运算符/赋值构造函数也必须处理它的基类成员的赋值 • C++ 基类构造函数带参数的继承方式及派生类的初始化 定义拷贝构造函数 [注意]对派生类进行拷贝构造时,如果想让基类的成 ...

  4. Chapter15:派生类

    在C++语言中,基类将类型相关的函数与派生类不做改变直接继承的函数区别对待,对于某些函数,基类希望它的派生类各自定义适合自身的版本,此时基类就将这些函数声明为虚函数. 派生类必须将其继承而来的成员函数 ...

  5. 从零开始学C++之继承(二):继承与构造函数、派生类到基类的转换

    一.不能自动继承的成员函数 构造函数 析构函数 =运算符 二.继承与构造函数 基类的构造函数不被继承,派生类中需要声明自己的构造函数. 声明构造函数时,只需要对本类中新增成员进行初始化,对继承来的基类 ...

  6. C++ 派生类构造函数和析构函数

    几个问题 一个类的各数据成员的构造顺序? 按他们在类定义中出现的先后顺序:先定义者先构造. 类的对象成员的构造函数与类自身的构造函数的执行顺序? 先执行对象成员的构造函数,再执行类自身的构造函数. 构 ...

  7. C++之旅:拷贝构造与友元

    拷贝构造与友元 拷贝构造是在构造一个对象的时候将已有对象的属性拷贝给新的对象:友元可以让一个类的所有属性(主要是private)对特定的类开放 拷贝构造 如果没有复写拷贝构造函数,系统会帮我们默认生成 ...

  8. C++ | 继承(基类,父类,超类),(派生类,子类)

    转载:https://blog.csdn.net/Sherlock_Homles/article/details/82927515 文章参考:https://blog.csdn.net/war1111 ...

  9. C++构造函数 & 拷贝构造函数 & 派生类的构造函数 & 虚继承的构造函数

    构造函数 ,是一种特殊的方法 .主要用来在创建对象时初始化对象, 即为对象成员变量赋初始值,总与new运算符一起使用在创建对象的语句中 .特别的一个类可以有多个构造函数 ,可根据其参数个数的不同或参数 ...

随机推荐

  1. 【VBA】判断文件是否存在

    效果: 源码: Sub 判断文件是否存在() Dim strcfg As String strcfg = "D:\a.cfg" If Dir(strcfg, vbDirectory ...

  2. 【模拟7.14】建造游乐园(play)

    这题是玄学的数论 首先考虑如何枚举偶数点度的图 可以考虑取出i-1个点 那么成图的数量为2^C(i-1,2) (原因单独取出的i点能平衡已建图中的奇数点,原因是某种性质....) 然后求带联通标号的欧 ...

  3. FreeRTOS移植EasyFlash

    1. EasyFlash Easyflash可以让 Flash 成为小型 KV 数据库(Key-Value) GitHub: https://github.com/armink/SFUD Gitee: ...

  4. SQL 利用存储过程实现对表数据有则更新无则添加(转)

    初学存储过程,发现这篇文章简单易懂,特意转载,地址 http://blog.csdn.net/luotuomianyang/article/details/52013144 如果某一操作包含大量的T- ...

  5. 简易版JDBC连接池

    JDBC连接池mini版的实现 首先是工具类 DbUtil 主要参数就是Driver.User.PWD等啦,主要用于建立连接 URL需要注意的是SSL和serverTimezone参数,和mysql驱 ...

  6. Python中Random随机数返回值方式

    1.a=["1","2","3"] print(random.choice(a)),  随机返回列表a中的一个元素 print(random ...

  7. 温故知新,CSharp遇见字符串比较(String Comparison),更佳科学的比较字符串

    背景 在C#中,我们经常会遇到需要比较字符串的场景,有时候甚至因为外部输入的不确定性,我们需要忽略大小写来进行比较,以达到判断业务的述求. 对字符串用法的建议 使用.NET进行开发时,请遵循以下简要建 ...

  8. .net core 常用rsa 加签类

    using Org.BouncyCastle.Crypto; using Org.BouncyCastle.Crypto.Parameters; using Org.BouncyCastle.Math ...

  9. SQL 小知识笔记

    1.自动生成序列号 select row_number() over(order by field1) as row_number,* from t_table

  10. Raspberry Pi:树莓派开发板配置USB启动系统

    准备材料 树莓派4B U盘 TF卡 树莓派基础镜像2020-08-20稳定版(这个系统是必须的并拷录在TF卡) Kali树莓派系统(这个是我想要学习的系统,大家可以准备自己的系统,拷录在U盘的) SD ...