• 拷贝初始化与赋值时的拷贝与移动操作


    摘要:右值引用不会拷贝/赋值。在拷贝初始化或赋值时,调用拷贝构造函数/运算符还是移动构造函数/运算符都是允许的,实际调用拷贝的还是移动的,要看类是否定义了移动构造函数/运算符,若定义了,则调用移动的;否则调用拷贝的。

    有一个类HasPtr,假设已定义了一个HasPtr 对象 hp。文末给出了该类的定义。

    右值引用

    对于 HasPtr&& hp1 = std::move(hp);,这个是不会进行任何拷贝/赋值的,也就不会调用任何构造函数或赋值运算符,因为这个就是给 hp 添加了一个别名,执行该语句后 hp 仍是原来的值,地址也不变。

    // main.cpp
    #include "HasPtr_draft.h"
    
    int main()
    {
        HasPtr hp("hello");
        cout << *hp.ps << "\t" << &hp << "\n";
        HasPtr&& hp1 = std::move(hp);
        cout << *hp1.ps << "\t" << &hp1 << "\n";
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    输出:

    hello 0x808f3ff6b0
    hello 0x808f3ff6b0

    拷贝初始化

    对于 HasPtr hp1 = std::move(hp);,这个可能会调用移动构造函数,但也有可能调用拷贝构造函数。首先我们知道 hp1 是新定义的一个对象,所以这个语句肯定会执行拷贝初始化。赋值号右边,std::move(hp) 返回一个右值。

    int main()
    {
        HasPtr hp("hello");
        cout << hp.ps << "\n";
        HasPtr hp1 = std::move(hp);
        cout << hp.ps << "\n";
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 若未定义移动构造函数,则调用拷贝构造函数,该右值会传给拷贝构造函数的形参,此时 HasPtr&& 转换为 const HasPtr&,这是允许的。

      输出:

      0x180215f4b60
      move constructor
      0

    • 若定义了移动构造函数,则调用移动构造函数。此时,虽然拷贝构造函数与移动构造函数都是参数匹配的,但移动构造函数是精确匹配,无需转换,因此会被调用。

      输出:

      0x19ca3f34b60
      copy constructor
      0x19ca3f34b60

    拷贝赋值

    对于 HasPtr hp1; hp1 = std::move(hp);,这个是拷贝赋值,也是要看类是否定义了移动赋值运算符。

    int main()
    {
        HasPtr hp("hello");
        cout << hp.ps << "\n";
        HasPtr hp1;
        hp1 = std::move(hp);
        cout << hp.ps << "\n";
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 若未定义移动赋值运算符,则调用拷贝赋值运算符。

      输出:

      0x19e78c74b60
      copy-assignment
      0x19e78c74b60

    • 若定义了移动赋值运算符,则调用移动赋值运算符,因其精确匹配。

      输出:

      0x2b0383e4b60
      move-assignment
      0

    附录:HasPtr类的定义

    // HasPtr_draft.h
    #ifndef HASPTR_DRAFT_H
    #define HASPTR_DRAFT_H
    #include 
    #include 
    
    using std::cout;
    using std::string;
    
    struct HasPtr
    {
        int i;
        string *ps;
        HasPtr(const string &s = "") : ps(new string(s)), i(0) {}
        HasPtr(const HasPtr &hp) : i(hp.i), ps(new string(*hp.ps)) // 拷贝构造函数
         { cout << "copy constructor\n"; }
        HasPtr &operator=(const HasPtr &rhs) // 拷贝赋值运算符
        {
            auto p = new string(*rhs.ps);
            delete ps;
            ps = p;
            i = rhs.i;
            cout << "copy-assignment\n";
            return *this;
        }
        HasPtr(HasPtr &&hp) noexcept : i(hp.i), ps(hp.ps) // 移动构造函数
        {
            hp.ps = 0;
            cout << "move constructor\n";
        }
        HasPtr &operator=(HasPtr &&hp) noexcept // 移动赋值运算符
        {
            i = hp.i;
            ps = hp.ps;
            hp.ps = 0;
            cout << "move-assignment\n";
            return *this;
        }
    };
    
    #endif
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
  • 相关阅读:
    学编程:Python入门考级必备[10]
    HTML5期末考核大作业,网站——青岛民俗 7页。 美丽家乡 学生旅行 游玩 主题住宿网页
    GAN Step By Step -- Step2 GAN的详细介绍及其应用
    openGL之纹理
    Linu基础-分区规划与使用
    WeNet:面向工业落地的E2E语音识别工具
    合并K个升序链表
    Java的设计模式基本概念及使用场景
    【PAT甲级】1022 Digital Library
    Spring MVC 中的数据验证技术
  • 原文地址:https://blog.csdn.net/jasminefeng/article/details/127416936