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(×tamp1);
for (long long i = 0; i<max; i++) {
ss = s1 + s2 + s3 + s4;
}
time(×tamp2);
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(×tamp1);
for (long long i = 0; i<max; i++)
{
MemoryBlock a = MemoryBlock(50);
MemoryBlock c = std::move(a);
}
time(×tamp2);
timestamp3 = timestamp2 - timestamp1;
復制代碼
如果把MemoryBlock c = std::move(a)換成MemoryBlock c = a;
性能上大概也有30%的差距。
這就是右值引用和移動構造語義帶來的好處,我理解就是以前只能引用左值,而右值是不能引用的,新語法的加入實現了右值的引用,減少了零時對象的產生銷毀,但是也帶來了更多怪異的語法,明顯增加了c++的學習成本,如果語法設計角度,像oc一樣增加類似引用計數器來管理對象,會不會更加優雅一下,至少讓上層的碼農不會那麼累,其實通智能指針也能達到同樣的效果。
這麼多年了c++都在做加法,讓學習,使用成本太高了,標准委員為的大爺些什麼時候考慮一下做點減法呢,不要讓c++那麼學院派或者滿地都是陷阱,也不要讓實現一種技術有10種方法,但是有5種都是陷阱。靠!