什么是拷贝构造函数(copy constructor)?

拷贝构造函数是一个类或结构体的特殊构造函数,用于复制一个现有的实例。

一个类名为T的类的复制构造函数是一个非模板构造函数,其第一个参数是T&、const T&、volatile T&或const volatile T&,并且没有其他参数,或者其余参数都有默认值。

根据C++的标准,MyClass的拷贝构造函数(只能有一个)的声明必须是下面之一:

MyClass( const MyClass& other );

MyClass( MyClass& other );

MyClass( volatile const MyClass& other );

MyClass( volatile MyClass& other );

而下面这些构造函数并不是拷贝构造函数,尽管作用和拷贝构造函数类似:

MyClass( MyClass* other );

MyClass( const MyClass* other )

什么时候需要写一个拷贝构造函数?

首先,你应该明白,如果你不声明一个拷贝构造函数,编译器会隐含地给你生成一个默认的。

这个隐含的拷贝构造函数会对源对象进行成员式拷贝。

例如,给定一个类:

class MyClass {

int x;

char c;

std::string s;

};

编译器提供的拷贝构造函数完全等同于:

MyClass::MyClass( const MyClass& other ) :

x( other.x ), c( other.c ), s( other.s )

{}

在许多情况下,这已经足够了。然而,在某些情况下,仅成员式拷贝是不够的。

到目前为止,默认的拷贝构造函数不行的最常见的原因,是由于对象包含原始指针,需要对指针进行 "深度 "拷贝。

也就是说,你不希望拷贝指针本身;相反,你想拷贝指针的指向的东西。

为什么你需要进行 "深度 "拷贝?

这是因为通常实例拥有该指针,也就是说,实例负责对该指针调用删除命令进行内存释放,可能是在析构器或其他时刻。

如果两个对象最终在同一个非空指针上调用删除操作,就会导致堆损坏。

你很少会遇到不包含原始指针的类,所以默认的拷贝构造函数一般是不够的。

语法

class-name ( const class-name & ) (1)

class-name ( const class-name & ) = default; (2) (since C++11)

class-name ( const class-name & ) = delete; (3) (since C++11)

其中class-name是当前类的名字(或类模板的当前实例),或者,当在命名空间范围内或在友方声明中声明时,它必须是一个合格的类名。

上面三种情况说明:

1) 复制构造函数的典型声明。

2) 强制由编译器生成一个拷贝构造函数。这样用户无法再重载或定义自己的拷贝构造函数。

3) 避免隐式生成拷贝构造函数。

每当一个对象从另一个相同类型的对象初始化(通过直接初始化或复制初始化)时,就会调用拷贝构造函数(除非重载解析选择了更好的匹配或调用被省略)。

使用复制构造函数有三种情况:

a. 初始化: T a = b;或者T a(b);,其中b是T的类型。

b. 函数参数传递:f(a);,其中a是T类型的,f是void f(T t)。

c. 函数返回:返回a;在一个函数里面,比如T f(),其中a是T类型.

其中b的情况是,调用函数f时,会构造一个对象作为参数传递给函数。

c的情况是,函数返回一个对象时,也会构造一个对象,并返回。

我们来使用代码验证一下:

copyctor.cpp#include <cstdio>class A
{
public:A(const A& obj){printf("Copy ctor called.\n"); }A(){printf("Normal ctor called.\n");}};void parameterA(A a)
{}int main()
{A a;printf("===============\n");A b = a;printf("===============\n");A c(a);printf("===============\n");parameterA(a);}$ g++ -o copyctor copyctor.cpp
$ ./copyctor
Normal ctor called.
===============
Copy ctor called.
===============
Copy ctor called.
===============
Copy ctor called.

关于拷贝构造函数的参数

在拷贝构造函数的的声明中,其参数前面可以加const也可以不加。

比如:

MyClass( const MyClass& other );

MyClass( MyClass& other );

这两种声明方式都是可以的。

但最好加上const,因为这里使用的是引用参数,而在C++标准里规定非常量引用类型是不能引用临时变量的。

理解起来就是,你把临时对象作为引用传进来,如果不加const,那就会修改这个对象,但临时对象的生命周期管理会引起问题。

所以加上const的引用类型,来作为函数参数,才能将临时对象传进来。

并且拷贝构造函数,一般也不需要对源对象做修改。

那么临时对象是什么样的?

比如:

std::string("Hello!");

这就是一个没有名字的临时对象。

我们来写个代码体验一下:

#include <cstdio>#include <string>void printStringNoConst(std::string & s){printf("%s\n", s.c_str());}void printStringConst(const std::string & s){printf("%s\n", s.c_str());}int main(){std::string txt = "Hello!";printStringNoConst(std::string("123"));  // Compile error printStringConst(std::string("123"));  // OKstd::string hello( "Hello" );printStringNoConst(hello);  // OKprintStringConst(hello);  // OKreturn 0;}$ g++ -o test test.cpptest.cpp: In function ‘int main()’:test.cpp:19:27: error: cannot bind non-const lvalue reference of type ‘std::string&’ {aka ‘std::__cxx11::basic_string<char>&’} to an rvalue of type ‘std::string’ {aka ‘std::__cxx11::basic_string<char>’}19 |   printStringNoConst(std::string("123"));|                           ^~~~~~~~~~~~~test.cpp:5:39: note:   initializing argument 1 of ‘void printStringNoConst(std::string&)’5 | void printStringNoConst(std::string & s)

