C++ Primer 第五版 第十三章 拷贝控制

C++Primer第五版——习题答案+详解(完整版) C++Primer第五版——习题答案详解 看的也比较快,但是有很多东西确实用不上,所以很多都没有深入的探讨,但是知识点覆盖的很全面,每一道题涉及的知识点我都写上简单的解释了。 文中代码的中文注释也尽量都写上,自己皆编译通过。 水平有限,如有有误之处,希望大家不吝指教! 下面的链接是 第一章:全部题目(第一章基础较多)http:/ 阅读详情

当定义一个类时,我们显式地或隐式地指定在此类型的对象拷贝、移动、赋值和销毁时做什么。一个类通过定义五种特殊的成员函数来控制这些操作,包括:拷贝构造函数(copy constructor)、拷贝赋值运算符(copy-assignment operator)、移动构造函数(move constructor)、移动赋值运算符(move-assignment operator)和析构函数(destructor)。我们称这些操作为拷贝控制操作(copy control)。

一、拷贝、赋值与销毁
1. 拷贝构造函数

如果一个构造函数的第一个参数是自身类类型的引用,且任何额外参数都有默认值,则此构造函数是拷贝构造函数。

拷贝构造函数的第一个参数必须是一个引用类型。虽然我们可以定义一个接受非const引用的拷贝构造函数,但此参数几乎总是一个const的引用。

(如果其参数不是引用类型,则调用永远也不会成功——为了调用拷贝构造函数,我们必须拷贝它的实参,但为了拷贝实参,我们有需要调用拷贝构造函数,如此无限循环。)

合成拷贝构造函数

编译器会合成一个拷贝构造函数。合成的拷贝构造函数会将其参数的成员逐个拷贝到正在创建的对象中。编译器从给定对象中依次将每个非static成员拷贝到正在创建的对象中。

每个成员的类型决定了它如何拷贝:对类类型的成员,会使用其拷贝的构造函数来拷贝;内置类型的成员则直接拷贝。虽然我们不能直接拷贝一个数组,但合成拷贝构造函数会逐元素地拷贝一个数组类型的成员。

拷贝初始化

拷贝初始化通常使用拷贝构造函数来完成。如果一个类有一个移动构造函数,则拷贝初始化有时会使用移动构造函数而非拷贝构造函数来完成。

编译器可以绕过拷贝构造函数

2. 拷贝赋值运算符
重载赋值运算符

重载运算符本质上是函数,其名字由operator关键字后接要定义的运算符的符号组成。因此,赋值运算符就是一个名为operator=的函数。运算符函数也有一个返回类型和一个参数列表。

拷贝赋值运算符本身是一个重载的赋值运算符,定义为类的成员函数,左侧运算对象绑定到蕴含的this参数,而右侧的运算对象是所属类类型的,作为函数的参数。函数返回指向其左侧运算对象的引用。

当对类对象进行赋值时,会使用拷贝赋值运算符。

如果类未定义自己的拷贝赋值运算符,编译器会为它合成一个。通常情况下,合成的拷贝赋值运算符会将右侧对象的非static成员逐个赋予左侧对象的对应成员,这些赋值操作是由类型的拷贝赋值运算符完成的。

3. 析构函数

析构函数释放对象使用的资源,并销毁对象的非static数据成员。

析构函数是类的一个成员函数,名字由波浪号接类名构成。它没有返回值,也不接受参数,因此也不能被重载。对于一个给定类,只会有唯一一个析构函数。

在一个构造函数中,成员的初始化是在函数体执行之前完成的,且按照它们在类中出现的顺序进行初始化。在一个析构函数中,首先执行函数体,然后销毁成员。成员按初始化顺序的逆序销毁。

隐式合成的析构函数体为空,但这并不意味着它什么也不做,当空函数体执行完后,非静态数据成员会被逐个销毁。成员是在析构函数体之外隐含的析构阶段中被销毁的。析构部分是隐式的。销毁类类型的成员需要执行成员自己的析构函数。内置类型没有析构函数,因此销毁内置类型成员什么也不需要做。

销毁一个内置指针类型的成员不会delete它所指向的对象。与普通指针不同,智能指针是类类型,所以具有析构函数,智能指针成员在析构阶段会被自动销毁。

4. 三/五法则

有三个基本操作可以控制类的拷贝操作:拷贝构造函数、拷贝赋值运算符和析构函数。在新标准下,一个类还可以定义一个移动构造函数和一个移动赋值运算符。

需要析构函数的类也需要拷贝和赋值操作
需要拷贝操作的类也需要赋值操作,反之亦然
5.  使用=default

