zoukankan      html  css  js  c++  java
  • 拷贝构造函数和移动构造函数

    转载自拷贝构造函数和移动构造函数

    C++11之前,对象的拷贝控制由三个函数决定:拷贝构造函数(Copy Constructor)、拷贝赋值运算符(Copy
    Assignment operator)和析构函数(Destructor)。

    C++11之后,新增加了两个函数:移动构造函数(Move Constructor)和移动赋值运算符(Move Assignment operator)。

    我猜即使是经常用C++编程的同学也不一定听说过后两者。其实不了解这些并不影响编程,但了解了之后就会进一步感受到C++的强(丧)大(心)威(病)力(狂)。

    好了,下面步入正题,我希望用最简单的几个案例说明这些构造函数和运算符的用途。

    口诀:构造函数与赋值运算符的区别是,构造函数在创建或初始化对象的时候调用,而赋值运算符在更新一个对象的值时调用。

    举个例子:

    #include <iostream>
    using namespace std;
    
    class A {
    public:
        int x;
        A(int x) : x(x)
        {
            cout << "Constructor" << endl;
        }
        A(A& a) : x(a.x)
        {
            cout << "Copy Constructor" << endl;
        }
        A& operator=(A& a)
        {
            x = a.x;
            cout << "Copy Assignment operator" << endl;
            return *this;
        }
        A(A&& a) : x(a.x)
        {
            cout << "Move Constructor" << endl;
        }
        A& operator=(A&& a)
        {
            x = a.x;
            cout << "Move Assignment operator" << endl;
            return *this;
        }
    };
    
    A GetA()
    {
        return A(1);
    }
    
    A&& MoveA(A& a)
    {
        return std::move(a);
    }
    
    int main()
    {
        cout << "-------------------------1-------------------------" << endl;
        A a(1);
        cout << "-------------------------2-------------------------" << endl;
        A b = a;
        cout << "-------------------------3-------------------------" << endl;
        A c(a);
        cout << "-------------------------4-------------------------" << endl;
        b = a;
        cout << "-------------------------5-------------------------" << endl;
        A d = A(1);
        cout << "-------------------------6-------------------------" << endl;
        A e = std::move(a);
        cout << "-------------------------7-------------------------" << endl;
        A f = GetA();
        cout << "-------------------------8-------------------------" << endl;
        A&& g = MoveA(f);
        cout << "-------------------------9-------------------------" << endl;
        d = A(1);
    }
    

    请读者猜测这九行语句各自的输出是什么。
    下面公布答案:

    -------------------------1-------------------------
    Constructor
    -------------------------2-------------------------
    Copy Constructor
    -------------------------3-------------------------
    Copy Constructor
    -------------------------4-------------------------
    Copy Assignment operator
    -------------------------5-------------------------
    Constructor
    Move Constructor
    -------------------------6-------------------------
    Move Constructor
    -------------------------7-------------------------
    Constructor
    Move Constructor
    Move Constructor
    -------------------------8-------------------------
    -------------------------9-------------------------
    Constructor
    Move Assignment operator
    

    我们来分析这里面的奥妙。

    第1行毋庸置疑,调用构造函数。
    第2行创建新对象b,使用a初始化b,因此调用拷贝构造函数。
    第3行创建新对象c,使用a初始化c,因此调用拷贝构造函数。
    第4行使用a的值更新对象b,因为不需要创建新对象,所以调用拷贝赋值运算符。
    第5行创建新对象d,使用临时对象A(1)初始化d,由于临时对象是一个右值,所以调用移动构造函数。
    第6行创建新对象e,使用a的值初始化e,但调用std::move(a)将左值a转化为右值,所以调用移动构造函数。
    第7行创建新对象f,使用GetA()函数返回的临时对象初始化f,由于临时对象是右值,所以调用移动构造函数。值得注意的是,这里调用了两次移动构造函数。第一次是GetA()返回前,A(1)移动构造了一个临时对象。第二次是临时对象移动构造f。
    第8行没有创建新对象,也不更新任何对象,只是将MoveA()的返回值绑定到右值引用g。因此不调用构造函数,也不调用赋值运算符。
    第9行使用临时对象A(1)更新d,因为不需要创建新对象,所以调用移动赋值运算符。

    怎么样,是不是一脸懵逼?哈哈哈...

    我知道仅凭这些是不足以搞懂拷贝构造函数和移动构造函数的,特别是移动构造函数,它涉及到C++编程的根本问题:值传递和引用传递的问题。与Java等完全建立在堆上、含垃圾回收器的语言相比,C++的特点就是撇清值和引用的区别,而不是像Java一样全部按照引用来对待。然而值传递造成的性能问题必须解决,所以有了C++11新特性:移动拷贝、移动赋值、右值引用等概念。直观来讲,移动语义的出现使得大对象可以避免频繁拷贝造成的性能下降,特别是对于临时对象,移动语义是传递它们的最佳方式。

  • 相关阅读:
    hdu 2222 Keywords Search
    Meet and Greet
    hdu 4673
    hdu 4768
    hdu 4747 Mex
    uva 1513 Movie collection
    uva 12299 RMQ with Shifts
    uva 11732 strcmp() Anyone?
    uva 1401
    hdu 1251 统计难题
  • 原文地址:https://www.cnblogs.com/hunter-w/p/13884186.html
Copyright © 2011-2022 走看看