午夜视频在线网站,日韩视频精品在线,中文字幕精品一区二区三区在线,在线播放精品,1024你懂我懂的旧版人,欧美日韩一级黄色片,一区二区三区在线观看视频

分享

(原創(chuàng))C++11改進我們的程序之move和完美轉(zhuǎn)發(fā)

 quasiceo 2013-10-18

(原創(chuàng))C++11改進我們的程序之move和完美轉(zhuǎn)發(fā)

  本次要講的是右值引用相關(guān)的幾個函數(shù):std::move, std::forward和成員的emplace_back,通過這些函數(shù)我們可以避免不必要的拷貝,提高程序性能。move是將對象的狀態(tài)或者所有權(quán)從一個對象轉(zhuǎn)移到另一個對象,只是轉(zhuǎn)移,沒有內(nèi)存的搬遷或者內(nèi)存拷貝。如圖所示是深拷貝和move的區(qū)別。


  這種移動語義是很有用的,比如我們一個對象中有一些指針資源或者動態(tài)數(shù)組,在對象的賦值或者拷貝時就不需要拷貝這些資源了。在c++11之前我們的拷貝構(gòu)造函數(shù)和賦值函數(shù)可能要這樣定義:
假設(shè)一個A對象內(nèi)部有一個資源m_ptr;

A& A::operator=(const A& rhs)
{
// 銷毀m_ptr指向的資源
// 復(fù)制rhs.m_ptr所指的資源,并使m_ptr指向它
}

同樣A的拷貝構(gòu)造函數(shù)也是這樣。假設(shè)我們這樣來用A:

A foo(); // foo是一個返回值為X的函數(shù)
A a;
a = foo();

最后一行有如下的操作:

  • 銷毀a所持有的資源
  • 復(fù)制foo返回的臨時對象所擁有的資源
  • 銷毀臨時對象,釋放其資源

  上面的過程是可行的,但是更有效率的辦法是直接交換a和臨時對象中的資源指針,然后讓臨時對象的析構(gòu)函數(shù)去銷毀a原來擁有的資源。換句話說,當(dāng)賦值操作符的右邊是右值的時候,我們希望賦值操作符被定義成下面這樣:

A& A::operator=(const A&& rhs)
{
// 僅僅轉(zhuǎn)移資源的所有者,將資源的擁有者改為被賦值者
}

  這就是所謂的move語義。再看一個例子,假設(shè)一個臨時容器很大,賦值給另一個容器。

復(fù)制代碼
{
std::list< std::string > tokens;//省略初始化...
std::list< std::string > t = tokens;
}
std::list< std::string > tokens;
std::list< std::string > t = std::move(tokens);
復(fù)制代碼

  如果不用std::move,拷貝的代價很大,性能較低。使用move幾乎沒有任何代價,只是轉(zhuǎn)換了資源的所有權(quán)。如果一個對象內(nèi)部有較大的對內(nèi)存或者動態(tài)數(shù)組時,很有必要寫move語義的拷貝構(gòu)造函數(shù)和賦值函數(shù),避免無謂的深拷貝,以提高性能。

完美轉(zhuǎn)發(fā)

  在上一篇的博文中我介紹了右值引用,右值引用類型是獨立于值的,一個右值引用參數(shù)作為函數(shù)的形參,在函數(shù)內(nèi)部再轉(zhuǎn)發(fā)該參數(shù)的時候它已經(jīng)變成一個左值了,并不是它原來的類型了。因此,我們需要一種方法能按照參數(shù)原來的類型轉(zhuǎn)發(fā)到另一個函數(shù),這種轉(zhuǎn)發(fā)被稱為完美轉(zhuǎn)發(fā)。所謂完美轉(zhuǎn)發(fā)(perfect forwarding),是指在函數(shù)模板中,完全依照模板的參數(shù)的類型,將參數(shù)傳遞給函數(shù)模板中調(diào)用的另外一個函數(shù)。c++11中提供了這樣的一個函數(shù)std::forward,它是為轉(zhuǎn)發(fā)而生的,它會按照參數(shù)本來的類型來轉(zhuǎn)發(fā)出去,不管參數(shù)類型是T&&這種未定的引用類型還是明確的左值引用或者右值引用??纯催@個例子。

