拷贝构造函数是一种特殊的构造函数,函数的名称必须和类名称一致,它必须的一个参数是本类型的一个引用变量。 作用就是用来复制对象,在使用这个对象的实例来初始化这个对象的一个新的实例。类中可以存在多个拷贝构造函数。

拷贝构造函数的调用时机

  • 当函数的参数为类的对象时
#include<iostream>
using namespace std;
class CExample
{
private:int a;
public:CExample(int b){a=b;printf("constructor is called\n");}CExample(const CExample & c){a=c.a;printf("copy constructor is called\n");}~CExample(){cout<<"destructor is called\n";}void Show(){cout<<a<<endl;}
};
void g_fun(CExample c)
{cout<<"g_func"<<endl;
}
int main()
{CExample A(100);CExample B=A;B.Show(); g_fun(A);return 0;
}

调用g_fun()时,会产生以下几个重要步骤:
(1).A对象传入形参时,会先会产生一个临时变量,就叫 C 吧。
(2).然后调用拷贝构造函数把A的值给C。 整个这两个步骤有点像:CExample C(A);
(3).等g_fun()执行完后, 析构掉 C 对象。

  • 函数的返回值是类的对象
#include<iostream>
using namespace std;
class CExample
{
private:int a;
public://构造函数CExample(int b){a=b;printf("constructor is called\n");}//拷贝构造函数CExample(const CExample & c){a=c.a;printf("copy constructor is called\n");}//析构函数~CExample(){cout<<"destructor is called\n";}void Show(){cout<<a<<endl;}
};
CExample g_fun()
{CExample temp(0);return temp;
}
int main()
{g_fun();return 0;
}

当g_Fun()函数执行到return时,会产生以下几个重要步骤:
(1). 先会产生一个临时变量,就叫XXXX吧。
(2). 然后调用拷贝构造函数把temp的值给XXXX。整个这两个步骤有点像:CExample XXXX(temp);
(3). 在函数执行到最后先析构temp局部变量。
(4). 等g_fun()执行完后再析构掉XXXX对象。

  • 对象需要通过另外一个对象进行初始化
CExample A(100);
CExample B=A;

浅拷贝与深拷贝

拷贝者和被拷贝者若是同一个地址,则为浅拷贝,反之为深拷贝,深拷贝会在堆内存中另外申请空间来储存数据。默认的拷贝构造函数实现的是浅拷贝,数据成员中有指针时,必须要用深拷贝。

一般的赋值操作是深度拷贝:

//深度拷贝
int a = 5;
int b = a;

简单的指针指向,则是浅拷贝:

//浅拷贝
int a = 8;
int *p;
p = &a;char* str1 = "HelloWorld";
char* str2 = str1;

如果要将上面的浅拷贝转换为深拷贝,需要这样

//深度拷贝
int a = 8;
int *p = new int;
*p = a;char* str1 = "HelloWorld";
int len = strlen(str1);
char *str2 = new char[len];
memcpy(str2, str1, len);

以字符串拷贝为例,浅拷贝后,str1和str2同指向0x123456,不管哪一个指针,对该空间内容的修改都会影响另一个指针。

深拷贝后,str1和str2指向不同的内存空间,各自的空间的内容一样。因为空间不同,所以不管哪一个指针,对该空间内容的修改都不会影响另一个指针。

当出现类的等号赋值时,会调用拷贝函数,在未定义显示拷贝构造函数的情况下,系统会调用默认的拷贝函数——即浅拷贝,它能够完成成员的一一复制。当数据成员中没有指针时,浅拷贝是可行的。但当数据成员中有指针时,如果采用简单的浅拷贝,则两类中的两个指针将指向同一个地址,当对象快结束时,会调用两次析构函数,而导致指针悬挂现象。这时,必须采用深拷贝。

默认的拷贝构造函数存在弊端,看如下类定义

class TestCls{
public:int a;int *p;public:TestCls()   //无参构造函数{std::cout<<"TestCls()"<<std::endl;p = new int;}~TestCls()     //析构函数{delete p;   std::cout<<"~TestCls()"<<std::endl;}
};int main(void)
{TestCls t1;TestCls t2 = t1;   //效果等同于TestCls t2(t1);return 0;
}

类的默认拷贝构造函数只会用被拷贝类的成员的值为拷贝类简单初始化,也就是说二者的p指针指向的内存空间是一致的。以前面TestCls可以知道,编译器为我们默认定义的拷贝构造函数为:

TestCls(const TestCls& testCls)
{a = testCls.a;p = testCls.p;      //两个类的p指针指向的地址一致。
}

main函数将要退出时,拷贝类t2的析构函数先得到执行,它把自身p指向的堆空间释放了;接下来,t1的析构函数得到调用,被拷贝类t1的析构函数得到调用,它同样要去析构自身的p指向指向的堆空间,但是该空间和t2类中p指向的空间一样,造成重复释放,程序运行崩溃。

解决办法十分简单,自定义拷贝构造函数,里面用深度拷贝的方式为拷贝类初始化:

class TestCls{
public:int a;int *p;public:TestCls(){std::cout<<"TestCls()"<<std::endl;p = new int;}TestCls(const TestCls& testCls){std::cout<<"TestCls(const TestCls& testCls)"<<std::endl;a = testCls.a;//p = testCls.p;p = new int;*p = *(testCls.p);      //为拷贝类的p指针分配空间,实现深度拷贝}~TestCls(){delete p;   std::cout<<"~TestCls()"<<std::endl;}
};int main(void)
{TestCls t1;TestCls t2 = t1;return 0;
}

防止默认拷贝发生

自定义拷贝构造函数,并设置为private属性,其实现体可以什么都不写,那么这个类将变成一个不可被复制的类了。

拷贝构造函数与深拷贝和浅拷贝相关推荐

