C++ 拷贝控制(二) — 移动构造函数和移动赋值运算符

相关文章:

移动语义

移动语义是 C++ 新标准所引入的一个新的概念,和拷贝语义相对。以拷贝赋值为例,在拷贝的对象的过程当中,为了保证拷贝过程是异常安全的,我们往往需要以下三个步骤:

  • 将赋值运算符左侧的运算对象(以下简称左对象)用局部对象 tmp 保存下来
  • 将左对象析构
  • 将临时变量拷贝到右对象当中。

而在这一过程当中,局部变量 tmp 的作用仅仅只是作为中转站,在拷贝开始前创建,拷贝结束后销毁,成为了制约拷贝效率的一个短板。而 C++ 新标准中所引入的移动语义,则能够将一个对象转移到另一个对象当中,而避免了不必要的局部对象的创建、拷贝和销毁操作。通俗地说,拷贝构造函数和拷贝赋值运算符为对象提供了 “拷贝” 功能,而移动构造函数和移动赋值运算符则为对象提供了 “剪切” 的功能

C++ 中的移动语义通过右值引用来实现,右值引用不仅延长临时对象的生命周期,还可以直接获取临时对象的全部状态。借由移动语义,我们可以将临时对象转移至其他对象当中,从而大大提高了效率。一个比较典型的例子是swap 函数的实现:

template <typename T>
void swap(T &a, T &b){
    T tmp(std::move(a));
    a = std::move(b);
    b = std::move(tmp);
}

移动构造函数

移动构造函数的定义如下:

class MyClass{
public:
    ...
    //移动构造函数定义方式
    MyClass(MyClass &&obj) noexcept:p_str(obj.p_str){
        //将移动源对象置于析构安全状态
        obj.p_str = nullptr;
    }
    ...
private:
    string* p_str;
};
int main(void){
    ...
    MyClass a;
    MyClass b = a;              // 拷贝构造方式
    MyClass c = std::move(a);   // 移动构造方式
    ...
    return 0;
}

从例子中可以看出,移动构造函数是一个以右值引用为参数的构造函数。对于一个移动构造函数,我们应当注意以下两点:

  1. 移动构造函数通常被声明为“无异常抛出”的函数。由于移动操作可以 “窃取” 源对象的状态,因此不涉及资源分配,故一般不抛出异常。此外,当进行拷贝时,由于源对象的状态不发生改变,因此当异常发生时,只需要释放新分配的资源即可,而移动对象会改变源对象的状态,因此出于安全性考虑,编译器在移动构造函数和移动赋值运算符没有显式声明为 “无异常抛出” 时,会在移动过程中自动调用拷贝构造函数和拷贝赋值运算符。
  2. 移动后可能需要对源对象进行析构,我们需要将源对象设置为可安全析构的状态,这样才能防止释放源对象后对移动后对象产生影响。

移动赋值运算符

移动赋值运算符的定义和使用方式如下

class MyClass{
public:
    ...
    //移动赋值运算符定义方式
    MyClass& operator=(MyClass &&obj)noexcept {
        if(this != &obj){
            p_str = obj.p_str;
            obj.p_str = nullptr;
        }
        return *this;
    }
    ...
private:
    string* p_str;
};
int main(void){
    ...
    MyClass a;
    MyClass b,c
    b = a;              // 拷贝赋值方式
    c = std::move(a);   // 移动赋值方式
    ...
    return 0;
}

基于同样的理由,我们通常也会将移动赋值运算符声明为 “无异常抛出” 的,同时我们也需要考虑自赋值以及移动后将源对象置于可析构的有效状态。在使用上,使用移动赋值运算符需要用 std::move 函数来将左值引用转化为右值引用。编译器会根据函数匹配的结果找到合适的运算符。

默认移动构造函数与默认移动赋值运算符

移动构造函数和移动赋值运算符的默认生成规则如下:

生成规则:

  • 只有当一个类没有定义任何自己版本的拷贝控制成员,且所有的非 static 数据成员都能进行移动构造和移动赋值时,编译器才会自动生成移动构造函数和移动赋值运算符

删除规则:

  • 当有类成员定义了自己的拷贝操作而没有定义移动操作,或者编译器不能为类成员生成默认移动操作时,该类的移动操作是删除的;
  • 若类成员的移动操作是删除的或不可访问的,则类的移动操作也被定义为删除的;
  • 类的析构函数被定义为删除的或是不可访问的,则类的移动构造函数被定义为删除的
  • 类成员是 const 或者引用类型,则类的移动赋值运算符被定义为删除的。

注:移动操作 = 移动构造函数 + 移动赋值运算符

总结

  • C++ 新标准中引入了移动语义,能够为对象提供 “剪切” 的功能,这样可以避免不必要的临时对象的创建、拷贝和析构过程,大大提高了效率。移动语义通过右值引用来实现
  • 移动构造函数和移动赋值运算符通常需要被显式声明为 “无异常抛出” 的,否则编译器会出于安全性的考虑,在“剪切”对象的过程中使用拷贝功能。此外,移动的后还应当将移动源对象设置为析构安全的状态,以避免移动源对象析构后导致移动后对象访问异常。
  • 对于移动构造函数和移动赋值运算符而言,一旦类定义了拷贝构造函数和拷贝赋值运算符,则编译器不会自动生成移动构造函数和移动赋值运算符
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,001评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,210评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 161,874评论 0 351
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,001评论 1 291
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,022评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,005评论 1 295
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,929评论 3 416
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,742评论 0 271
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,193评论 1 309
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,427评论 2 331
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,583评论 1 346
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,305评论 5 342
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,911评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,564评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,731评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,581评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,478评论 2 352