拷贝控制操作,有5个特殊成员函数copy ctor,copy =opt,move ctor,move =opt,dtor
有哪些地方会用到
拷贝初始化
除了=定义变量时
参数传递和函数返回时
花括号列表初始化一个数组中元素或一个聚合类中成员
某些类对所分配的对象使用拷贝初始化,如insert和push;相对地,emplace直接初始化
成员是在析构函数体之后隐含的析构阶段中被销毁的
1一个类需要自定义析构函数,几乎可以肯定它也需要自定义拷贝构造和拷贝赋值。
2拷贝构造和拷贝赋值有一个,几乎可以肯定另一个也需要,但是不一定需要析构函数。
- 某些类需要阻止拷贝
如iostream类,避免多个对象写入或读取相同的IO缓冲。
定义为删除的函数来阻止拷贝。
struct NoCopy{
NoCopy() = default; //合成的默认构造函数
NoCopy(const NoCopy&) = delete; //阻止拷贝
NoCopy &operator=(const NoCopy&) = delete; //阻止赋值
~NoCopy() = default; //合成版本
//其他
}
=delete必须出现在第一次声明的时候
=delete可以对任意函数指定,但不能删除析构函数
如果一个类有数据成员不能默认构造、拷贝、赋值或销毁,则对应的成员函数将被定义为删除的。
希望阻止拷贝应该用=delete定义,而不应该声明为private,成员函数和友元函数仍然可以进行拷贝。
管理类外资源的类,通常必须定义拷贝控制成员。首先需要确定此类型对象的拷贝语义,一般有两种选择,使类看起来像一个值或者一个指针。
-
两种类
- 像值的类:需要深拷贝,定义copy ctor、copy =opt(自赋值)和析构
- 像指针类:使用shared_ptr来管理资源,使用引用计数,拷贝时修改计数,析构时判断
定义类自己的swap
定义类自己的swap是一种优化手段,应该调用swap而不是std::swap。如果存在类型特定的swap版本,匹配程度会优于std版本。
定义了swap的类通常用swap来定义赋值运算符
HasPtr& HasPtr::operator=(HasPtr ths)
{
swap(*this, ths);//ths现在指向本对象曾经使用的内存,不能交换指针局部变量会被销毁
return *this;//rhs被销毁
}
该方法还自动处理了自赋值情况且是天然异常安全的。
在改变左侧运算对象之前拷贝右侧运算对象
唯一可能抛出异常的是拷贝构造函数中new表达式,真出现也会在改变左侧之前就发生。
- 引用与move
左值引用,不能绑定到要求转换的表达式、字面值常量、返回右值的表达式。
const左值引用或者右值引用可以绑定到右值上。
区别:左值有持久的状态,右值要么是字面常量,要么是在表达式求值过程中创建的临时对象。
所以右值引用的对象,将要被销毁,且没有其他用户。这就意味着右值引用的代码可以*地接管所引用对象的资源,“窃取”其状态。
我们可以显式地将一个左值转换为对应的右值引用类型。还可以通过move来获得绑定到左值的右值引用。<utility>
int &&rr3=std::move(rr1);
move告诉编译器,我们有一个左值,但希望像一个右值一样处理它。调用move意味着除了对rr1赋值和销毁外,不能使用它的值。
移动一个对象数据并不会销毁此对象,但有时移动完成后源对象会销毁。所以编写移动操作时,必须确保源对象进入可析构的安全状态,还必须保证对象仍然是有效的(可以安全地赋予新值或者可以安全地使用而不依赖其当前值)。我们的程序不应该依赖于移后源对象的数据。
当编写一个不抛出异常的移动操作的时候,必须在声明和定义中都指定noexcept。
1虽然移动操作通常不抛出异常,但抛出异常是允许的
2标准库容器能对异常发生时自身的行为提供保证。
除非知道移动不会抛出异常,否则必须用拷贝。同样需要检测自赋值,不能再使用右侧资源前就释放左侧资源。
参数是左值,移动版本赋值不可行,不能隐式地将一个右值引用绑定到一个左值。
参数是右值,两个版本都可以,拷贝=需要一次const转换,移动=则是精确匹配。
所有5个拷贝控制成员应该看做一个整体。
- 移动迭代器
make_move_iterator,解引用后生成一个右值引用。
标准库不保证哪些算法适用于移动迭代器。只有在确信算法在为一个元素赋值或者将其传递给一个用户定义的函数后不再访问它时,才能将移动迭代器传递给算法。
引用限定符& &&,类似const限定符只能用于非static成员函数,且必须同时出现在声明和定义中。const在前,&在后。
对象是一个右值,意味着没有其他用户,因此可以改变对象。
C/C++基础----拷贝控制的更多相关文章
-
C++ 拷贝控制和资源管理,智能指针的简单实现
C++ 关于拷贝控制和资源管理部分的笔记,并且介绍了部分C++ 智能指针的概念,然后实现了一个基于引用计数的智能指针.关于C++智能指针部分,后面会有专门的研究. 通常,管理类外资源的类必须定义拷贝控 ...
-
C++ Primer : 第十三章 : 拷贝控制之对象移动
右值引用 所谓的右值引用就是必须将引用绑定到右值的引用,我们通过&&来绑定到右值而不是&, 右值引用只能绑定到即将销毁的对象.右值引用也是引用,因此右值引用也只不过是对象的别名 ...
-
C++ Primer : 第十三章 : 拷贝控制之拷贝控制和资源管理
定义行为像值的类 行为像值的类,例如标准库容器和std::string这样的类一样,类似这样的类我们可以简单的实现一个这样的类HasPtr. 在实现之前,我们需要: 定义一个拷贝构造函数,完成stri ...
-
C++的那些事:类的拷贝控制
1,什么是类的拷贝控制 当我们定义一个类的时候,为了让我们定义的类类型像内置类型(char,int,double等)一样好用,我们通常需要考下面几件事: Q1:用这个类的对象去初始化另一个同类型的对象 ...
-
C++ Primer : 第十三章 : 拷贝控制之拷贝、赋值与销毁
拷贝构造函数 一个构造函数的第一个参数是自身类类型的引用,额外的参数(如果有)都有默认值,那么这个构造函数是拷贝构造函数.拷贝构造函数的第一个参数必须是一个引用类型. 合成的拷贝构造函数 在我们没 ...
-
Chapter13:拷贝控制
拷贝控制操作:拷贝构造函数.拷贝赋值运算符.移动构造函数.移动赋值运算符.析构函数. 实现拷贝控制操作的最困难的地方是首先认识到什么时候需要定义这些操作. 拷贝构造函数: 如果一个构造函数的第一个参数 ...
-
c/c++ 拷贝控制 构造函数的问题
拷贝控制 构造函数的问题 问题1:下面①处的代码注释掉后,就编译不过,为什么??? 问题2:但是把②处的也注释掉后,编译就过了,为什么??? 编译错误: 001.cpp: In copy constr ...
-
c/c++ 拷贝控制 右值与const引用
拷贝控制 右值与const引用 背景:当一个函数的返回值是自定义类型时,调用侧用什么类型接收?? 1,如果自定义类型的拷贝构造函数的参数用const修饰了:可以用下面的方式接收. Test t2 = ...
-
C++ Primer 笔记——拷贝控制
1.如果构造函数的第一个参数是自身类类型的引用,且任何额外参数都有默认值,则此构造函数是拷贝构造函数.拷贝构造函数的第一个参数必须是引用类型(否则会无限循环的调用拷贝构造函数). 2.如果没有为一个类 ...
随机推荐
-
【Python网络爬虫一】爬虫原理和URL基本构成
1.爬虫定义 网络爬虫,即Web Spider,是一个很形象的名字.把互联网比喻成一个蜘蛛网,那么Spider就是在网上爬来爬去的蜘蛛.网络蜘蛛是通过网页的链接地址来寻找网页的.从网站某一个页面(通常 ...
-
SVG 2D入门8 - 文档结构
前面介绍了很多的基本元素,包括结构相关的组合和重用元素,这里先对SVG的文档结构中剩下的相关元素简单总结一下,然后继续向前领略SVG的其他特性. SVG文档的元素基本可以分为以下几类: 动画元素:an ...
-
BZOJ 1033: [ZJOI2008]杀蚂蚁antbuster(模拟)
坑爹的模拟题QAQ DEBUG多了1kb QAQ 按题意做就行了 注意理解题意啊啊啊啊 尼玛输出忘换行wa了3次QAQ CODE: #include<cstdio>#include< ...
-
【原创】数据库基础之Mysql(3)mysql删除历史binlog
mysql开启binlog后会在/var/lib/mysql下创建binlog文件,如果手工删除,则下次mysql启动会报错: mysqld: File './master-bin.000001' n ...
-
Swift开发实例:苹果Swift编程语言新手教程中文版+FlappyBird,2048游戏源代码
源代码: 用IOS Swift语言实现的Flappy Bird源代码:http://download.csdn.net/detail/estellise/7449547 用IOS Swift实现的游戏 ...
-
BottomNavigationBar 底部导航控件
BottomNavigationBar 底部导航控件 属性 说明BottomNavigationBarItem 多个 item,iconSize icon大小currentIndex 默认选中第几个o ...
-
Golang获得执行文件的当前路径
运行环境:golang1.9.2+win7x64golang1.9.2+centos6.5×64 /*获取当前文件执行的路径*/ func GetCurPath() string { file, _ ...
-
C/C++中的常量到底存在了什么地方
一般来说,基本类型(整型.字符型等)常量会在编译阶段被编译成立即数,占的是代码段的内存.(代码段是只读的,而且不允程序员获取代码段的地址,所以在c++中,尽量不为const分配数据段的内存,但是一旦取 ...
-
【POJ】3270.Cow Sorting
题解 用到一点群论的知识! 我们发现把操作写成一个置换后,一定是单个置换圈的内进行操作,把置换圈进行扩大的操作不优 我们有两个办法,一个是用全局最小的换进来,代替这个圈里最小的值,交换操作完成后再换出 ...
-
HTML标签 闭合还是不闭合?
你在写 HTML5 代码的时候,是否纠结过应该写 <br /> 还是 <br>,是写 <input /> 还是写 <input>.写 <scrip ...