一. 概述

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

环境: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. 如何使用perf进行程序分析

    1.安装. sudo apt-get install linux-tools 如果提示没有可安装候选.请输入: sudo apt-get install linux-perf-version 其中ve ...

  2. 给元素设置overflow:hidden,pc端正常,但移动端依然可以滑动

    给设置overflow:hidden的元素设置position:fixed即可解决

  3. Java8中一个极其强悍的新特性,很多人没用过(非常实用)

    Java8中有两个非常有名的改进,一个是Lambda表达式,一个是Stream.如果我们了解过函数式编程的话,都知道Stream真正把函数式编程的风格引入到了java中.这篇文章由简入繁逐步介绍Str ...

  4. ORA-09817:Write to audit file failed

    ERROR: ORA-09817: Write to audit file failed. --这种错误基本是磁盘空间满了 问题描述:sqlplus / as sysdba登不上,报上述ora-098 ...

  5. 复习Spring第三课--数据源配置的多种方式

    spring数据源配置可以说分为:spring容器自带连接池.项目中创建连接池.服务器创建连接池三种 一.spring容器自带连接池   Spring本身也提供了一个简单的数据源实现类DriverMa ...

  6. 第11章 PADS功能使用技巧(2)-最全面

    原文链接点击这里 七.Flood与Hatch有什么区别? 我们先看看PADS Layout Help 文档是怎么说的,如下图所示: 从检索到的帮助信息,我们可以得到Hatch与Pour的区别,原文如下 ...

  7. WIN10无法进行Android应用开发真机调试解决方案

    在WIN10操作系统进行ANDROID开发真机调试时,遇到的问题主要归纳一下有以下几点: 一.没有打开"USB调试"项.这点不再赘述: 二.没有安装ADB Interface驱动: ...

  8. 『心善渊』Selenium3.0基础 — 6、Selenium中使用XPath定位元素

    目录 1.Selenium中使用XPath查找元素 (1)XPath通过id,name,class属性定位 (2)XPath通过标签中的其他属性定位 (3)XPath层级定位 (4)XPath索引定位 ...

  9. c#在类中使用session

    先要继承页面的System.Web.UI.Page using System; using System.Collections.Generic; using System.Linq; using S ...

  10. 创建Akamai CDN

    背景说明: XX全球版项目CDN, 原有改之前:主CDN为Akamai,备CDN为Cloudflare. 计划改之后:主CDN为Cloudflare,备CDN为Akamai. 原因:Akamai CD ...