拷贝控制、赋值和销毁

  • 如果一个构造函数的第一个参数是自身类的引用,且额外的参数都有默认值,则此构造函数是拷贝控制函数(拷贝构造函数不应该是explicit的)。
  • 如果我们没有为一个类定义拷贝构造函数,编译器会为我们定义一个,与合成默认构造函数不同, 即使我们定义了其他构造函数,编译器也会为我们合成一个拷贝构造函数。
  1. class Sales_data
  2. {
  3. public:
  4. Sales_data(const Sales_data&);
  5. private:
  6. string bookNo;
  7. int units_sold = ;
  8. double revenue = 0.0;
  9. };
  10.  
  11. Sales_data::Sales_data(const Sales_data &rhs):
  12. bookNo(rhs.bookNo), //ISBN号
  13. units_sold(rhs.units_sold), //销量
  14. revenue(rhs.revenue) //总销售收入
  15. {}

  

析构函数

  • 当一个类定义自己的析构函数时,编译器会为它定义一个合成析构函数。

练习参考答案

13.13

  1. #include<iostream>
  2. #include<string>
  3. #include<vector>
  4. using namespace std;
  5.  
  6. struct X
  7. {
  8. X() { cout << "默认构造函数X()" << endl; }
  9. X(const X&) { cout << "拷贝构造函数 X(const X&)" << endl; }
  10. X& operator=(const X &rhs) { cout << "拷贝赋值运算符=(const X&)" << endl; return *this; }
  11. ~X() { cout << "析构函数 ~()" << endl; }
  12. };
  13.  
  14. void f1(X x)
  15. {}
  16.  
  17. void f2(X &x)
  18. {}
  19.  
  20. int main()
  21. {
  22. cout << "局部变量" << endl;
  23. X x;
  24. cout << endl;
  25.  
  26. cout << "非引用参数传递:" << endl;
  27. f1(x);
  28. cout << endl;
  29.  
  30. cout << "引用参数传递" << endl;
  31. f2(x);
  32. cout << endl;
  33.  
  34. cout << "动态内存:" << endl;
  35. X *px = new X(x);
  36. cout << endl;
  37.  
  38. cout << "添加到容器中:" << endl;
  39. vector<X> vx;
  40. vx.push_back(x);
  41. cout << endl;
  42.  
  43. cout << "释放动态内存" << endl;
  44. delete px;
  45. cout << endl;
  46.  
  47. cout << "间接初始化和赋值:" << endl;
  48. X y = x;
  49. y = x;
  50. cout << endl;
  51.  
  52. cout << "程序结束" << endl;
  53. return ;
  54. }

运行结果:


  

三五法则

  • 如果一个类需要自定义析构函数,几乎可以肯定它也需要自定义拷贝赋值运算符和拷贝构造函数。
  • 如果一个人类需要一个拷贝构造函数,几乎可以肯定它也需要一个拷贝赋值运算符。
  • 如果一个类需要一个拷贝赋值运算符,几乎可以肯定它也需要一个拷贝构造函数。

 练习参考答案

  1. //13.14: 0 0 0
  2.  
  3. //13.15: 3 4 5
  4.  
  5. //13.16: 0 1 2
  6.  
  7. #include<iostream>
  8. using namespace std;
  9.  
  10. class numbered
  11. {
  12. private:
  13. static int seq;
  14. public:
  15. numbered() { mysn = seq++; }
  16. numbered(numbered &n) { mysn = seq++; }
  17. int mysn;
  18. };
  19.  
  20. int numbered::seq = 0;
  21.  
  22. void f(numbered s)
  23. {
  24. cout << s.mysn << endl;
  25. }
  26.  
  27. int main(int argc, char **argv)
  28. {
  29. numbered a, b = a, c = b;
  30. f(a);
  31. f(b);
  32. f(c);
  33. return 0;
  34. }

 

