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

在一个类的内部同时实现常规拷贝构造函数和移动拷贝构造函数,常规赋值函数和移动赋值函数。
调用时若参数为一个左值,则调用常规函数;若参数为一个右值,则调用移动函数。
也可调用"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()

时间: 2024-08-28 14:11:26

移动构造函数和移动赋值函数的相关文章

含有指针变量的类需要重写拷贝构造函数,拷贝赋值函数,析构函数

编译器自带拷贝构造(ctor)和拷贝赋值函数(operator =), 但是对于成员变量含有指针的类,其不能使用默认的拷贝赋值函数.因为使用默认的,会直接将指针指向的地址进行赋值 (浅拷贝,共享内存,共指一个对象),而不是分配一块内存,具有相同的数值 (深拷贝,独立,两个对象). 浅拷贝容易造成dangling pointer. 用一个例子来展示: 1 #ifndef __MYSTRING__ 2 #define __MYSTRING__ 3 4 class String{ 5 public:

复制构造函数 与 赋值函数 的区别(转)

转自:jihite 构造函数.析构函数.赋值函数是每个类最基本的的函数.每个类只有一个析构函数和一个赋值函数.但是有很多构造函数(一个为复制构造函数,其他为普通构造函数.对于一个类A,如果不编写上述四个函数,c++编译器将自动为A产生四个默认的函数,即: A(void)                                    //默认无参数构造函数 A(const A &a)                         //默认复制构造函数 ~A(void);         

拷贝构造函数和赋值函数的一些知识

/*******************拷贝构造函数和赋值运算符重载有以下两个不同之处***************************/ 1.拷贝构造函数生成新的类对象,而赋值运算符不能. 2.由于拷贝构造函数是直接构造一个新的类对象,所以在初始化这个对象之前不用检验源对象是否和新对象相同,而复制操作符需要这个操作,另外赋值运算符中如果原来对象中有内存分配,要先把内存释放掉. 下面是String类的一个实现的部分函数,可以看出二者的区别. 1 class String{ 2 public:

拷贝构造函数与赋值函数的区别

1.从概念上区分:复制构造函数是构造函数,而赋值操作符属于操作符重载范畴,它通常是类的成员函数 2.从原型上来区分:复制构造函数原型ClassType(const ClassType &);无返回值赋值操作符原型ClassType& operator=(const ClassType &);返回值为ClassType的引用,便于连续赋值操作 3.从使用的场合来区分:复制构造函数用于产生对象,它用于以下几个地方:函数参数为类的值类型时.函数返回值为类类型时以及初始化语句,例如(示例了

复制构造函数 与 赋值函数 的区别

构造函数.析构函数.赋值函数是每个类最基本的的函数.每个类只有一个析构函数和一个赋值函数.但是有很多构造函数(一个为复制构造函数,其他为普通构造函数.对于一个类A,如果不编写上述四个函数,c++编译器将自动为A产生四个默认的函数,即: A(void)                                    //默认无参数构造函数 A(const A &a)                         //默认复制构造函数 ~A(void);                   

赋值函数与拷贝构造的差异

C++ 拷贝构造函数 赋值构造函数 ================================= 一句话,赋值函数的前提是对象已定义:而拷贝构造是执行时才会创建一个对象.拷贝构造需要的是深拷贝. 赋值函数一般模式: type& operator =(const type& par) { // (1) 检查自赋值 if( this == &par ) return *this; // (2) 释放原有的内存资源 //(3)分配新的内存资源,并复制内容 ,2.3顺序没讲究,注意释

C++中拷贝构造函数和赋值函数被调用的时机

一.拷贝构造函数被调用的时机: 1. 当用类的一个对象去初始化该类的另一个对象(或引用)时系统自动调用拷贝构造函数实现拷贝赋值. 2. 若函数的形参为类对象,调用函数时,实参赋值给形参,系统自动调用拷贝构造函数. 3. 当函数的返回值是类对象时,系统自动调用拷贝构造函数. 二.赋值函数被调用的时机: 当用一个对象赋值给另一个对象时 注意一.二中初始化和赋值的区别 C++中拷贝构造函数和赋值函数被调用的时机,布布扣,bubuko.com

编写类String 的构造函数、析构函数和赋值函数

编写类String 的构造函数.析构函数和赋值函数,已知类String 的原型为:class String{public:String(const char *str = NULL); // 普通构造函数String(const String &other); // 拷贝构造函数~ String(void); // 析构函数String & operate =(const String &other); // 赋值函数private:char *m_data; // 用于保存字符串

拷贝构造函数和赋值函数

在 C++ 中,赋值和拷贝是不同的, 1)拷贝构造函数是对未初始化的内存进行初始化操作 2)而赋值是对现有的已经初始化的对象进行操作.(这里我对"已经初始化"的理解是已经调用了构造函数,并且构造函数体可以未执行,只要调用到即可),赋值函数应该给所有数据成员都初始化. 3)重点:包含动态分配成员的类 应提供拷贝构造函数,并重载"="赋值操作符. 4)可以说,C++中什么时候有临时对象产生,此时刻c++一定要调用拷贝构造函数.(临时对象产生时有一个特例,此时不需要调用拷