我们可以通过将拷贝控制成员定义为=default来显式地要求编译器生成合成的版本。

6. 阻止拷贝

定义删除的函数

在新标准下,我们可以通过将拷贝构造函数和拷贝赋值运算符定义为删除的函数(deleted function)来阻止拷贝。删除的函数是这样一种函数:我们虽然声明了它们,但不能以任何方式使用它们。在函数的参数列表后面加上=deleted来指出我们希望将它定义为删除的。

=delete通知编译器(以及我们代码的读者),我们不希望定义这些成员。

与=default不同,=delete必须出现在函数第一次声明的时候。我们可以对任何函数指定=delete(我们只能对编译器可以合成的默认构造函数或拷贝控制成员使用=default)。

析构函数不能是删除的成员
合成的拷贝控制成员可能是删除的

对某些类来说,编译器将这些合成的成员定义为删除的函数:

二、 拷贝控制和资源管理

通常,管理类外资源的类必须定义拷贝控制成员。这种类需要通过析构函数来释放对象所分配的资源。一旦一个类需要析构函数,那么它几乎肯定也需要一个拷贝构造函数和一个拷贝赋值运算符。

1. 行为像值的类

为了提供类值的行为,对于类管理的资源,每个对象都应该拥有一份自己的拷贝。

赋值运算符通常祝贺了析构函数和构造函数的操作。

2. 定义行为像指针的类

对于行为类似指针的类,我们需要为其定义拷贝构造函数和拷贝赋值运算符,来拷贝指针成员本身而不是它指向的string。我们的类仍需要自己的析构函数来释放接受string参数的构造函数分配的内存。在本例中,只有当最后一个指向string的HasPtr销毁时,析构函数才可以释放string。

引用计数

定义一个使用引用计数的类

三、 动态内存管理类
移动构造函数和std::move

一些标准库类,包括string,都定义了所谓的“移动构造函数”。移动构造函数通常是将资源从给定对象“移动”到正在创建的对象。标准库保证“移后源(moved-from)”string仍然保持一个有效的、可析构的状态。

move标准库函数定义在utility头文件中。①当realloctae在新内存中构造string时,它必须调用move来表示希望使用string的移动构造函数,如果它漏掉了move调用,将会使用string的拷贝构造函数。②我们通常不为move提供一个using声明。当我们使用move时,直接调用std::move而不是move。

四、对象移动

1. 右值引用

所谓右值引用就是必须绑定到右值的引用。我们通过&&而不是&来获得右值引用。

右值引用只能绑定到一个将要销毁的对象。因此,我们可以自由地将一个右值引用地资源“移动”到另一个对象中。

一般而言,一个左值表达式表示地是一个对象的身份,而一个右值表达式表示的是对象的值。

左值持久;右值短暂

左值有持久的状态,而右值要么是字面常量。要么是在表达式求值过程中创建的临时对象。

变量是左值

标准库move函数

move函数返回给定对象的右值引用。

调用move意味着:除了对rr1赋值或销毁它外,我们将不再使用它。

返回左值的表达式包括:返回左值引用的函数及赋值、下标、解引用和前置递增/递减运算符;

返回右值的包括:返回非引用类型的函数及算数、关系、位以及后置递增/递减运算符。(我们可以将一个const的左值引用或者一个右值引用绑定到这类表达式上。)

2. 移动构造函数和移动赋值运算符

移动构造函数的第一个参数是该类类型的一个右值引用,任何额外的参数都必须有默认实参。

除了完成资源移动,移动构造函数还必须确保移后源对象处于这样一个状态——销毁它是无害的。特别是,一旦资源完成移动,源对象必须不再指向被移动的资源——这些资源的所有权已经归属新创建的对象。

与拷贝构造函数不同,移动构造函数不分配任何新内存:它接管给定的StrVec中的内存。最终,移后源对象会被销毁,意味着将在其上运行析构函数。

移动操作通常不会抛出任何异常。除非标准库知道我们的移动构造函数不会抛出异常,否则它会认为移动我们的类对象时可能会抛出异常,并且为了处理这种可能性而做一些额外的工作。

一种通知标准库的方法是在我们的构造函数中指明noexpect。我们在一个函数的参数列表后指定noexpect。

我们必须在类头文件的声明中和定义中(如果定义在类外的话)都指定noexpect。

移动赋值运算符

移动赋值运算符执行与析构函数和移动构造函数相同的工作。与移动构造函数一样,如果我们的移动赋值运算符不抛出任何异常,我们就应该将它标记为noexpect。