復(fù)制代碼
template<typename T>
void PrintT(T& t)
{
cout << "lvaue" << endl;
}

template<typename T>
void PrintT(T && t)
{
cout << "rvalue" << endl;
}

template<typename T>
void TestForward(T && v)
{
PrintT(v);
PrintT(std::forward<T>(v));
PrintT(std::move(v));
}

Test()
{
TestForward(1);
int x = 1;
TestForward(x);
TestForward(std::forward<int>(x));
}
復(fù)制代碼

  測試結(jié)果:


  我們來分析一下測試結(jié)果:

  • TestForward(1);由于1是右值,所以未定的引用類型T && v被一個右值初始化后變成了一個右值引用,但是在TestForward函數(shù)體內(nèi)部,調(diào)用PrintT(v);時,v又變成了一個左值,因為它這里已經(jīng)變成了一個具名的變量,所以它是一個左值,因此第一個PrintT被調(diào)用,打印出"lvaue";PrintT(std::forward<T>(v));由于std::forward會按參數(shù)原來的類型轉(zhuǎn)發(fā),因此,這時它還是一個右值(這里已經(jīng)發(fā)生了類型推導(dǎo),所以這里的T&&不是一個未定的引用類型,關(guān)于這點可以參考我的上一篇講右值引用的博文),所以會調(diào)用void PrintT(T &&t)函數(shù)。PrintT(std::move(v));是將v變成一個右值引用,雖然它本來也是右值引用,因此它和PrintT(std::forward<T>(v));的輸出結(jié)果是一樣的。
  • TestForward(x);未定的引用類型T && v被一個左值初始化后變成了一個左值引用,因此在調(diào)用PrintT(std::forward<T>(v));它會轉(zhuǎn)發(fā)到void PrintT(T& t);

萬能的函數(shù)包裝器

  右值引用、完美轉(zhuǎn)發(fā)再結(jié)合可變模板參數(shù),我們可以寫一個萬能的函數(shù)包裝器,它可以接收所有的函數(shù),帶返回值的、不帶返回值的、帶參數(shù)的和不帶參數(shù)的函數(shù)都可以委托這個萬能的函數(shù)包裝器執(zhí)行??纯催@個萬能的函數(shù)包裝器。

復(fù)制代碼
template<class Function, class... Args>
inline auto FuncWrapper(Function && f, Args && ... args) -> decltype(f(std::forward<Args>(args)...))
{
//typedef decltype(f(std::forward<Args>(args)...)) ReturnType;
return f(std::forward<Args>(args)...);
//your code; you can use the above typedef.
}
復(fù)制代碼

再看看測試代碼:

復(fù)制代碼
void test0()
{
cout << "void" << endl;
}

int test1()
{
return 1;
}

int test2(int x)
{
return x;
}

string test3(string s1, string s2)
{
return s1 + s2;
}

test()
{
FuncWrapper(test0);  //沒有返回值,打印1
FuncWrapper(test1); //返回1
FuncWrapper(test2, 1); //返回1
FuncWrapper(test3, "aa", "bb"); //返回"aabb"
}
復(fù)制代碼

成員的emplace_back

  c++11中大部分容器都加了一個emplace_back成員函數(shù),vector中它的定義是這樣的:

template< class... Args >
void emplace_back( Args&&... args );

  這里的Args&&是一個未定的引用類型,因此它可以接收左值引用和右值引用,它的內(nèi)部也是調(diào)用了std::forward實現(xiàn)完美轉(zhuǎn)發(fā)的。因此如果我們需要往容器中添加右值、臨時變量時,用emplace_back可以提高性能。

c++11 boost技術(shù)交流群:296561497,歡迎大家來交流技術(shù)。

    本站是提供個人知識管理的網(wǎng)絡(luò)存儲空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點。請注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購買等信息,謹防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請點擊一鍵舉報。
    轉(zhuǎn)藏 分享 獻花(0

    0條評論

    發(fā)表

    請遵守用戶 評論公約

    類似文章 更多