c++ 11学习笔记--右值引用和移动构造语义

今天我决定尝试用另外一种方式来表达,任何新语法的的产生都是为了解决某个问题,所以今天先看问题。

class myStr
{
protected:
    char* str_;
    
public:
    myStr(void)                       // 默认的构造函数,什么也不做
    : str_(nullptr)
    {}
    
    myStr(const char* rhs)            // 普通赋值构造函数
    : str_(nullptr)
    {
        if (!rhs) return;
        str_ = new char[1024];
        strcpy(str_, rhs);
      //  cout << "Str constructor " << str_ << std::endl;
    }
    
    myStr(const myStr& rhs)             // 拷贝构造函数
    : str_(nullptr)
    {
        if (!rhs) return;
        str_ = new char[1024];
        strcpy(str_, rhs.str_);
      //  cout << "Str copy constructor " << str_ << std::endl;
    }
    
    myStr(myStr&& rhs)
    : str_(nullptr)
    {
        swap(rhs);
      //  std::cout << "Str move constructor " << str_ << std::endl;
    }
    
    ~myStr()                          // 析构函数
    {
        if (!str_) return;
      //  std::cout << "Str destructor " << str_ << std::endl;
        delete [] str_;
    }
    
    const myStr& operator=(myStr rhs)   // 赋值操作符重载
    {
        rhs.swap(*this);            // 使用copy-and-swap惯用法获得数据
        return (*this);             // 避免重复撰写operator=
    }
    
    void swap(myStr& rhs)             // 交换算法
    {
        std::swap(str_, rhs.str_);
    }
    
    operator char*(void) const
    {
        return str_;
    }
    
    myStr& operator+=(const char* rhs)
    {
        if (rhs) strcat(str_, rhs);
        return (*this);
    }
    
//    friend myStr operator+(const myStr& x, const myStr& y)
//    {
//        return myStr(x) += y;
//    }
    friend myStr operator+(const myStr& x, const myStr& y)
    {
        return std::move(myStr(x) += y);
}

执行下面的代码

myStr ss("000");
    myStr s1("11111"), s2("22222"), s3("3333333"), s4("4444444");
    cout << std::endl;
    time_t timestamp1;
    time_t timestamp2;
    time_t timestamp3;
    
    const long long max = 30000000;
    time(&timestamp1);

    for (long long i = 0; i<max; i++) {
        ss = s1 + s2 + s3 + s4;
    }
     time(&timestamp2);
    
 timestamp3 = timestamp2 - timestamp1;

 下面的代码是唯一不同的实现,但是却带来30-40%的性能差距。

/    friend myStr operator+(const myStr& x, const myStr& y)
//    {
//        return myStr(x) += y;
//    }
    friend myStr operator+(const myStr& x, const myStr& y)
    {
        return std::move(myStr(x) += y);
}

再找一个例子

class MemoryBlock
{
public:
    
    // 构造器(初始化资源)
    explicit MemoryBlock(size_t length)
    : _length(length)
    , _data(new int[length])
    {
        std::cout << "MemoryBlock constructor "  << std::endl;
    }
    
    // 析构器(释放资源)
    ~MemoryBlock()
    {
        if (_data != nullptr)
        {
            delete[] _data;
        }
        std::cout << "MemoryBlock destructor "  << std::endl;
    }
    
    // 拷贝构造器(实现拷贝语义:拷贝that)
    MemoryBlock(const MemoryBlock& that)
    // 拷贝that对象所拥有的资源
    : _length(that._length)
    , _data(new int[that._length])
    {
        std::copy(that._data, that._data + _length, _data);
        std::cout << "copy constructor "  << std::endl;
    }
    
    // 拷贝赋值运算符(实现拷贝语义:释放this + 拷贝that)
    MemoryBlock& operator=(const MemoryBlock& that)
    {
        if (this != &that)
        {
            // 释放自身的资源
            delete[] _data;
            
            // 拷贝that对象所拥有的资源
            _length = that._length;
            _data = new int[_length];
            std::copy(that._data, that._data + _length, _data);
        }
        return *this;
    }
    
    // 移动构造器(实现移动语义:移动that)
    MemoryBlock(MemoryBlock&& that)
    // 将自身的资源指针指向that对象所拥有的资源
    : _length(that._length)
    , _data(that._data)
    {
        // 将that对象原本指向该资源的指针设为空值
        that._data = nullptr;
        that._length = 0;
    }
    
    // 移动赋值运算符(实现移动语义:释放this + 移动that)
    MemoryBlock& operator=(MemoryBlock&& that)
    {
        if (this != &that)
        {
            // 释放自身的资源
            delete[] _data;
            
            // 将自身的资源指针指向that对象所拥有的资源
            _data = that._data;
            _length = that._length;
            
            // 将that对象原本指向该资源的指针设为空值
            that._data = nullptr;
            that._length = 0;
        }
        return *this;
    }
private:
    size_t _length; // 资源的长度
    int* _data; // 指向资源的指针,代表资源本身
};

MemoryBlock f() { return MemoryBlock(50); }

执行下面的代码

const long long max = 100000;
    time_t timestamp1;
    time_t timestamp2;
    time_t timestamp3;

    time(&timestamp1);
    for (long long i = 0; i<max; i++)
    {
        MemoryBlock a = MemoryBlock(50);

        MemoryBlock c = std::move(a);
    }
    
    time(&timestamp2);
timestamp3 = timestamp2 - timestamp1;

如果把MemoryBlock c = std::move(a)换成MemoryBlock c = a;

性能上大概也有30%的差距。

 

这就是右值引用和移动构造语义带来的好处,我理解就是以前只能引用左值,而右值是不能引用的,新语法的加入实现了右值的引用,减少了零时对象的产生销毁,但是也带来了更多怪异的语法,明显增加了c++的学习成本,如果语法设计角度,像oc一样增加类似引用计数器来管理对象,会不会更加优雅一下,至少让上层的码农不会那么累,其实通智能指针也能达到同样的效果。

 

这么多年了c++都在做加法,让学习,使用成本太高了,标准委员为的大爷些什么时候考虑一下做点减法呢,不要让c++那么学院派或者满地都是陷阱,也不要让实现一种技术有10种方法,但是有5种都是陷阱。靠!

原文地址:https://www.cnblogs.com/budaixi/p/3860457.html