按值傳遞的意義是什么??
當一個函數的參數按值傳遞時,這就會進行拷貝。當然,編譯器懂得如何去拷貝。?
而對于我們自定義的類型,我們也許需要提供拷貝構造函數。
但是不得不說,拷貝的代價是昂貴的。
所以我們需要尋找一個避免不必要拷貝的方法,即C++11提供的移動語義。?
上一篇博客中有一個句話用到了:
~~~
#include <iostream>
void f(int& i) { std::cout << "lvalue ref: " << i << "\n"; }
void f(int&& i) { std::cout << "rvalue ref: " << i << "\n"; }
int main()
{
int i = 77;
f(i); // lvalue ref called
f(99); // rvalue ref called
f(std::move(i)); // 稍后介紹
return 0;
}
~~~
實際上,右值引用主要用于創建移動構造函數和移動賦值運算。
移動構造函數類似于拷貝構造函數,把類的實例對象作為參數,并創建一個新的實例對象。?
但是 移動構造函數可以避免**內存的重新分配**,因為我們知道右值引用提供了一個暫時的對象,而不是進行copy,所以我們可以進行移動。
換言之,在設計到關于臨時對象時,右值引用和移動語義允許我們避免不必要的拷貝。我們不想拷貝將要消失的臨時對象,所以這個臨時對象的資源可以被我們用作于其他的對象。
右值就是典型的臨時變量,并且他們可以被修改。如果我們知道一個函數的參數是一個右值,我們可以把它當做一個臨時存儲。這就意味著我們要移動而不是拷貝右值參數的內容。這就會節省很多的空間。
說多無語,看代碼:
~~~
#include <iostream>
#include <algorithm>
class A
{
public:
// Simple constructor that initializes the resource.
explicit A(size_t length)
: mLength(length), mData(new int[length])
{
std::cout << "A(size_t). length = "
<< mLength << "." << std::endl;
}
// Destructor.
~A()
{
std::cout << "~A(). length = " << mLength << ".";
if (mData != NULL) {
std::cout << " Deleting resource.";
delete[] mData; // Delete the resource.
}
std::cout << std::endl;
}
// Copy constructor.
A(const A& other)
: mLength(other.mLength), mData(new int[other.mLength])
{
std::cout << "A(const A&). length = "
<< other.mLength << ". Copying resource." << std::endl;
std::copy(other.mData, other.mData + mLength, mData);
}
// Copy assignment operator.
A& operator=(const A& other)
{
std::cout << "operator=(const A&). length = "
<< other.mLength << ". Copying resource." << std::endl;
if (this != &other) {
delete[] mData; // Free the existing resource.
mLength = other.mLength;
mData = new int[mLength];
std::copy(other.mData, other.mData + mLength, mData);
}
return *this;
}
// Move constructor.
A(A&& other) : mData(NULL), mLength(0)
{
std::cout << "A(A&&). length = "
<< other.mLength << ". Moving resource.\n";
// Copy the data pointer and its length from the
// source object.
mData = other.mData;
mLength = other.mLength;
// Release the data pointer from the source object so that
// the destructor does not free the memory multiple times.
other.mData = NULL;
other.mLength = 0;
}
// Move assignment operator.
A& operator=(A&& other)
{
std::cout << "operator=(A&&). length = "
<< other.mLength << "." << std::endl;
if (this != &other) {
// Free the existing resource.
delete[] mData;
// Copy the data pointer and its length from the
// source object.
mData = other.mData;
mLength = other.mLength;
// Release the data pointer from the source object so that
// the destructor does not free the memory multiple times.
other.mData = NULL;
other.mLength = 0;
}
return *this;
}
// Retrieves the length of the data resource.
size_t Length() const
{
return mLength;
}
private:
size_t mLength; // The length of the resource.
int* mData; // The resource.
};
~~~
**移動構造函數**?
語法:
~~~
A(A&& other) noexcept // C++11 - specifying non-exception throwing functions
{
mData = other.mData; // shallow copy or referential copy
other.mData = nullptr;
}
~~~
最主要的是沒有用到新的資源,是移動而不是拷貝。?
假設一個地址指向了一個有一百萬個int元素的數組,使用move構造函數,我們沒有創造什么,所以代價很低。
~~~
// Move constructor.
A(A&& other) : mData(NULL), mLength(0)
{
// Copy the data pointer and its length from the
// source object.
mData = other.mData;
mLength = other.mLength;
// Release the data pointer from the source object so that
// the destructor does not free the memory multiple times.
other.mData = NULL;
other.mLength = 0;
}
~~~
**移動比拷貝更快!!!**
**移動賦值運算符**?
語法:
~~~
A& operator=(A&& other) noexcept
{
mData = other.mData;
other.mData = nullptr;
return *this;
}
~~~
工作流程這樣的:Google上這么說的:
Release any resources that *this currently owns.?
Pilfer other’s resource.?
Set other to a default state.?
Return *this.
~~~
// Move assignment operator.
A& operator=(A&& other)
{
std::cout << "operator=(A&&). length = "
<< other.mLength << "." << std::endl;
if (this != &other) {
// Free the existing resource.
delete[] mData;
// Copy the data pointer and its length from the
// source object.
mData = other.mData;
mLength = other.mLength;
// Release the data pointer from the source object so that
// the destructor does not free the memory multiple times.
other.mData = NULL;
other.mLength = 0;
}
return *this;
}
~~~
讓我們看幾個move帶來的好處吧!?
vector眾所周知,C++11后對vector也進行了一些優化。例如vector::push_back()被定義為了兩種版本的重載,一個是cosnt T&左值作為參數,一個是T&&右值作為參數。例如下面的代碼:
~~~
std::vector<A> v;
v.push_back(A(25));
v.push_back(A(75));
~~~
上面兩個push_back()都會調用push_back(T&&)版本,因為他們的參數為右值。這樣提高了效率。
而 當參數為左值的時候,會調用push_back(const T&) 。
~~~
#include <vector>
int main()
{
std::vector<A> v;
A aObj(25); // lvalue
v.push_back(aObj); // push_back(const T&)
}
~~~
但事實我們可以使用 static_cast進行強制:
~~~
// calls push_back(T&&)
v.push_back(static_cast<A&&>(aObj));
~~~
我們可以使用std::move完成上面的任務:
~~~
v.push_back(std::move(aObj)); //calls push_back(T&&)
~~~
似乎push_back(T&&)永遠是最佳選擇,但是一定要記住:?
push_back(T&&) 使得參數為空。如果我們想要保留參數的值,我們這個時候需要使用拷貝,而不是移動。
最后寫一個例子,看看如何使用move來交換兩個對象:
~~~
#include <iostream>
using namespace std;
class A
{
public:
// constructor
explicit A(size_t length)
: mLength(length), mData(new int[length]) {}
// move constructor
A(A&& other)
{
mData = other.mData;
mLength = other.mLength;
other.mData = nullptr;
other.mLength = 0;
}
// move assignment
A& operator=(A&& other) noexcept
{
mData = other.mData;
mLength = other.mLength;
other.mData = nullptr;
other.mLength = 0;
return *this;
}
size_t getLength() { return mLength; }
void swap(A& other)
{
A temp = move(other);
other = move(*this);
*this = move(temp);
}
int* get_mData() { return mData; }
private:
int *mData;
size_t mLength;
};
int main()
{
A a(11), b(22);
cout << a.getLength() << ' ' << b.getLength() << endl;
cout << a.get_mData() << ' ' << b.get_mData() << endl;
swap(a,b);
cout << a.getLength() << ' ' << b.getLength() << endl;
cout << a.get_mData() << ' ' << b.get_mData() << endl;
return 0;
}
~~~
- 前言
- 吐血整理C++11新特性
- C++11新特性之std::function
- c++11特性之正則表達式
- c++11特性之Lambda表達式
- c++11特性之override和final關鍵字
- c++11特性之std::thread--初識
- c++11特性之std::thread--初識二
- c++11特性之initializer_list
- c++11特性之std::thread--進階
- c++11特性之std::thread--進階二
- C++11新特性之 CALLBACKS
- C++11新特性之 std::array container
- C++11新特性之 nullptr
- C++11新特性之 rvalue Reference(右值引用)
- C++11新特性之 Move semantics(移動語義)
- C++11新特性之 default and delete specifiers
- C++11新特性之 Static assertions 和constructor delegation
- 開始使用C++11的幾個理由
- C++11新特性之 std::future and std::async