参考:

Copy constructors - cppreference.comhttps://en.cppreference.com/w/cpp/language/copy_constructor

Copy constructors, assignment operators, - C++ Articleshttp://www.cplusplus.com/articles/y8hv0pDG/

C++实操 - 拷贝构造函数相关推荐

  1. 「动手学深度学习」在B站火到没谁,加这个免费实操平台,妥妥天花板!

    论 AI 圈活菩萨,非李沐老师莫属. 前有编写「动手学深度学习」,成就圈内入门经典,后又在B站免费讲斯坦福 AI 课,一则艰深硬核讲论文的视频播放量36万,不少课题组从导师到见习本科生都在追番. 如此 ...

  2. 华为昇腾师资培训沙龙·南京场 |华为昇腾 ACL 语言开发实践全程干货来了!看完就实操系列...

    自今年疫情以来,AI 技术加速进入了人们的视线,在抗疫过程中发挥了重要作用,产业发展明显提速,我国逐步走出了一条由需求导向引领商业模式创新.市场应用倒逼基础理论和关键技术创新的发展道路,AI 人才的争 ...

  3. 实操教程|PyTorch AutoGrad C++层实现

    点击上方"视学算法",选择加"星标"或"置顶" 重磅干货,第一时间送达 作者丨xxy-zhihu@知乎 来源丨https://zhuanla ...

  4. 机器学习的入门平台天花板,还可免费实操经典教程,确实也没谁了

    韩智 边策 发自 凹非寺 量子位 报道 | 公众号 QbitAI 论 AI 圈活菩萨,非李沐老师莫属. 前有编写「动手学深度学习」,成就圈内入门经典,后又在B站免费讲斯坦福 AI 课,一则艰深硬核讲论 ...

  5. git按照tag拉取代码_Git实操小课堂

    平时也多是使用 IDEA 自带的 Git 插件,简单又方便,不需要理解 Git 背后的技术,最近突然让我在 VsCode 上更新提交代码,发现又需要学习 VsCode 的插件使用,思量一番后,决定好好 ...

  6. 工欲善其事必先利其器,TI-ONE平台“实操手册”在这里!

    为帮助选手们更好地备战赛事,2021腾讯广告算法大赛官方于5月10日至5月12日每晚七点,开启了"视"界杯系列专题直播活动.在5月11日的直播中,腾讯云高级工程师谢博文.彭彪及腾讯 ...

  7. 面试准备每日五题:C++(七)——左值右值、面向对象、四种cast转换、拷贝构造函数赋值、虚函数多态

    文章目录 一. 什么是右值引用,跟左值又有什么区别? 二. 面向对象的三大特征 三. c++中四种cast转换 四.拷贝构造函数和赋值运算符的认识 五. 对虚函数和多态的理解 一. 什么是右值引用,跟 ...

  8. 「动手学深度学习」在B站火到没谁,加这个免费实操平台,妥妥天花板

    论 AI 圈活菩萨,非李沐老师莫属. 前有编写「动手学深度学习」,成就圈内入门经典,后又在B站免费讲斯坦福 AI 课,一则艰深硬核讲论文的视频播放量36万,不少课题组从导师到见习本科生都在追番. 如此 ...

  9. 拷贝构造函数和赋值构造函数声明为私有的作用

    转贴地址:http://blog.csdn.net/winer632/archive/2009/01/12/3762292.aspx 每个类只有一个赋值函数. 由于并非所有的对象都会使用拷贝构造函数和 ...

最新文章

  1. Windows Mobile下访问Sqlite的Native C++封装
  2. 06-02-测试 Office Online Server Updated 2018
  3. 一个 .git 目录,领悟 Git 的强大!
  4. Oracle中查看最近被修改过的表的方法
  5. 什么是RPC?RPC框架dubbo的核心流程
  6. program的发展史与两个数学方法
  7. 前端学习(2818):小程序学习之新建页面
  8. 计算一个子网掩码有多少个有效ip地址_一个月有多少个工作日,你会计算了吗?...
  9. hadoop基本思想与概念
  10. 单个对象和多个对象在内存中的结构图
  11. 摄影平铺海报psd模板|简单搭建层次场景海报
  12. C语言之文件读写探究(一):fopen、fclose(文件的打开和关闭)
  13. c语言直流电机控制实验报告,直流电机实验报告.docx
  14. QQ自动登录 发消息给某人C++/C
  15. PTAM的笔记(二)---ptam移植到android平台
  16. 一种低成本的机械动平衡方法 郑氏橡皮泥动平衡法
  17. Docker端口映射不起作用的解决办法
  18. 解决esp8266无法连接手机和电脑热点的问题
  19. TeamTalk IM_PDUBASE详解
  20. 大数据之道 HMM系列

热门文章

  1. 外网访问内网svn【内网穿透】
  2. 选择性搜索算法(Selective Search)超详解(通俗易懂版)
  3. IP 归属地查询 API 教你从0到1顺着网线找到键盘侠
  4. java.lang.reflect.InvocationTargetException异常的一种解决方法
  5. 好文:如何成为一个独立思考之人?
  6. 拆除工程计价的注意事项
  7. 项目经理的五项基本能力
  8. FPGA定点小数二进制乘法运算
  9. HTML+CSS+JS网页设计
  10. vue.js 大小写转换