使用=default

  • 我们可以通过将拷贝控制成员定义为=default来显式地要求编译器生成合成的版本。
  1. class Sales_data
  2. {
  3. public:
  4. //拷贝控制成员; 使用default
  5. Sales_data() = default;
  6. Sales_data(const Sales_data &) = default;
  7. Sales_data& operator=(const Sales_data &);
  8. ~Sales_data() {} = default;
  9. //其他成员的定义,如前
  10. }
  11. Sales_data& Sales_data::operator=(const Sales_data &) = default;

阻止拷贝

如果一个类有数据成员不能默认构造、拷贝、复制和销毁,则对应的成员函数将被定义为删除的。规则引申如下:

  • 如果类的某个成员的析构函数时删除的或不可访问的,则类的合成析构函数被定义为删除的;同时类的默认构造函数是删除的;同时类的合成拷贝构造函数也被定义为删除的。(析构函数被定义为删除的,则不能定义该类型的变量。)
  • 如果类的某个成员的拷贝构造函数是删除的或不可访问的,则类的合成拷贝构造函数被定义为删除的;
  • 如果类的某个成员的拷贝赋值运算符是删除的或不可访问的,或类有一个const的或引用成员,则类的合成拷贝赋值运算符被定义为删除的;
  • 如果类有一个引用成员,它没有类内初始化器,或是类有一个const成员,没有类内初始化器且其类型未显式定义默认构造函数,则该类的默认构造函数是删除的。

练习参考答案

【练习13.5】

  1. class HasPtr
  2. {
  3. public:
  4. HasPtr(const string &s = string()): ps(new string(s)), i() {}
  5. HasPtr(const HasPtr &rhs): ps( new string(*rhs.ps)), i(rhs.i) {}
  6. private:
  7. string *ps;
  8. int i;
  9. }

