1. 初始化构造函数

构造函数的函数体内属于数据成员的赋值,初始化发生在函数体语句之前,所以需要显式初始化的成员必须在初始化列表中完成初始化,包括:

  1. 包含 const 或引用成员,且没有提供默认初始化值,这两者只能初始化而不能赋值。
  2. 包含了没有默认构造函数的类数据成员,没有默认构造函数的不能隐式初始化。只有当一个类没有定义任何构造函数时,编译器才会自动生成默认构造函数。
  3. 基类没有默认构造函数,派生类必须使用初始化列表显式调用基类的构造函数。这个与第 2 条类似。

初始化列表中成员初始化的顺序与其在类中声明的顺序一致,与初始化列表中的顺序无关,不要用声明在后面的成员初始化声明在前面的成员。但在构造函数内部属于赋值操作,可以不按声明的顺序赋值。

最好使用默认实参来代替默认构造函数,如 A(int _a=0) { a = _a; },可以节省代码量。

派生类不能直接初始化基类的数据成员,即便对于派生类是可见的。但是派生类可以在构造函数内对基类可见成员进行赋值。

2. 拷贝构造函数与重载赋值操作符

编译器会提供默认的拷贝构造函数以及重载赋值操作符,但是这只是浅拷贝,即单纯复制其数据成员的值,如果数据成员包含指针,并指向了对象以外的内存,就容易造成访问冲突的情况。为了更加灵活地处理对象的拷贝,通常需要手动定义拷贝构造函数以及重载赋值操作符,一般形式如下:

 A(const A& a);A& operator=(const A& a);

注意,对于拷贝构造函数,输入参数必须采用引用的方式传递,这并不是代码风格的问题,而是 C++ 标准就是这样要求。这是因为如果采用按值传递的方式,会导致拷贝构造函数的递归调用,因为按值传递本身就需要调用拷贝构造函数。而对于赋值操作符,采用按值传递只是多调用了一次拷贝构造函数,所以这是允许的,只是从效率上讲通常会使用引用。

注意,尽管有时候输入参数并不强制为 const,但不使用 const 修饰会导致很多的麻烦。没有 const 修饰的参数只能接受左值的输入,而无法接受右值(即不具名的临时变量),当然在 C++11 之后我们可以通过右值引用来定义移动构造函数来处理这个问题。但更严重的问题是,拷贝构造函数的输入有时候本身就是 const 对象,如果参数没有 const 修饰,参数列表就无法匹配,也就无法调用拷贝构造函数。例如在 vector 容器中,我们可以通过调用 vector(size_t n, const T& x); 来创建一个长度为 n,初始值为 x 的向量,但是如果 T 的拷贝构造函数参数不为 const,我们就没办法对 x 进行拷贝,这会严重影响我们对 STL 容器的使用。

赋值运算符的重载不一定要有返回值,因为重载的时候函数会包含左侧操作数的 this 指针,从而可以直接修改其值。使用返回值主要是为了实现等号的连续赋值,即如 x=y=z; 但注意这时一般使用引用返回,返回的是 *this 的引用。因为 *this 对象的作用域在函数之外,返回其引用是有意义的。如果直接返回 *this 对象,那么对于 x=y; 除了调用一次 operator= 以外还有一次拷贝构造函数的调用,即编译器生成了一个临时变量来存储 operator= 返回的结果。对于 x=y=z; 实际上相当于 A tmp1(y=z); A tmp2(x=tmp1); 即多调用了两次拷贝构造函数,所以应该尽量使用引用返回。

3. 移动拷贝构造函数与移动赋值函数 (C++11)

对于拷贝构造函数以及赋值重载,我们使用 const 来防止对原始对象的修改,所以当 A 中包含了动态分配的内存时,应该新开辟一块内存副本来拷贝原始对象的内容,而不是直接复制其内存地址,这就是对象的深拷贝。如果直接复制动态内存的地址,当原始对象在某个阶段被析构时,该内存就会被 delete 释放掉,这时保留其指针已经没有意义了,这就是浅拷贝以及多个指针指向同一块内存的弊端。

然而,如果原始对象是右值,即一些临时变量,我们没必要再重新开辟一块内存副本,因为我们知道临时变量即便不去修改马上也会被销毁,这时我们直接把原始对象指向的动态内存地址保留下来即可,但是需要把原始对象的指针置为空,这样才不会在原始对象被析构时把内存释放掉,这就是移动构造函数的存在意义。假设 A 包含了一个指针如 int *ptr,其一般形式如下:

    A(A&& a) noexcept    // 移动构造以及移动赋值函数一般要加上 noexcept,{                    // 用于告诉编译器函数不会发生异常,可以进行优化,没有的话STL可能调用的是普通拷贝构造函数ptr = a.ptr;a.ptr = nullptr;}

因为我们需要修改原始对象的指针,所以不能使用const修饰。这时编译器会根据原始对象是左值还是右值来选择拷贝构造函数还是移动构造函数。

 A getA() {A x;return x;}A a = getA();

在以上的例子中,实际发生了一次构造和两次拷贝,即首先构造局部变量 x,x 在函数结束后就会被析构销毁,属于临时变量。因为按值返回发生了一次拷贝,生成了另外一个临时变量 getA(),最后才被拷贝到具名对象 a 中。如果 A 中包含了动态分配的内存,普通的拷贝构造函数由于深拷贝的原因需要进行两次动态内存的拷贝,而移动构造函数则不需要任何的动态内存拷贝,降低了程序运行的复杂度。实际上,以上的例子在编译器中通常会被优化为只需单次构造无需拷贝,关掉优化可使用 -fno-elide-constructors 选项。

