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

news2024/11/18 15:39:30

一个类通过定义五种特殊的成员函数来控制这些操作:

拷贝构造函数

拷贝赋值运算符

移动构造函数

移动赋值运算符

析构函数

13.1拷贝,赋值与销毁

13.1.1拷贝构造函数

如果一个构造函数的第一个参数是自身类型的引用(必须是引用类型!),并且没有其他参数或是其他参数都有默认值(即只需要传入一个自身类型的实参即可的构造函数)那么称为拷贝构造函数.

如果我们没有定义,编译器会定义一个合成拷贝构造函数.

合成的拷贝构造函数会将参数的成员逐个拷贝当当前正在创建的对象.

拷贝初始化通常用拷贝构造函数来完成,但如果类中有移动拷贝函数,那么拷贝初始化有时会使用移动构造函数.(粗略来说,区别在于拷贝初始化函数的参数为左值引用&,移动拷贝函数的参数为右值引用&&,后面会再提到)

以std::string为例,以下初始化string即是拷贝初始化:

string s1="hello";
string s2=s1;
string s3=string(10,'w');

以下是直接初始化:

string s4("hello world");
string s5(s4);

这里再粗略来说,一般情况下用等号=来初始化的即为拷贝初始化.(不懂对不对,看书上给出的例子貌似是这样)

同时,不只是用=来定义变量时会发生拷贝初始化,还有以下几种情况会发生拷贝初始化:

将一个对象作为实惨传递给一个非引用类型的形参.

从一个返回类型为非引用类型的函数返回一个对象.

用花括号列表初始化一个数组中的元素或一个聚合类中的成员.(1 所有成员都是public的,2 没有定义任何构造函数,3 没有类内初始值, 4 没有基类没有虚函数 的类为聚合类)

 拷贝构造函数的参数必须是引用类型,是因为如果不是引用类型,你们调用拷贝构造函数,则必须拷贝它的实参,为了拷贝实参又必须调用拷贝构造函数……约等于没有终止条件的递归.

13.1.2拷贝赋值运算符

还是以std::string为例,简单看看拷贝赋值运算符是怎么个一回事:

string s1="hello";
string s2="world";
s1=s2;

没错,拷贝赋值运算符就是等号=.因此要定义拷贝赋值运算符,即要重载运算符:

class Text{
public:
    Text& operator= (const Text&);
};

返回值为本类的引用(通常来说,不排除有奇特的想法),函数名为operator加上要重载的符号(这里是=),然后参数列表里是本类的引用类型,因为不改变实参的值,因此加上const(非必要,但建议).

同样的,如果我们没定义拷贝赋值运算符,那么编译器会生成一个合成拷贝赋值运算法,默认是将右侧的运算对象的每个非static成员赋予左侧运算符的对应成员.例如:

class Student{
public:
    int age;
    string name;
    //等价于合成拷贝赋值运算符
    Student& operator= (const Student& s){
        age=s.age;        
        name=s.name;      
        return *this;    //返回此对象的引用
    }
};

13.1.3析构函数

析构函数名字由波浪号和类名构成,没有返回值,没有参数:

class Text{
public:
    Text(int a,int b){};    //构造函数
    ~Text(){};              //析构函数
}

和构造函数一样,没有返回值,函数名由类名构成.构造函数是类出生时(姑且这么说)执行,而析构函数是类死亡时(姑且这么说)执行.

没有参数因此不能被重载,一个类只能有一个析构函数,

和前面的一样,如果我们没有定义析构函数,那么编译器会生成合成构造函数.

析构函数体本身并不直接销毁成员,成员是在析构函数体之后隐含的析构阶段中被销毁的.在整个对象销毁过程中,析构函数体是作为成员销毁步骤之外的另一部分而进行的.

13.1.4三/五法则

如果一个类需要一个析构函数,那么一般也会需要拷贝构造函数和拷贝赋值运算符.

如果一个类需要一个拷贝构造函数,那么一般也会需要拷贝赋值运算符.

如果一个类定义了任何一个拷贝操作,那么应该定义所有的五个操作.

13.1.5使用=default

使用=default可以显示地要求编译器来生成的版本,合成的函数将隐式声明为内联的.

class Text{
public:
    Text() =default;                        //显式要求生成编译器生成合成版本的构造函数
    Text(const Text&) =default;             //显式要求生成编译器生成合成版本的拷贝构造函数
    Text& operator=(const Text&) =default;  //显式要求生成编译器生成合成版本的拷贝赋值运算符
    ~Text() =default;                       //显式要求生成编译器生成合成版本的析构函数
};

13.1.6阻止拷贝

把上面的default换成delete,那么则将对应的特殊函数定义为删除的函数,即即使定义了函数也不能使用它们.delete通知编译器我们不希望定义这些成员.

一般用于禁止拷贝.

