13-拷贝控制

13.1 拷贝,赋值与销毁

以上这些操作,必须明白定义与不定义会对类的操作产生何种影响,变编译器定义的合成版本未必符合类设计的初衷。

13.1.1 拷贝构造函数

如果一个构造函数的第一个参数是同类型的引用,且任何其他参数都有默认值,则此构造函数为拷贝构造函数。

必须是引用类型参数,因为在调用非引用参数的函数时,会拷贝实参,而拷贝实参又需要调用拷贝构造函数,那么会无休止的调用下去。

Foo(const Foo&);

合成拷贝构造函数会将其参数的成员(非static)逐个拷贝到正在创建的对象中。

直接初始化:使用普通的函数匹配,选择参数最匹配的构造函数。

拷贝初始化:将对象或者可以转换为相同类型的对象拷贝到正在创建的对象中。

1,使用=运算符定义变量

2,将对象作为实参传递给一个非引用类型

3,从非引用类型返回类型的函数里返回一个对象

4,使用初始值列表初始化一个数组中的元素或一个聚类中的成员

13.1.2 拷贝赋值运算符

Foo& operator=(const Foo&);

如果运算符是一个成员函数,其左侧对象就绑定到隐式的this参数。

合成拷贝赋值运算符:将右侧运算对象的每个成员(非static)赋予左侧运算对象的对应成员

13.1.3 析构函数

~Foo();

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

当一个对象被销毁时会自动调用其析构函数。当指向一个对象的引用会指针离开作用域是,析构函数不会执行。

合成析构函数:空的函数体

13.1.4 三/五法则

如果一个类需要自定义析构函数(一般是销毁动态分配的内存),则可能也需要拷贝构造函数和拷贝赋值运算符。

需要拷贝操作的类也需要赋值操作,反之亦然,但未必需要析构函数。

13.1.5 使用=default

使用=default修饰拷贝控制成员,编译器将生成相应成员的合成版本。=default在类内则隐式的声明为内联。

Foo& operator=(const Foo&) = default;

Foo(const Foo&) =default;

只能用来修饰具有合成版本的成员函数。

13.1.6 阻止拷贝

将拷贝构造函数和拷贝赋值运算符定义为删除的函数来阻止拷贝,即,虽然声明了,但是不可以使用它们。

Foo(const Foo&) = delete;//阻止拷贝

Foo& operator=(const Foo&) = delete;//阻止赋值

=delete必须出现在第一次声明的时候;可以对任何函数指定=delete

对于删除了析构函数的类型,不可以定义此类型的变量或成员,但可以动态分配这种类型,但是无法释放它们。

合成的拷贝控制成员可能是删除的

如果类有数据成员不能被默认构造,拷贝,赋值,复制或销毁(delete或者private修饰),则此类对应的合成成员函数被定义为删除的。(引用成员或无法默认构造的const成员)

通过声明但不定义private的拷贝构造函数或拷贝赋值运算符,试图拷贝或赋值的操作在编译阶段被标记为错误;成员函数或友元函数中的拷贝或赋值会导致链接错误。(旧的方法)

13.2 拷贝控制和资源管理

管理类外的类必须定义拷贝控制成员。

13.2.1 行为像值的类

对于类管理的资源,每个对象都有一份拷贝。

赋值操作会销毁左侧运算对象的资源,之后从右侧运算对象拷贝数据,必须确保自赋值是异常安全的(可先将右侧运算对象的数据拷贝到零时对象中)。

13.2.2 行为像指针的类

多个此类的对象共享同一份数据(使用智能指针或引用计数管理)

13.3 交换操作

void swap(Foo &lhs, Foo &rhs){

using std::swap;//若没有类型自定义的swap版本,则调用std的版本

swap(lhs.h, rhs.h);//类型自定义的版本

}

拷贝并交换技术

Foo& Foo::operator=(Foo rhs){

swap(*this, rhs);

return *this;

}

参数并不是引用,在函数执行完后会释放。可自动处理自赋值的情况,且是异常安全的。

13.6 对象移动

当一个对象拷贝后就立即销毁,此时使用移动操作可以提高性能。

标准库容器,string,shared_ptr类支持移动和拷贝,IO类和unique_ptr类只支持移动。

13.6.1 右值引用

即,必须绑定到右值的引用。只能绑定到将要销毁的对象,故可以将右值引用的资源移动到另一个对象中。

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

int &&r = i*42;

返回左值引用的函数,连同赋值,下标,解引用和前置递增递减运算符,都返回左值;

返回非引用类型的函数,连同算数,关系,位以及后置递增递减运算符,都生成右值,可以用const的左值引用和右值引用绑定。

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

右值意味着:该对象将被销毁;该对象没有其他用户。故使用右值引用的代码可以自由的接管引用的对象的资源。

int &&rr1 = 42;

int &&rr2 = rr1;//错误:rr1是右值引用类型的变量,是左值

#include <utility>

int &&rr3  =std::move(rr1);//move函数获得绑定到左值上的右值引用。

move意味着希望像处理右值一样处理一个左值,即,除了对rr1赋值和销毁外,代码不会再使用它。

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

从给定对象“窃取”而不是拷贝资源。

移动拷贝构造函数第一个参数是该类类型的右值引用,其他的参数必须都具有默认实参。

StrVec::StrVec(StrVec &&s) noexcept:e(s.e), f(s.f){s.e = s.f = nullptr;}

1,移动操作不应该抛出异常,noexcept通知标准库移动操作是安全的的,无需标准库做额外的操作

2,成员初始化器中接管s中的资源

3,函数体中使对s进行析构是安全的

noexcept在声明和定义中都需要指定。

StrVec &StrVec::operator=(StrVec &&rhs) noexcept{}

在移动操作之后,移后源对象必须保持有效的,可析构的状态,但用户不可对其值进行任何假设。

当一个类没有定义任何自己版本的拷贝控制成员,且类的每个非static数据成员都可以移动时,编译器才会合成移动构造函数或移动赋值运算符。

可以移动:内置类型,类类型定义了相应的移动操作。

移动操作不会隐式的定义为删除的函数,但显式定义=default的移动操作,且编译器不能移动所有成员,则编译器将移动操作定义为删除的函数。

如果类定义了一个移动构造函数或一个移动赋值运算符,则该类的合成拷贝构造函数和拷贝赋值运算符会被定义为删除的。

如果一个类既有移动构造函数,又有拷贝构造函数,则使用普通的函数匹配规则来选择调用。如果没有移动构造函数或移动赋值运算符,右值会被拷贝。

移动迭代器

移动迭代器的解引用运算符生成一个右值引用,make_move_iterator(origin_iterator);函数将普通的迭代器转换为一个移动迭代器,会调用相应的西东构造函数或移动赋值运算符操作。

13.6.3 右值引用和成员函数

成员函数提供移动版本:

void push_back(const X&);//拷贝

void push_back(X&&);//移动

引用限定符

Foo &operator=(const Foo&) &;//只能向可修改的左值赋值

引用限定符可以是&和&&,分别指出this可以指向一个左值或右值,只能用于非static的成员函数,且必须同时出现在声明和定义中。

一个函数可以同时用const和引用限定,但引用限定符必须跟随在const之后。

引用限定符可以区分重载版本,并且可以和const综合起来区分。

当定义多个具有相同名字和相同参数列表的成员函数时,必须所有函数都加上引用限定符或都不加。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,242评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,769评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,484评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,133评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,007评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,080评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,496评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,190评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,464评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,549评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,330评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,205评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,567评论 3 298
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,889评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,160评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,475评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,650评论 2 335

推荐阅读更多精彩内容