移动构造函数和移动赋值函数

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了移动构造函数和移动赋值函数相关的知识,希望对你有一定的参考价值。

在一个类的内部同时实现常规拷贝构造函数和移动拷贝构造函数,常规赋值函数和移动赋值函数。
调用时若参数为一个左值,则调用常规函数;若参数为一个右值,则调用移动函数。
也可调用"std::move"强行调用移动函数。

#include <iostream>
#include <utility>
using std::cout;
using std::endl;

class Useless
{
private:
    int n;          // number of elements
    char * pc;      // pointer to data
    static int ct;  // number of objects
public:
    Useless();
    explicit Useless(int k);
    Useless(int k, char ch);
    Useless(const Useless & f); // regular copy constructor
    Useless(Useless && f);      // move constructor
    ~Useless();
    Useless operator+(const Useless & f)const;
    Useless & operator=(const Useless & f); // copy assignment
    Useless & operator=(Useless && f);      // move assignment 
    void ShowObject() const;
};
// implementation
int Useless::ct = 0;
Useless::Useless()
{
    cout << "enter " << __func__ << "()\n";
    ++ct;
    n = 0;
    pc = nullptr;
    ShowObject();
    cout << "leave " << __func__ << "()\n";
 }
Useless::Useless(int k) : n(k)
{
    cout << "enter " << __func__ << "(k)\n";
    ++ct; 
    pc = new char[n];
    ShowObject();
    cout << "leave " << __func__ << "(k)\n";
}
Useless::Useless(int k, char ch) : n(k)
{
    cout << "enter " << __func__ << "(k, ch)\n";
    ++ct;
    pc = new char[n];
    for (int i = 0; i < n; i++)
        pc[i] = ch;
    ShowObject();
    cout << "leave " << __func__ << "(k, ch)\n";
}
Useless::Useless(const Useless & f): n(f.n) 
{
    cout << "enter " << __func__ << "(const &)\n";
    ++ct;
    pc = new char[n];
    for (int i = 0; i < n; i++)
        pc[i] = f.pc[i];
    ShowObject();
    cout << "leave " << __func__ << "(const &)\n";
}
Useless::Useless(Useless && f): n(f.n) 
{
    cout << "enter " << __func__ << "(&&)\n";
    ++ct;
    pc = f.pc;       // steal address
    f.pc = nullptr;  // give old object nothing in return
    f.n = 0;
    ShowObject();
    f.ShowObject();
    cout << "leave " << __func__ << "(&&)\n";
}
Useless::~Useless()
{
    cout << "enter " << __func__ << "()\n";
    ShowObject();
    --ct;
    delete [] pc;
    cout << "leave " << __func__ << "()\n";
}
Useless & Useless::operator=(const Useless & f)  // copy assignment
{
    cout << "enter " << __func__ << "(const &)\n";
    ShowObject();
    f.ShowObject();
    if (this == &f)
        return *this;
    delete [] pc;
    n = f.n;
    pc = new char[n];
    for (int i = 0; i < n; i++)
        pc[i] = f.pc[i];
    ShowObject();
    f.ShowObject();
    cout << "leave " << __func__ << "(const &)\n";
    return *this;
}
Useless & Useless::operator=(Useless && f)       // move assignment
{
    cout << "enter " << __func__ << "(&&)\n";
    ShowObject();
    f.ShowObject();
    if (this == &f)
        return *this;
    delete [] pc;
    n = f.n;
    pc = f.pc;
    f.n = 0;
    f.pc = nullptr;
    ShowObject();
    f.ShowObject();
    cout << "leave " << __func__ << "(&&)\n";
    return *this;
}
Useless Useless::operator+(const Useless & f)const
{
    cout << "enter " << __func__ << "(const &)\n";
    ShowObject();
    f.ShowObject();
    Useless temp = Useless(n + f.n);
    for (int i = 0; i < n; i++)
        temp.pc[i] = pc[i];
    for (int i = n; i < temp.n; i++)
        temp.pc[i] = f.pc[i - n];
    cout << "\t temp: ";
    temp.ShowObject();
    cout << "leave " << __func__ << "(const &)\n";
    return temp;
}
void Useless::ShowObject() const
{ 
    cout << "\t this=" << this << ", ct=" << ct; 
    cout << ", pc=(" << n << ", " << (void*)pc << ", "; 
    if (n == 0)
        cout << "(object empty)";
    else
        for (int i = 0; i < n; i++)
            cout << pc[i];
    cout << endl;
}
// application
int main()
{
    Useless one(10, ‘x‘);
    Useless two = one + one;   // calls move constructor
    cout << "object one:\n";
    one.ShowObject();
    cout << "object two:\n";
    two.ShowObject();
    Useless three, four;
    cout << "three = one\n";
    three = one;              // automatic copy assignment
    cout << "now object three:\n";
    three.ShowObject();
    cout << "and object one:\n";
    one.ShowObject();
    cout << "four = one + two\n";
    four = one + two;         // automatic move assignment
    cout << "now object four:\n";
    four.ShowObject();
    cout << "four = move(one)\n";
    four = std::move(one);    // forced move assignment
    cout << "now object four:\n";
    four.ShowObject();
    cout << "and object one:\n";
    one.ShowObject();
    
 std::cin.get();
}

测试结果如下。红色部分不是测试结果的一部分,是对测试结果的分析。