移后源对象必须可析构

从一个对象移动数据并不会销毁此对象,但有时在移动操作完成后,源对象会被销毁。因此,当我们编写一个移动操作时,必须确保移后源对象进入一个可析构的状态(析构安全的状态)。

合成的移动操作

只有当一个类没有定于任何自己版本的拷贝控制成员(拷贝构造函数、拷贝赋值运算符或析构函数),且类的每个非static数据成员都可以移动时,编译器才会为它合成移动构造函数或移动赋值运算符。编译器可以移动内置类型的成员。如果一个成员时类类型。且该类有对应的移动操作,编译器也能移动这个成员。

移动右值,拷贝左值

但如果没有移动构造函数,右值也被拷贝。

移动迭代器

移动迭代器的解引用运算符生成一个右值引用。

我们通过调用标准库的make_move_iterator函数将一个普通迭代器转换为一个移动迭代器。此函数接受一个迭代器参数,返回一个移动迭代器。

移动迭代器支持正常的迭代器操作。

3. 右值引用和成员函数

右值和左值引用成员函数

重载和引用函数

引用限定符也可以区分重载版本。

C++ Primer第五版_第十三章习题答案(21~30) 假定希望定义 StrBlob 的类值版本,而且希望继续使用 shared_ptr,这样我们的 StrBlobPtr 类就仍能使用指向vector的 weak_ptr 了。你修改后的类将需要一个拷贝的构造函数和一个拷贝赋值运算符,但不需要析构函数。即,对于对象所指向的 string 成员,每个对象都有一份自己的拷贝。确定你理解了你的代码和我们的代码之间的差异。如果未定义拷贝构造函数,将会拷贝指针的值,指向同一个地址。给定下面的类,为其实现一个默认构造函数和必要的拷贝控制成员。合成的版本满足所有的需求。 阅读详情

相关推荐

C++Primer第五版 第十三章习题答案(31~40)

31: #include #include #include #include using namespace std; class Hasptr1 { friend void swap(Hasptr1&,Hasptr1&); friend bool operator<(const Hasptr1& s1,const Hasptr1& s2); friend void show(ve

MISAYAONE的博客 8538

C++标准库 -- 顺序容器 (Primer C++ 第五版 · 阅读笔记)

C++标准库 -- 顺序容器(Primer C++ 第五版 · 阅读笔记)

weixin_43412762的博客 1381

CSP历年真题201503-1-图像旋转(C/C++)

一、问题描述 旋转是图像处理的基本操作,在这个问题中,你需要将一个图像逆时针旋转90度。 计算机中的图像表示可以用一个矩阵来表示,为了旋转一个图像,只需要将对应的矩阵旋转即可。 输入格式 二、输入格式 输入的第一行包含两个整数n, m,分别表示图像矩阵的行数和列数。 接下来n行每行包含m个整数,表示输入的图像。 三、输出格式 输出m行,每行包含n个整数,表示原始矩阵逆时针旋转90度后的矩阵。 四、...

GTboy100的博客 1671

C++ Primer (第五版)-第一章 -初识C++开始

学习笔记

jiyanghao19的博客 2015

声纹识别实战:用Python从零搭建一个简易声纹验证系统(附完整代码)

本文是一份详细的声纹识别实战指南,教你使用Python从零搭建一个简易的声纹验证系统。文章涵盖了从音频采集、预处理、MFCC特征提取,到基于高斯混合模型(GMM)的建模与验证全流程,并提供了完整的代码实现,帮助开发者快速掌握这项生物特征识别核心技术。

ik6789012的博客 235

零基础用AHK操作Excel:从数据读取到自动填写(附完整脚本解析)

本文为零基础用户详细介绍了如何使用AutoHotkey(AHK)通过COM接口高效操作Excel,实现从数据自动读取到填写的完整自动化流程。文章包含完整的脚本解析、环境搭建指南、核心代码逐行讲解以及实际应用案例,帮助办公人员大幅提升数据处理效率,告别重复劳动。

weixin_29202213的博客 557

C++ Primer 第五版

C++ Primer 第五版 学习笔记

laiyuhua120的博客 1112

①Modbus TCP转Modbus RTU/ASCII网关同步采集无需编程高速轻松组网

MS-A1-50X1系列作为MODBUS TCP通信的服务器进行动作。可通过MODBUS TCP 通信,将 MS-A1-50X1 系列产品通过 RS485 采集的仪器仪表之类的值作为通信数 据输出到 PLC,上位机等。

2401_89571741的博客 1399

