一. 概述

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

环境: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. JMeter定时器种类+详细教程举例

    首先,我们先了解一下定时器的常见种类以及它的作用. 原文地址:https://www.cnblogs.com/istart/p/11184533.html 一.定时器种类+作用 上面是我截图的自己有道 ...

  2. Mysql在线DDL

    1.  Mysql各版本DDL方式 1.1 MysqlDDL演进 当mysql某个业务表上有未提交的活动事务的时候,你去执行在线DDL,这相当危险,直接会被卡住,show processlist里面会 ...

  3. Redis 底层数据结构之字典

    文章参考 <Redis 设计与实现>黄建宏 字典 在字典中,每个键都是独一无二的,程序可以在字典中根据键查找与之相关联的值,或者通过键来更新和删除值. 字典在 Redis 中的应用相当广泛 ...

  4. sed 大括号 sed {} 的作用详解

    今天看别人写的脚本的时候,看到了sed  -r   {}   我看网上对于这个的记录比较少,所以就写了这篇随笔. 先看一下效果 cat  test.txt image: qqq/www/eee:TAG ...

  5. CentOS查看和修改PATH环境变量的方法 (转)

      查看PATH:echo $PATH以添加mongodb server为列修改方法一:export PATH=/usr/local/mongodb/bin:$PATH//配置完后可以通过echo $ ...

  6. JavaWeb中Servlet和Jsp跳转路径的写法

    最近学习时,常常要写一些页面之间的跳转或者前台和后端之间的跳转 下面总结一下自己对于这些跳转路径的写法 声明:以下讲到的jsp文件都默认在WebRoot目录下 1.表单(Jsp)->Servle ...

  7. Linux 3.16 release 贡献度

    内核 3.16 release 的贡献度可以在下面网页看到: http://www.remword.com/kps_result/3.16_whole.html 一共发布了 12802 个补丁, 18 ...

  8. 跨域解决之JSONP和CORS的详细介绍

    JSONP跨域和CORS跨域 什么是跨域? 跨域:指的是浏览器不能执行其它网站的脚本,它是由浏览器的同源策略造成的,是浏览器的安全限制! 同源策略 同源策略:域名.协议.端口均相同. 浏览器执行Jav ...

  9. git研究详解(官网文档)及总结

    前言:git作为新一代的版本控制软件,说实话比svn好用多了,个人见解,关于git的详细介绍及研究,我推荐三个地方 1.git官网上的文档(推荐UC浏览器,比火狐多个英文翻译的功能) 地址为:http ...

  10. windows服务器下MySQL配置字符集

    这俩天公司使用.netcore微服务+mysql做项目,mysql在使用的时候总是出现一些字符集的问题,修改utf8或utf8mb4后mysql的服务就启动不了,这里做下记录如果把my.ini中的字符 ...