【练习13.8】

  1. class HasPtr
  2. {
  3. public:
  4. HasPtr(const string &s = string()): ps(new string(s)), i() {}
  5. HasPtr(const HasPtr &rhs): ps( new string(*rhs.ps)), i(rhs.i) {}
  6.  
  7. HasPtr operator=(const HasPtr &rhs);
  8. private:
  9. string *ps;
  10. int i;
  11. }
  12.  
  13. HasPtr HasPtr::operator=(const HasPtr &rhs)
  14. {
  15. ps = new string(*rhs.ps);
  16. i = rhs.i;
  17. return *this;

【C++ Primer 第13章】1. 拷贝控制、赋值和销毁的更多相关文章

  1. [C++ Primer] : 第13章: 拷贝控制

    拷贝, 赋值与销毁 当定义一个类时, 我们显示地或隐式地指定在此类型的对象拷贝, 移动, 赋值和销毁时做什么. 一个类通过定义5种特殊的成员函数来控制这些操作, 包括: 拷贝构造函数, 拷贝赋值运算符 ...

  2. 【C++ Primer 第13章】2. 拷贝控制和资源管理

    拷贝控制和资源管理 • 类的行为像一个值.意味着它应该有自己的状态,当我们拷贝一个像值得对象时,副本和原对象是完全独立的,改变副本不会对原对象有任何影响. • 行为像指针的类则共享状态.当我们拷贝一个 ...

  3. C++ Primer : 第十三章 : 拷贝控制之拷贝、赋值与销毁

    拷贝构造函数 一个构造函数的第一个参数是自身类类型的引用,额外的参数(如果有)都有默认值,那么这个构造函数是拷贝构造函数.拷贝构造函数的第一个参数必须是一个引用类型. 合成的拷贝构造函数   在我们没 ...

  4. 【C++ Primer | 15】构造函数与拷贝控制

    合成拷贝控制与继承 #include <iostream> using namespace std; class Base { public: Base() { cout << ...

  5. 【C++ Primer 第13章】3. 交换操作

    交换操作 class HasPtr { friend void swap(HasPtr &rhs, HasPtr &yhs); //其他成员定义 }; void swap(HasPtr ...

  6. 【C++ Primer 第13章】6.对象移动

    右值引用 左值和右值 (1)两者区别: ①左值:能对表达式取地址.或具名对象/变量.一般指表达式结束后依然存在的持久对象. ②右值:不能对表达式取地址,或匿名对象.一般指表达式结束就不再存在的临时对象 ...

  7. 【C++ Primer 第13章】5. 动态内存管理类

    StrVec类的设计 [题目描述]:我们将实现标准库vector类的一个简化版本,我们所做的一个简化是不使用模板,我们类只用于string,因此,它被命名为StrVec. #include<io ...

  8. 《C++ Primer》笔记 第13章 拷贝控制

    拷贝和移动构造函数定义了当用同类型的另一个对象初始化本对象时做什么.拷贝和移动赋值运算符定义了将一个对象赋予同类型的另一个对象时做什么.析构函数定义了当此类型对象销毁时做什么.我们称这些操作为拷贝控制 ...

  9. 【c++ Prime 学习笔记】第13章 拷贝控制

    定义一个类时,可显式或隐式的指定在此类型对象上拷贝.移动.赋值.销毁时做什么.通过5种成员函数实现拷贝控制操作: 拷贝构造函数:用同类型的另一个对象初始化本对象时做什么(拷贝初始化) 拷贝赋值算符:将 ...

随机推荐

  1. DAC杂谈二 ——ADC和DAC常用技术术语

    采集时间 采集时间是从释放保持状态(由采样-保持输入电路执行)到采样电容电压稳定至新输入值的1 LSB范围之内所需要的时间.采集时间(Tacq)的公式如下: 混叠 根据采样定理,超过奈奎斯特频率的输入 ...

  2. 什么是 java.awt.headless

    以下是网上的说法,我觉得简单地说就是有些功能需要硬件设备协助,比如显卡,但如果是服务器可能都没装显卡,这时就需要JDK调用自身的库文件去摸拟显卡的功能. 什么是 java.awt.headless? ...

  3. 在 uniGUI 中实现自动弹窗后延迟几秒关闭 — Toast 功能

    在 uniGUI 中实现自动弹窗后延迟几秒关闭 — Toast 功能. uniGUI 的客户端使用 EXTJS 6 ,本就有 Toast 功能. 但UniGui 官方没有相应的控件,我们如何使用 EX ...

  4. 用ARX自定义实体

      本文介绍了构造自定义实体的步骤.必须继承的函数和必须注意的事项 1.新建一个从AcDbEntity继承的类,如EntTest,必须添加的头文件: "stdarx.h",&quo ...

  5. ObjectArx2013新建工程出错的解决办法

    最近将一个ObjectArx升级到Arx2013版,使用ObjectArx2013向导时,新建项目时弹出错误"未能加载项目文件.给定编码中的字符无效.第1行,位置1",经网上查找发 ...

  6. Holedox Eating HDU - 4302 2012多校C 二分查找+树状数组/线段树优化

    题意 一个长度$n<=1e5$的数轴,$m<=1e5$个操作 有两种一些操作 $0$  $x$ 在$x$放一个食物 $1$ 一个虫子去吃最近的食物,如果有两个食物一样近,不转变方向的去吃 ...

  7. Git系列①之仓库管理互联网托管平台github.com的使用

    互联网项目托管平台github.com的使用 1.安装git客户端 # yum install -y git 配置git全局用户以及邮箱 [root@web01 ~]# git config --gl ...

  8. java的小数比较反例

    double num = 0.3; System.out.println(num); System.out.println(num - 0.2); System.out.println(num - 0 ...

  9. FS 日志空间限定

    一.说明: FS默认安装的log文件,仅仅的限制了每个文件的大小,但是没有限制文件的个数.这种情况下,在FS运行很长时间之后,会出现物理空间不够的情况,导致FS或者mysql 或者其他应用没有空间使用 ...

  10. MariaDB主从备份记录

    一.预期效果: 环境: centos 6.5   mariadb 10.0.14 (mysql -V) 主服务器:192.168.5.206   从服务器:192.168.5.207   主服务器数据 ...