enter Useless(k, ch)
         this=0x7fffb5ade0f0, ct=1, pc=(10, 0x23f7010, xxxxxxxxxx
leave Useless(k, ch)
enter operator+(const &)
         this=0x7fffb5ade0f0, ct=1, pc=(10, 0x23f7010, xxxxxxxxxx
         this=0x7fffb5ade0f0, ct=1, pc=(10, 0x23f7010, xxxxxxxxxx
enter Useless(k)
         this=0x7fffb5ade100, ct=2, pc=(20, 0x23f7030,
leave Useless(k)
         temp:   this=0x7fffb5ade100, ct=2, pc=(20, 0x23f7030, xxxxxxxxxxxxxxxxxxxx
leave operator+(const &)
object one:
         this=0x7fffb5ade0f0, ct=2, pc=(10, 0x23f7010, xxxxxxxxxx
object two:
         this=0x7fffb5ade100, ct=2, pc=(20, 0x23f7030, xxxxxxxxxxxxxxxxxxxx
         //"Useless two = one +one;"
         //首先调用"operator+(const &)",在这个函数内调用"Useless(k)"生成temp对象。
         //返回时调用拷贝构造函数生成一个临时匿名对象。
         //析构temp对象。
         //然后再调用移动拷贝构造函数,生成对象two。
         //析构临时匿名对象。
         //当前gcc版本是g++ (Ubuntu 4.8.4-2ubuntu1~14.04) 4.8.4,对编译过程做了改进,直接把temp对象和two对象当成一个对象,省去了后面四步。

enter Useless()
         this=0x7fffb5ade110, ct=3, pc=(0, 0, (object empty)
leave Useless()
enter Useless()
         this=0x7fffb5ade120, ct=4, pc=(0, 0, (object empty)
leave Useless()
three = one
enter operator=(const &)
         this=0x7fffb5ade110, ct=4, pc=(0, 0, (object empty)
         this=0x7fffb5ade0f0, ct=4, pc=(10, 0x23f7010, xxxxxxxxxx
         this=0x7fffb5ade110, ct=4, pc=(10, 0x23f7050, xxxxxxxxxx
         this=0x7fffb5ade0f0, ct=4, pc=(10, 0x23f7010, xxxxxxxxxx
leave operator=(const &)
now object three:
         this=0x7fffb5ade110, ct=4, pc=(10, 0x23f7050, xxxxxxxxxx
and object one:
         this=0x7fffb5ade0f0, ct=4, pc=(10, 0x23f7010, xxxxxxxxxx
four = one + two
enter operator+(const &)
         this=0x7fffb5ade0f0, ct=4, pc=(10, 0x23f7010, xxxxxxxxxx
         this=0x7fffb5ade100, ct=4, pc=(20, 0x23f7030, xxxxxxxxxxxxxxxxxxxx
enter Useless(k)
         this=0x7fffb5ade130, ct=5, pc=(30, 0x23f7070,
leave Useless(k)
         temp:   this=0x7fffb5ade130, ct=5, pc=(30, 0x23f7070, xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
leave operator+(const &)
//"four = one + two"首先调用"operator+(const &)"函数。在这个函数内生成temp对象。
//在返回"operator+(const &)"函数后,并没有生成一个临时匿名对象,也没有析构temp对象,而是直接以temp做参数调用移动拷贝函数"operator=(&&)"。

enter operator=(&&)
         this=0x7fffb5ade120, ct=5, pc=(0, 0, (object empty)
         this=0x7fffb5ade130, ct=5, pc=(30, 0x23f7070, xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
         this=0x7fffb5ade120, ct=5, pc=(30, 0x23f7070, xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
         this=0x7fffb5ade130, ct=5, pc=(0, 0, (object empty)
leave operator=(&&)
//在返回"operator=(&&)"函数后才析构temp对象。
enter ~Useless()
         this=0x7fffb5ade130, ct=5, pc=(0, 0, (object empty)
leave ~Useless()
now object four:
         this=0x7fffb5ade120, ct=4, pc=(30, 0x23f7070, xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
four = move(one)
//"four = std::move(one);"强行调用移动赋值函数"operator=(&&)"。
//调用之后,four对象接管了one对象的内部资源(pc和n),one对象没有被析构,但内部资源被“掏空”了!

enter operator=(&&)
         this=0x7fffb5ade120, ct=4, pc=(30, 0x23f7070, xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
         this=0x7fffb5ade0f0, ct=4, pc=(10, 0x23f7010, xxxxxxxxxx
         this=0x7fffb5ade120, ct=4, pc=(10, 0x23f7010, xxxxxxxxxx
         this=0x7fffb5ade0f0, ct=4, pc=(0, 0, (object empty)
leave operator=(&&)
now object four:
         this=0x7fffb5ade120, ct=4, pc=(10, 0x23f7010, xxxxxxxxxx
and object one:
         this=0x7fffb5ade0f0, ct=4, pc=(0, 0, (object empty)

//退出"main()"时,析构栈空间的对象。析构顺序与构造顺序相反。
enter ~Useless()
         this=0x7fffb5ade120, ct=4, pc=(10, 0x23f7010, xxxxxxxxxx
leave ~Useless()
enter ~Useless()
         this=0x7fffb5ade110, ct=3, pc=(10, 0x23f7050, xxxxxxxxxx
leave ~Useless()
enter ~Useless()
         this=0x7fffb5ade100, ct=2, pc=(20, 0x23f7030, xxxxxxxxxxxxxxxxxxxx
leave ~Useless()
enter ~Useless()
         this=0x7fffb5ade0f0, ct=1, pc=(0, 0, (object empty)
leave ~Useless()


本文出自 “用C++写诗” 博客,谢绝转载!

以上是关于移动构造函数和移动赋值函数的主要内容,如果未能解决你的问题,请参考以下文章

如何利用模板复制&移动构造函数和赋值运算符?

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

移动构造函数和移动赋值

编译器何时不生成移动构造函数和移动赋值?

C++链表移动赋值和移动构造函数

构造函数复制构造赋值操作移动构造移动操作