析构函数不能是删除的成员!

本质上,当不可能拷贝,赋值,销毁类的成员时,类的合成拷贝控制成员就被定义为删除的.

13.2拷贝控制和资源管理

编写赋值运算符时,一个好的模式是先将右侧运算对象拷贝到一个局部临时对象中.拷贝完成后,销毁左侧运算对象的现有成员就是安全的了.简单来说就是销毁左侧运算对象资源之前拷贝右侧运算对象.

//这里我们假设Text类的成员name是一个字符串指针
Text& operator=(const Text& r){
    auto newName = new string(*r.name);    //先拷贝右侧运算对象.
    delete name;         //销毁左侧对象资源.
    name=newName;        //将右侧对象数据拷贝到本对象.
    return *this;        //返回本对象.
}

13.3交换操作

除了开头说的五种拷贝控制成员,管理资源的类通常还定义一个名为swap的函数(重载标准库里的swap)

//假设我们Text类有两个成员变量age和name,其中name是字符串指针
void swap(Text& left,Text& right){
    using std::swap;                
    swap(left.name,right.name);
    swap(left.age,left.age);
}

13.4拷贝控制示例

以下直接贴出书中的例子了.

 根据Message类的设计编写:

 save和remove成员

 拷贝控制成员

 析构函数

 拷贝赋值运算符

 swap函数

 13.5动态内存管理类

本小节省略.不是因为我看不懂哈

13.6对象移动

标准库容器,string(string虽然操作和标准库容器很接近但不属于容器),shared_ptr类既支持移动也支持拷贝.IO类和unqieu_ptr类可以移动但不能拷贝.

13.6.1右值引用

简单来说,一个&获取的是左值引用,两个&&获取的是右值引用.

左值持久,右值短暂.

右值引用指向将要被销毁的对象.因此我们可以从绑定到右值引用的对象"窃取"状态.(书中原话,没有很理解.这一整章的内容我都迷迷糊糊的.)

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

移动构造函数和拷贝构造函数基本一致,但拷贝构造函数要的参数是左值引用,而移动构造函数要的参数是右值引用(移动赋值运算符同理).并且在参数列表后加上noexcept关键字,它将会通知标准库我们的构造函数不抛出任何异常.

Text(Text&& t) noexcept{

}

不抛出异常的移动构造函数和移动赋值运算符必须标记为noexcept.

只要当一个类没有定义任何自己版本的拷贝控制成员,且所有数据成员都可以移动构造或是移动赋值时,编译器才会为它合成移动构造函数或移动赋值运算符.简而言之,移动右值,拷贝左值,但如果没有移动构造函数,右值也被拷贝.

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/729908.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

基于matlab检测使用校准的立体摄像头拍摄的视频中的人物并确定其与摄像头的距离(附源码)

一、前言 此示例演示如何检测使用校准的立体摄像头拍摄的视频中的人物,并确定他们与摄像头的距离。 二、加载立体摄像机的参数 加载对象,这是使用应用程序或函数校准相机的结果。 三、创建视频文件阅读器和视频播放器 创建用于读取和显示视频的系统对…

LabVIEW开发X射线图像增强

LabVIEW开发X射线图像增强 X射线图像在临床诊断中起着重要作用。但是,由于各种原因,例如不均匀,低照度条件和一些噪点,图像质量通常不是很好。因此有必要增强这些图像,以方便后续处理或诊断。模糊集论是开发图像处理中…

C# Color的名称对应该工具

