• C++ 何时调用默认构造、拷贝构造、移动构造、拷贝赋值、移动赋值、析构以及对象析构顺序


    一小段代码足以说明问题

    核心测试代码

    int main()
    {
        cout << "1."; Foo a1;       // 默认构造
        cout << "2."; Foo a2(a1);   // 拷贝构造(直接初始化)
        cout << "3."; Foo a3 = a1;  // 拷贝构造(拷贝初始化)
        cout << "4."; Foo a4 = static_cast<Foo>(10);  // 默认优化:直接构造;如果禁用一切优化,直接构造+移动构造(拷贝初始化)
        cout << "5."; a1 = a2;      // 拷贝赋值
        cout << "6."; a1 = static_cast<Foo>(5);       // 直接构造、移动赋值、立即析构右侧移动后的对象
        cout << "----------" << endl;
    }
    

    执行结果及分析

    # g++ main.cpp && ./a.out
    1.默认构造 ptr = 0               # a1
    2.拷贝构造 ptr = 0x559e05fe0280  # a2
    3.拷贝构造 ptr = 0x559e05fe02a0  # a3
    4.直接构造 ptr = 0x559e05fe02c0  # a4
    5.拷贝赋值 ptr = 0x559e05fe02f0  # a1=a2,深拷贝 a2 导致生成新的 ptr 地址
    6.直接构造 ptr = 0x559e05fe0310  # static_cast<Foo>(5)
    移动赋值 ptr = 0x559e05fe0310    # static_cast<Foo>(5) 转移给了 a1
    析构 ptr = 0                    # 转移后 static_cast<Foo>(5) 的析构
    ----------
    析构 ptr = 0x559e05fe02c0       # a4
    析构 ptr = 0x559e05fe02a0       # a3
    析构 ptr = 0x559e05fe0280       # a2
    析构 ptr = 0x559e05fe0310       # a1
    

    结论

    • 直接初始化和拷贝初始化都调用拷贝构造
    • 右值(将亡值)如测试代码中的 static_cast<Foo>(5),立即析构
    • 局部变量析构顺序(a4,a3,a2,a1)和定义顺序(a1,a2,a3,a4)相反
    • 默认优化,可以跳过构造+移动,省略临时变量,直接构造 a4 对象。即 Foo a4 = static_cast<Foo>(10); 默认直接优化为 Foo a4(10);

    完整测试代码

    #include <iostream>
    using namespace std;
    
    class Foo {
      public:
        Foo() : size(0), ptr(nullptr) { cout << "默认构造 ptr = " << ptr << endl; }
        explicit Foo(unsigned s) : size(s)
        {
            ptr = new int[size];
            cout << "直接构造 ptr = " << ptr << endl;
        }
        Foo(const Foo &I) : size(I.size)
        {
            ptr = new int[size];
            for (unsigned i = 0; i < size; ++i) ptr[i] = I.ptr[i];
            cout << "拷贝构造 ptr = " << ptr << endl;
        }
        Foo(Foo &&I) : size(I.size)
        {
            ptr = I.ptr;
            I.ptr = nullptr;
            cout << "移动构造 ptr = " << ptr << endl;
        }
        Foo &operator=(const Foo &I)
        {  // 内存泄漏!标准解法:拷贝、交换
            if (this == &I) return *this;
            size = I.size;
            ptr = new int[size];
            for (unsigned i = 0; i < size; ++i) ptr[i] = I.ptr[i];
            cout << "拷贝赋值 ptr = " << ptr << endl;
            return *this;
        }
        Foo &operator=(Foo &&I)
        {
            if (this == &I) return *this;
            size = I.size;
            ptr = I.ptr;
            I.ptr = nullptr;
            cout << "移动赋值 ptr = " << ptr << endl;
            return *this;
        }
        ~Foo()
        {
            cout << "析构 ptr = " << ptr << endl;
            if (ptr) {
                delete[] ptr;
                ptr = nullptr;
            }
        }
    
      private:
        unsigned size;
        int *ptr;
    };
    
    int main()
    {
        cout << "1."; Foo a1;        // 默认构造
        cout << "2."; Foo a2(a1);   // 拷贝构造(直接初始化)
        cout << "3."; Foo a3 = a1;  // 拷贝构造(拷贝初始化)
        cout << "4."; Foo a4 = static_cast<Foo>(10);  // 默认优化:直接构造;如果禁用一切优化,直接构造+移动构造(拷贝初始化)
        cout << "5."; a1 = a2;      // 拷贝赋值
        cout << "6."; a1 = static_cast<Foo>(5);       // 直接构造、移动赋值、立即析构右侧移动后的对象
        cout << "----------" << endl;
    }
    
  • 相关阅读:
    Codeforces 946 A.Partition
    牛客网 2018年全国多校算法寒假训练营练习比赛(第五场) H.Tree Recovery-完全版线段树(区间更新、区间求和)
    牛客网 2018年全国多校算法寒假训练营练习比赛(第五场) F.The Biggest Water Problem
    牛客网 2018年全国多校算法寒假训练营练习比赛(第五场) B.Big Water Problem-完全版线段树(单点更新、区间求和)
    牛客网 2018年全国多校算法寒假训练营练习比赛(第五场) A.逆序数
    POJ 3368.Frequent values-处理数据+RMQ(ST)
    hdu 3033 I love sneakers! 分组背包
    bzoj 2957: 楼房重建 线段树
    hdu 5925 Coconuts 离散化+dfs
    HDU 5929 Basic Data Structure 模拟
  • 原文地址:https://www.cnblogs.com/tengzijian/p/16187388.html
Copyright © 2020-2023  润新知