  1. C++拷贝构造函数、深拷贝、浅拷贝

    对于普通类型的对象来说,它们之间的复制是很简单的,例如: int a=88; int b=a;  而类对象与普通对象不同,类对象内部结构一般较为复杂,存在各种成员变量.下面看一个类对象拷贝的简单例子. ...

  2. C++拷贝构造函数:深拷贝和浅拷贝

    1 拷贝构造函数 它是一种特殊的构造函数,由编译器调用来完成一些基于同一类的其他对象的构件及初始化. 1.1 拷贝函数的调用场景: 1.值传递传递函数体 2.值传递从函数体返回(返回匿名对象) 3.用 ...

  3. C++基础-拷贝构造函数(深拷贝与浅拷贝)

    拷贝构造函数 是一种特殊的构造函数,用于在建立新对象时将已存在对象的数据成员值复制给新对象,即用一个已存在的对象初始化一个新建立的对象. 定义一个拷贝构造函数的一般形式: 类名 (类名 & 对 ...

  4. 拷贝构造函数的调用以及浅拷贝与深拷贝的理解

    今天一直在研究拷贝构造函数相关的东西,我这个大四老狗感觉又回到了大一学C++的时候.瞎捣鼓了一天,略微还是有些收获的,趁着脑子中的概念正热,把自己的心得赶紧整理出来分享给大家. ​       首先简 ...

  5. 简述构造函数、拷贝构造函数、深拷贝浅拷贝、析构函数

    一.构造函数特点: 1.构造函数也是函数,其函数名和类名相同 2.构造函数无返回值 3.构造函数可以重载 4.构造函数创建对象时自动调用 注:当设计一个类时,如果没有手动实现一个构造函数,那么编译器会 ...

  6. 拷贝构造(深拷贝、浅拷贝)

    一.概念介绍 拷贝构造:拷贝构造函数,又称构造函数,是一种特殊的构造函数,它由编译器调用来完成一些基于同一类的其他对象的构造及初始化. 其唯一的形参必须是引用,但并不限制为const,一般普遍的会加上 ...

  7. 构造函数、深拷贝、浅拷贝

    c++ 什么时候生成默认构造函数? c++中的深拷贝,浅拷贝 浅拷贝带来的问题 重写拷贝构造函数 //重写的拷贝构造函数 Person(const Person &p) {m_Age = p. ...

  8. 理解C++中拷贝构造函数

    拷贝构造函数的功能是用一个已有的对象来初始化一个被创建的同样对象,是一种特殊的构造函数,具有一般构造函数的所有特性,当创建一个新对象的时候系统会自动调用它:其形参是本类对象的引用,它的特殊功能是将参数 ...

  9. C++中深拷贝和浅拷贝

    C++中深拷贝和浅拷贝的问题是很值得我们注意的知识点,如果编程中不注意,可能会出现疏忽,导致bug.本文就详细讲讲C++深浅拷贝的种种. 对于一般的对象,如: C++代码 int a = 10; in ...

最新文章

  1. 在Ubuntu上通过命令行安装Elisa KDE音乐播放器
  2. js生日计算年龄_JS根据生日算年龄的方法
  3. linux fedora下vscode终端字体间距不正常解决办法
  4. Java并发编程之CountDownLatch(闭锁)使用详解
  5. python线程执行完后释放内存_python变量内存地址释放与加速并行计算多线程
  6. 英特尔发布其首个适配Win11的显卡驱动
  7. “管理压力,控制情绪”培训小结
  8. 用C#编写一个进程外的COM组件示例代码讲解
  9. Android组件化项目详细实施方案
  10. python连点封闭多边形_python实现根据给定坐标点生成多边形mask的例子
  11. 二次录入已经OUT! 4S店销售用小帮软件机器人教你做人!
  12. python批量将png格式转换为jpg格式,并保存到新的文件夹
  13. Error: Cannot find module 'chalk'
  14. 数据结构---线性表课后习题详解(朱昌杰编著)
  15. 苹果手机数据能恢复吗
  16. FICO辅助工具介绍【LSMW和Query】
  17. Tomcat启动项目出现 javax.el.ELException
  18. KALI LINUX渗透测试学习笔记
  19. 摘录的Tim Urban关于拖延症的总结
  20. eps8266自动重启问题, Soft WDT reset (已解决)

热门文章

  1. 1km分辨率逐月降雨量和最高温度数据集(1901-2022)
  2. matlab中str2func函数,MATLAB 的函数句柄
  3. OpenDDS与FastDDS的比较
  4. 聊天突然尬住?教你用Python一键获取斗图表情包,各种表情包轻松化解尴尬
  5. DB307S-ASEMI贴片整流桥DB307S
  6. java黑名单校验_短信验证码被刷怎么办?java 短信验证码防刷策略
  7. 虚构建筑·未来城~GANism艺术家会被人工智能取代吗?
  8. 力扣187-重复的DNA序列-C++
  9. 4款令人相见恨晚的APP,个个都是黑科技,后悔没有早点碰到你
  10. SQL2005关于quotename的用法(转)