C++ primer(第五版)- 1

C++

liangfei868的博客 1294

SPMI总线协议(一)

系统电源管理接口( System Power Management Interface简称SPMI)是一种双线串行接口,用于连接片上系统(SoC)处理器系统的集成电源控制器(PC)与一个或多个电源管理集成电路(PMIC)电压调节系统。SPMI 使系统能够使用单个 SPMI 总线动态调整 SoC 内部电压域的电源和衬底偏置电压。移动设备和其他便携式设计正日益采用系SPMI)协议,实现多主多从系统,根据实时系统需求监测和动态控制供电电压。设计人员可以在系统级实现更低的功率。

qflook的博客 890

小秋SLAM入门实战ubuntu所有文章汇总

ubuntu 终端如何停止快速打印的输出以及恢复命令 ubuntu 终端如何快速打开当前路径下的图形化窗口界面? killall -9用途用法 ps -xu | grep这个命令的用途 linux 下 top命令显示的是内存是堆内存吗? zip分割和合并 关闭除当前 GNOME 终端窗口以外的所有其他 GNOME 终端窗口 如何在安装系统后设置短密码 【如何使用getrusage获取消耗的CPU时间和进程使用的最大物理内存】 【webots安装使用教程】 Linux系统下查看当前运行的进程和线程的几种方法

小秋 AI SLAM 入门实战教程 1406

《C++Primer 第五版》——第十三章 拷贝控制

《C++Primer 第五版》——第十三章 拷贝控制

KCDCY的博客 854

STM32F103RCT6的FLASH读写操作

STM32F103RCT6是一款常用的32位微控制器,它具有内置的FLASH存储器,可以用于存储程序代码和数据。在本文中,我们将详细介绍如何进行STM32F103RCT6的FLASH读写操作,并提供相应的源代码。写入数据时,我们通过将数据直接写入FLASH地址来完成。通过使用适当的API和规则,我们可以有效地读取和写入数据。写入FLASH时,我们需要按特定的规则进行编程。首先,我们需要包含适当的头文件和定义相关的宏和变量,以便在代码中使用FLASH API。类型的指针,我们可以直接读取FLASH中的数据。

UplKubernetes的博客 1034

C++ Primer (第五版)-第十三章 拷贝控制

## 析构函数不能是删除成员public://默认构造函数//拷贝构造函数//添加都移动构造函数P.i = 0;// 析构函数~HasPtr()delete ps;int main()//拷贝语义//移动语义所有五个拷贝控制成员应该看作一个整体:一般来说,如果一个类定义了任何一个拷贝操作,它应该定义所有五个操作。如前所述,某些类必须定义拷贝构造函数、拷贝赋值运算符和析构函数。这些类只有一个资源,而拷贝成员必须拷贝此资源。

jiyanghao19的博客 1083

基于STM32、HAL库的IP5356M_DBZ_CA快充协议芯片简介及驱动程序设计

IP5356M_BATTERY_LEVEL_25 = 1, /* 电池电量25% */IP5356M_CHARGE_STATE_CHARGE_DONE = 4, /* 充电完成 */IP5356M_CHARGE_STATE_FAST = 2, /* 快速充电 */IP5356M_CHARGE_STATE_NONE = 0, /* 无充电 */IP5356M_CHARGE_STATE_PRE = 1, /* 预充电 *//* 延时1秒 */

corlin6688的博客 1419

华为的C语言编程规范.pdf

华为技术有限公司内部技术规范。本规范制定了编写C语言程序的基本原则、规则和建议。从代码的清晰、简洁、可测试、安全、程序效率、可移植各个方面对C语言编程作出了具体指导。

C++ primer 第五版个人笔记 第十三章 拷贝控制

