C++ Primer学习总结 第13章 拷贝控制

第13章 拷贝控制

1.    什么是拷贝构造函数? P440

如果一个类的构造函数的第一个参数是自己类类型的引用, 且所有其他参数都有默认值, 那么这就是一个拷贝构造函数.

2.    拷贝初始化和直接初始化.  P441

如果初始化的时候使用等号”=”来初始化一个对象, 那么就是拷贝初始化.

相反, 如果初始化时, 没有用等号”=”, 那么就是直接初始化.

如果是拷贝初始化, 那么用的一定是拷贝构造函数(也有可能用移动构造函数).

如果是直接初始化, 那么就用参数形式对应的构造函数, 但是也有可能用的还是拷贝构造函数.

3.    拷贝构造函数不仅仅在我们用=定义变量时发生, 还发生在:

传递实参对象给非引用形参.

返回对象作为非引用返回值.(但是如果以该返回对象继续初始化一个新对象的话,
这个过程就不会调用拷贝构造函数了
)

用花括号列表初始化数组中的一个元素或一个聚合类的成员.

vector等对象用花括号初始化或使用push_back和insert等操作时.

上面b.v本来推测应该是31, 但是却是21. 这里应该是编译器在初始化b的时候主动略过了拷贝构造函数, 所以如果我们想定义一个类的拷贝构造函数,
就正确的定义它的行为.
不要像上述例子一样添加一些本质上不是拷贝的行为. 因为你没法100%保证到底拷贝构造函数能被执行几次…

4.    为什么拷贝构造函数的类对象形参必须是引用类型?

假设拷贝构造函数传递的类对象不是引用了. 现在要将实参B传递给非引用形参A, 那么A要调用自己的拷贝构造函数通过B来构造A. 但是A的拷贝构造函数也需要一个非引用的类对象做形参, 所以

A的拷贝构造函数的形参又开始调用自己的拷贝构造函数通过B来构造自己, … 依次类推, 无限循环.

5.    拷贝赋值运算符.   P443

拷贝赋值运算符就是用一个类对象给另一个类对象赋值时的”=”运算符(不是初始化时用的”=”哦).

6.    一个具有指针类型成员的类对象使用合成的拷贝赋值运算符.

可以看到a和b对象的指针成员指向了相同的地址, 那么当它们析构时, 后一个析构的对象将delete一个无效的指针.

7.    使用=default. P449

使用=default可以显示要求编译器为我们合成拷贝控制成员.

8.    阻止拷贝. P449

可以通过在拷贝构造函数和拷贝赋值运算符后面加=delete 来阻止拷贝. 为什么要阻止拷贝? 例如, iostream类阻止了拷贝, 以避免多个对象写入或读取相同的IO缓存.

9.    C++11之前,通过将类的拷贝构造函数和拷贝赋值运算符声明为private的且不定义来阻止对该类对象的拷贝(或拷贝初始化).就算是友元也不能拷贝该类型, 因为未定义.

10.  行为像值得类. P453

必须定义自己的: 默认构造函数,
拷贝构造函数,拷贝赋值运算符以及析构函数.

且拷贝赋值运算符必须能处理自赋值.

11.  定义行为像智能指针的类. P455

(下面代码中的A类的拷贝赋值运算符中的第一句 ++(*num)应改为++(*rhs.num))

12.  给值行为的类定义自己的swap函数. P458

13.  左值引用 和 右值引用: P471

左值持久,右值短暂.

变量是左值, 所以不能将一个右值引用绑定到变量上. 就算这个变量本身就是一个右值引用也不行.

14.  移动构造函数与移动赋值运算符. P473

移动构造函数的本质其实就是夺取一个对象的内存资源直接给被构造的对象,且原来对象不能继续控制本来的内存了.

15.  合成的移动操作     
P475

只有一个类没有定义任何拷贝控制成员(拷贝构造函数,拷贝赋值运算符,析构函数)时, 且类的所有非static成员都是可移动的,
此时编译器才会给该类合成移动构造函数和移动赋值运算符.

16.   当既有拷贝操作也有移动操作时,使用哪个? P477

一条原则:移动右值,拷贝左值.

即当右边是一个右值时, 就优先使用移动操作(也可以使用拷贝).

当右边是一个左值时, 只能使用拷贝操作(除非使用std::move将其转换)

注意上述get函数返回的结果是一个右值.
且如果我们定义get如下:

A get(A a)

{

return a;

}

那么在传递参数的时候a就会使用一次拷贝构造函数,使得a的值加上了”111”.

17.  如果没有移动操作, 只有拷贝操作, 那么右值也被拷贝. P477

即必要时候, 右值也能被拷贝构造函数和拷贝赋值运算符拷贝.

但是拷贝构造函数和拷贝赋值运算符的参数必须是const类型的引用才行, 如果不是就会出错(可以自己试试看看到底是什么情况).

18.   赋值运算符实现拷贝赋值和移动赋值两种功能P478

如果赋值运算符的形参是传值调用, 那么用实参初始化形参就需要调用拷贝构造函数(实参是左值)或移动构造函数(实参是右值). 那么可以用下面方式实现等价的拷贝赋值和移动赋值.

(注意:下面的程序=操作符内使用的是自定义的swap,
因为标准库的swap需要类支持=操作符,
但是=操作符我们还没定义
)