C++、构造函数与拷贝构造函数相关推荐

  1. 不存在从void转换到sqlist的适当构造函数_拷贝构造函数与赋值构造函数

    拷贝构造函数与赋值构造函数 在C++中,如果要创建一个新的类,并用已有的类来给它附初值.就要用到拷贝构造函数,拷贝构造函数又分为两种. 1.合成的拷贝构造函数 在你没有定义自己的拷贝构造函数而又调用了 ...

  2. String 的普通构造函数、拷贝构造函数、析构函数、赋值函数

    转自:http://blog.csdn.net/xiaoxiangzhu660810/article/details/8149398 题目:编写类String的构造函数.析构函数和赋值函数,已知类St ...

  3. C++ : 构造函数,拷贝构造函数,移动构造函数,拷贝赋值运算符,移动赋值运算符应用场景

    构造函数,拷贝构造函数,移动构造函数,拷贝赋值运算符,移动赋值运算符应用场景 #include <iostream> using namespace std;class Construct ...

  4. 构造函数、拷贝构造函数和析构函数的的调用时刻及调用顺序

    构造函数.拷贝构造函数和析构函数的的调用时刻及调用顺序 对象是由"底层向上"开始构造的,当建立一个对象时,首先调用基类的构造函数,然后调用下一个派生类的构造函数,依次类推,直至到达 ...

  5. 默认构造函数和拷贝构造函数

    构造函数 构造函数包括默认构造函数.拷贝构造函数和一般构造函数. 在编程时,如果程序员不显式声明和定义上述函数,编译器将自动产生4个public inline的默认函数. A();          ...

  6. 构造函数,拷贝构造函数,赋值函数

        C++中一般创建对象,拷贝或赋值的方式有构造函数,拷贝构造函数,赋值函数这三种方法.下面就详细比较下三者之间的区别以及它们的具体实现 1.构造函数 构造函数是一种特殊的类成员函数,是当创建一个 ...

  7. 类string的构造函数、拷贝构造函数和析构函数

    原文:http://www.cnblogs.com/Laokong-ServiceStation/archive/2011/04/19/2020402.html 类string的构造函数.拷贝构造函数 ...

  8. 构造函数、拷贝构造函数、赋值函数和析构函数

    文章目录 一.构造函数 1.认识构造函数 2.初始化列表 二.拷贝构造函数 1.类对象的拷贝 2.浅拷贝和深拷贝 三.赋值函数 四.析构函数 1.认识析构函数 2.销毁,清理? 3.析构函数来阻止该类 ...

  9. 移动构造函数和拷贝构造函数的区别

    讲讲移动构造函数与拷贝构造函数的区别 :移动构造函数是c++11的新特性,移动构造函数传入的参数是一个右值 用&&标出.一般来说左值可以通过使用std:move方法强制转换为右值.首先 ...

  10. C++ 构造函数、复制构造函数,拷贝构造函数(深拷贝、浅拷贝)

    文章目录 前言 一.什么是构造函数? 二.构造函数的分类 复制构造函数 复制构造函数被调用的三种情况 复制构造函数在以下三种情况下会被调用. 三.拷贝构造函数的分类 深.浅拷贝构造函数的区别 示例代码 ...

最新文章

  1. SQL Server Management Studio 2012 设置脚本默认保存路径
  2. numpy——hsplit()、vsplit()函数的详细使用
  3. 程序员面试题精选100题(37)-寻找丑数[算法]
  4. html自动播放图箭头,html5制作焦点图左右导航箭头样式
  5. 微信小程序开发工具中快捷键
  6. 自动生成WebForm中对实体类的编辑页面
  7. 真神器!在家也能控制公司的电脑了
  8. PPT优秀模板|7个技巧,让你的设计呈现更加完美
  9. for语句的执行过程_深入学习MySQL 01 一条查询语句的执行过程
  10. 阿里云服务器创建历史功能介绍 快速创建云服务器
  11. matlab两张图片显示,matlab怎么同时显示imshow 两幅图片
  12. linux系统是不是国产的,LINUX是什么系统,是国产软件吗
  13. 深度学习之编程语言Python(Ⅰ)
  14. 2.12美团点评技术
  15. 3.卡券、直充订单详情(post 表单提交)
  16. 支付宝服务商条码付,直接打款给签约子商户方法
  17. C++优化三板斧:Three Optimization Tips for C++
  18. lua 16进制转10 10转16进制
  19. 图片文字怎么转换成Word文档?教你两招快速解决
  20. 调用工厂模式来实现对象实例化

热门文章

  1. android13 kasan
  2. 一个“软件测试工程师”的《读书单》
  3. Java设计模式之结构型:桥接模式
  4. 2022-11 airpods pro/airpods pro2 连接win11 可能的解决方法
  5. 七夕,阿里云AI帮你算算你的撩妹战斗值
  6. mtr-网络分析工具
  7. 手拍试卷的位置以及打印矫正
  8. 简笔画动画儿童教育卡通PPT-朴尔PPT
  9. windows通信端口初始化失败_报错1011模拟器启动端口失败,请尝试修复系统!
  10. matlab设置consolas+雅黑字体