拷贝控制在一个类里定义了五种成员函数来控制这些操作,除了本科教材里提到的拷贝构造函数、析构函数,还有拷贝赋值运算符,移动构造函数,移动赋值运算符;如果一个类没有定义所有这些拷贝控制成员,编译器会自动为它定义缺失的操作,但是对于一些类来说,默认定义会导致灾难,因此要明白什么时候需要定义这些操作; 13.1 拷贝、赋值与销毁 拷贝构造函数的第一个参数必须是引用类型,它在某些情况下会被隐式地使用,因此拷贝构造函数通常不应该是explicit的; 拷贝初始化不仅在使用=定义变量时会发生,还有三种情况,(本科.

Mint2yx4的博客 383

pscad IEEE标准系统搭建说明

pscad IEEE标准系统搭建说明,包含典型的IEEE 9节点、14节点、30节点、39节点和118节点系统。内含标准测试系统的原始数据和潮流结果。

C++Primer(第五版 )第十三章 拷贝控制 章节编程练习答案

13.1拷贝构造函数是什么?什么时候使用它? 答:

是鲤鱼啊 4274

OV9734 DATASHEET,完整规格书

OV9734完整规格书,包括电气参数,尺寸规格,寄存器配置表等

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

纯原创    转载请注明出处:http://blog.youkuaiyun.com/axuan_k 13.2    13.3   13.4    13.5 #include using namespace std; class Point{ int a; }; Point global; //13.4 Point foo_bar(Point arg) //1

旧时光 4224

C++ Primer(第五版)|练习题答案与解析(第十三章:拷贝控制)

C++ Primer(第五版)|练习题答案与解析(第十三章:拷贝控制) 本博客主要记录C++ Primer(第五版)中的练习题答案与解析。 参考:C++ Primer C++ Primer C++ Primer 练习题13.1 拷贝构造函数是什么?什么时候使用它? P440。如果一个构造函数的第一个参数是自身类型的引用,且任何额外参数都有默认值,则此构造函数是拷贝构造函数。 P441。 使用...

qq_24739717的博客 1233

C++primer(第五版)第十三章(拷贝控制)

一个类通过定义五种特殊的成员函数来控制这些操作:拷贝构造函数拷贝赋值运算符移动构造函数移动赋值运算符析构函数。

m0_63235356的博客 270

C++Primer(第五版 )第十三章 拷贝控制 章节编程练习

练习13.1 拷贝构造函数是什么?什么 时候使用它? 拷贝构造函数第一个参数是一个类类型的引用,其他的参数都有默认值。 当拷贝初始化发生时,该拷贝初始化需要拷贝构造函数或移动构造函数。 拷贝初始化当使用=时会发生。 将一个对象作为实参 ...

qq_44741516的博客 1244

C++primer第五版笔记-第十三章拷贝控制

1、拷贝、赋值与销毁 2、拷贝控制与资源管理 3、交换操作 4、拷贝控制示例 5、动态内存管理类 6、对象移动

liugallup的专栏 527

《C++primer(第五版)》学习之路-第十三章:拷贝控制

【 声明:版权所有,转载请标明出处,请勿用于商业用途。  联系信箱:libin493073668@sina.com】 13.1 拷贝、赋值与销毁 1.当定义一个类时,我们显式地或隐式地指定在此类型的对象拷贝、移动、赋值和销毁时做什么。一个类通过定义五种特殊的成员函数来控制这些操作,包括:拷贝构造函数,拷贝赋值运算符,移动构造函数,移动赋值运算符和析构函数。 2.在一个

月下的寂寞肖邦 2537

C++Primer第五版 习题答案(部分) 第十三章 拷贝控制

13.4 Point global; Point foo_bar(Point arg) //1 函数参数 { Point local = arg, *heap = new Point(global); //2,3 赋值初始化 *heap = local; Point pa[ 4 ] = { local, *heap }; //4,5 列表初始化 return *heap; //6 返回值 } 13.5 class HasPtr { public: HasPtr(cons

qq_24898229的博客 974

C++Primer第五版 习题答案 第十三章 拷贝控制(Copy Control)

13.1 如果一个构造函数的第一个参数是自身类类型的引用,且任何额外的参数都有默认值,则次构造函数是拷贝构造函数。 一个对象作为函数参数,以值传递的方式传入函数体; 一个对象作为函数返回值,以值传递的方式从函数返回; 一个对象用于给另外一个对象进行初始化(常称为赋值初始化); 用花括号列表初始化一个数组中的元素或一个聚合类成员。 13.2 永远也不会调用成功,为了调用拷贝构造函数,我们需要拷贝...

shamozhizhoutx的博客 5359

c++ primer(第五版)笔记 第十三章(1) 拷贝控制

// 类的拷贝控制操作 // 拷贝构造函数 copy constructor // 如果一个构造函数,第一个参数是自身类型的引用,且任何额外参数都有默认值,如果是非引用类型,则会调用拷贝构造函数,无限循环。。。 // 通常拷贝构造函数 ,不应该是一个 explicit的,好多时候需要类型转换 // 如果没有定义拷贝构造函数,编译器会合成一个拷贝构造函数 // 通常第一个参数是

cook A code 424
上一篇: C++ Primer 第五版 第12章 动态内存
下一篇: CUDA C变成权威指南 第二章 CUDA编程模型
青禾子的夏
博客等级 码龄8年 405粉丝 68原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值