工具效果如下图: 代码: namespace WinFormsColor {public partial class Form1 : Form{public Form1(){InitializeComponent();}private void displayColor(){Control listControl this.Controls["listView_Color"];if (listControl ! null)…

mysql的一些练习题

1. 第1题 mysql> create database Market charset utf8; Query OK, 1 row affected (0.01 sec)第二题 mysql> use Market Database changed mysql> mysql> create table customers(-> c_num int(11) primary key auto_increment,-> c_name varchar(50),-&…

【自动化测试】

一、自动化测试定义 首先来说一下什么是软件测试? 软件测试简单来说就是在规定的条件下对程序进行操作以发现程序错误,衡量软件质量,并对其是否能满足设计要求进行评估的过程。 那么什么是自动化测试呢? 自动化测试是把以人为驱动的测试…

使用Jxbrowser7替换浏览器请求内容和header

前几天一个伙计让我帮他做jxbrowser的请求内容(requestBody,Header)的替换,特此记录。 通过官网文档查阅,我们可以通过BeforeSendUploadDataCallback和BeforeStartTransactionCallback来实现body和header的替换

MySQL之DQL(涵盖所有查询!!!)

文章目录 前言一、基础查询1.1 语法1.2 实例 二、条件查询2.1 语法2.2 条件查询实例2.3 模糊查询实例 三 、排序查询3.1 语法3.2 实例 四 、聚合函数4.1 概念4.2 聚合函数分类4.3 聚合函数语法4.4 实例 五、分组查询5.1 语法5.2 实例 六、分页查询6.1 语法6.2 实例 前言 在上一…

Docker 是什么,在 Ubuntu 虚拟机上安装(部署)Docker

本文目录 1. Docker 简介1.1 什么是 Docker1.2 Docker 与虚拟技术的区别和联系1.3 为什么要用 Docker 2. 几个基本概念2.1 镜像2.1.1 分层存储 2.2 容器2.3 仓库2.3.1 Docker Registry 公开服务2.3.2 私有 Docker Registry 3. Docker 安装3.1 卸载旧版本 Docker3.2 更新及安装工…

Visual Studio 2017下的C++开发环境搭建

Visual Studio 是Microsoft旗下的开发工具包系列产品,是一个基本完整的开发工具集,它包括整个软件生命周期中所需要的大部分工具,如UML工具、代码管控工具、集成开发环境(IDE)等等,是最流行的Windows平台应用程序的集成开发环境。…

动态规划之63 不同路径 II(第5道)

题目: 一个机器人位于一个 m x n 网格的左上角 (起始点在下图中标记为 “Start” )。 机器人每次只能向下或者向右移动一步。机器人试图达到网格的右下角(在下图中标记为 “Finish”)。 现在考虑网格中有障碍物。那…

ROS-Moveit和Gazebo联合仿真

文章目录 URDF功能包配置configlaunchCMakeLists.txtpackage.xmlurdf文件 Moveit功能包配置configlaunch 运行 URDF功能包配置 config 首先在SW2URDF生成的功能包下Config目录下新建文件joint_trajectory_controller.yaml robot_arm_controller:type: "position_contro…

Tomcat使用数据库连接池数据库

1.连接池技术的作用 数据池允许应用程序重复使用一个现有的数据库连接,而不是重新建立一个。这项技术能明显地提高对数据库操作的性能。 2.什么是连接池技术 数据库连接池在初始化时将创建一定数量的数据库连接,具体数量的由连接池的最小数据库连接数来设…

什么是Uniswap v3?

目录 1. 集中化流动性:2. 集中化资金池管理:3. 多个流动性池:4. 高级订单类型:5. 协议费用优化: Uniswap v3 是去中心化交易所 Uniswap 的第三个主要版本,它于2021年5月发布。相比于 Uniswap v2&#xff0c…

内嵌tomcat使用方式(读取web.xml)

pom <?xml version"1.0" encoding"UTF-8"?> <project xmlns"http://maven.apache.org/POM/4.0.0"xmlns:xsi"http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation"http://maven.apache.org/POM/4.0.0 http…

微信小程序之单选题按钮切换背景

先来效果图 未选效果 已选效果 上代码 <view class"questionClass">问题1&#xff1a;夜晚落地西安咯&#xff0c;你最想先去哪里看看呢&#xff1f;</view><view class"single"><ul class"box"><li v-for"(s,i…

Python获取豆丁文档数据内容, 保存word文档

前言 嗨喽&#xff0c;大家好呀~这里是爱看美女的茜茜呐 开发环境: python 3.8 pycharm 模块使用: requests --> pip install requests re base64 docx --> pip install python-docx 第三方模块安装方法&#xff1a; win R 输入cmd 输入安装命令 pip install …

第N4周:使用Word2vec实现文本分类

目录 二、数据预处理1.加载数据2.构建词典3.生成数据批次和迭代器 二、模型构建1.搭建模型2.初始化模型3.定义训练与评估函数 三、训练模型1.拆分数据集并运行模型2.测试指定数据 &#x1f368; 本文为&#x1f517;365天深度学习训练营 中的学习记录博客&#x1f356; 原作者&…

原子操作CAS

CAS 悲观锁 具有强烈的独占和排他特性。在有悲观锁的情况下&#xff0c;对数据进行处理&#xff0c;数据会处于锁定状态。前面讲到的synchronized同一时间只允许一个线程访问某块资源&#xff0c;其他线程处于阻塞状态&#xff0c;就是一个独占锁&#xff0c;是悲观锁中的一种…

语义分割大模型SAM论文阅读(二)

论文链接 Segment Anything 开源代码链接 SAM 论文阅读 摘要 We introduce the Segment Anything (SA) project: a new task, model, and dataset for image segmentation. Using our efficient model in a data collection loop, we built the largest segmentation dat…

Presto启动报错:No such file or directory

1. 问题描述 1.1 无法通过sudo -u presto启动 使用低版本的Presto时&#xff0c;在root用户下&#xff0c;通过如下命令切换到presto用户、启动Presto服务&#xff1a; sudo -u presto /install_dir/bin/launcher start # 或直接restart&#xff0c;包含stop和start操作 sudo …