19.  右值和左值引用成员函数 P483

在类的成员函数后面加上& 或&& 可以限定该成员函数只能接受左值或右值的参数.

同样可以避免对右值对象使用=赋值等操作.

时间: 2024-11-05 12:11:22

C++ Primer学习总结 第13章 拷贝控制的相关文章

《C++Primer》12、13章

第12章 动态内存 12.1 智能指针 shared_ptr<T> make_shared<T>(args) 直接初始化 new int(10); 默认初始化 new int; 值初始化 new int(); 由内置指针(而不是智能指针)管理的动态内存在被显示释放前一直都会存在. 最好坚持只使用智能指针: delete之后重置指针值为nullptr: unique_ptr u = nullptr 释放u指向的对象,将u置为空 u.release()  u放弃对指针的控制权,返回指针

c++primer(第五版) 第十三章 拷贝控制习题答案

纯原创    转载请注明出处:http://blog.csdn.net/axuan_k 13.2    13.3   13.4    13.5 #include<iostream> using namespace std; class Point{ int a; }; Point global; //13.4 Point foo_bar(Point arg) //1 { Point local = arg, *heap = new Point(global); //2,3 *heap = lo

【C语言学习】《C Primer Plus》第13章 文件输入/输出

学习总结 1.文件函数原型1: FILE* fopen(char *filename, char *openmode);  //打开文件,返回文件指针 filename:文件名,更确切地说,是包含文件名的字符串地址: openmode:打开模式(r,w,a,r+,w+,a+,……) 打开失败返回NULL int getc(FILE *fp);  //读文件,返回字符 fp:读取文件指针 int putc(int ch, FILE *fp);  //写文件,返回写入字符的ASCII值 ch:字符

C++ Primer学习总结 第6章 函数

第6章 函数 1.    函数最外层作用域中的局部变量不能使用与函数形参一样的名字,因为它们属于同一个作用域范围. 2.    局部静态变量的生命周期: 在整个程序的执行路径第一次经过对象定义语句时初始化,并且直到整个程序终止时才被销毁,在此期间即使对象所在函数结束执行也不会对它有影响. 3.    如果重载的函数的参数只有顶层const区别,那么是错误的: 如果有底层const区别可以算作重载. 4.    如果函数的参数要使用引用(且不会改变引用对象的值),那么应该定义成常量引用. 因为使用

C++ Primer学习总结 第7章 类

第7章 类 1.    引入const成员函数(C++ Primer P231-232) C++类的常量对象是无法调用非const成员函数的,如果想让常量对象调用某个成员函数,必须声明成const: 2.     一个类的尾后const成员函数如果返回*this,那么其返回类型必然是const 类名 & 前面这个const是不能少的.否则无法通过编译. 3.    默认构造函数P235-236 编译器只有在发现类中不包含任何构造函数的情况下,才会为我们合成一个默认的构造函数,且该函数对于类中的成

C++ Primer学习总结 第10章 泛型算法

第10章 泛型算法 1.    find()泛型算法使用示例: 2.    只读算法accumulate:对所给范围的元素求和并返回. 注意accumulate的第3个参数决定着它的返回类型. 即如果第3个参数是double,就算迭代器里都是int,最终还是返回double类型的数. 3.    只读算法equal:比较前两个迭代器表示范围的所有元素是否与第3个迭代器表示的对应位置的元素都相同,如果相同返回true.两个容器类型可以不同,保存的元素类型也可以不同,只要元素之间可以比较即可.如st

C++ Primer学习总结 第12章 动态内存

第12章 动态内存 1.    申请并使用shared_ptr.  P400 2.    shared_ptr计数. P402 赋值, 拷贝, 向函数传递一个智能指针, 或函数返回一个智能指针都会增加当前智能指针的计数. 3.    不同对象间利用智能指针共享数据的例子. 4.    使用new来动态申请内存. P407 默认情况下,new申请的内存对象都是默认初始化的. 5.    new申请的const对象必须初始化. P408 如果申请内置类型, 那么必须用括号()初始化. 如果申请类类型

java JDK8 学习笔记——第13章 时间与日期

第十三章 时间与日期 13.1 认识时间与日期 13.1.1 时间的度量 1.格林威治标准时间GMT 格林威治标准时间的正午是太阳抵达天空最高点之时.现在已经不作为标准时间使用. 2.世界时UT世界时是借由观测远方星体跨过子午线而得,在引入UTC之前,GMT和UT是相同的. 3.国际原子时TAI 将秒的国际单位定义为铯原子辐射振动91926331770周耗费的时间,从UT的1958年开始同步. 4.世界协调时UTC 采用了闰秒修正,确保UTC与UT相差不会超过0.9秒,加入闰秒的时间通常会在6月

&lt;&lt;Python基础教程&gt;&gt;学习笔记 | 第13章 | 数据库支持

备注:这章内容相对介绍的比较简单,不过例子比较使用,主要是要掌握如果连接,使用数据库,并以SQLite做示例 ------ Python数据库API 为了解决Python中各种数据库模块间的兼容问题,现在已经通过了一个标准的DB API.目前的API版本(2.0)定义在PEP249中的Python Database API Specification v2.0中. 异常 为了尽可能准确地处理错误,API中定义了一些异常.它们被定义在一种层次结构中,所以可以通过一个except块捕捉多种异常. 连