現代C++如何高效地設計建構式
不知道啥是
std::move()的同學請簡單做一下課前預習,
文章目錄
- 現代C++如何高效地設計建構式
- 傳統手藝 const & 與 現代技術 std::move
- 分析
- 實驗
- 總結
傳統手藝 const & 與 現代技術 std::move
先從一個我們非常熟悉的情形入手,我們需要寫一個Student類,傳入引數為std::string,任何一名上課認真聽講的學生都會想到傳const&,
class Student {
public:
explicit Student(const std::string &name)
: _name(name) {}
private:
std::string _name;
};
讓我們拋開C++11后引入的lvalue rvalue 等繁瑣的概念,僅僅從兩個例子入手分析一下這種實作方式的開銷:
-
情景1:
std::string name("TKONIY"); // 呼叫一次建構式 string(const char*) Student stu(name); // _name(name)呼叫一次建構式 string(const string&)一共是兩次字串拷貝,
-
情景2:
Student stu("TKONIY"); // 構造const string& name 引數時呼叫一次建構式string(const char*) // _name(name)呼叫一次建構式 string(const string&)本質上和上面一樣,兩次字串拷貝,
在情景2中,一行這樣的代碼竟然需要拷貝兩次字串,這真是非常地糟糕,對性能追求有強迫癥的程式員會給Student類添加一個Student(const char* )型別的建構式以減少拷貝次數,但是事實上,C++11為我們提供了std::move用于解決此類問題,我們可以修改建構式為:
class Student {
public:
explicit Student(std::string name)
: _name(std::move(name)) {}
private:
std::string _name;
};
再次分析一下情景2:
- 構造引數
string name,呼叫建構式string(const char*) - 呼叫
std::move(name)將name轉換成右值, - 呼叫
string(string&&)構造,
在這個方法下,一共僅發生了一次拷貝,盡管std::move也有一定的開銷,但是相比于一個容器類的拷貝來說還是微不足道的,
分析
上面僅僅是舉了一個非常簡單的例子分析建構式這樣寫的好處,本質上,這種寫法的好處體現在建構式接受的引數存在移動建構式的情形下,比如上面情景2的高效關鍵在于呼叫了string(string&&)建構式,
實驗
下面我們做一組實驗來證明一下上面的結論并給出實踐的例子,
Article類如下:
class Article {
public:
Article(Text text)
: _text(std::move(text)) {}
private:
Text _text;
};
Text類沒有定義移動建構式,
執行以下代碼:class Text { private: char *_content; public: Text(const char *content) { _content = new char[std::strlen(content)]; std::strcpy(_content, content); std::cout << "拷貝一次1" << std::endl; } Text(const Text &text) { _content = new char[std::strlen(text._content)]; std::strcpy(_content, text._content); std::cout << "拷貝一次2" << std::endl; } };
輸出:Article article("C++ is the best language.");拷貝一次1 拷貝一次2Text類定義移動建構式,
執行以下代碼:class Text { private: char *_content; public: Text(const char *content) { _content = new char[std::strlen(content)]; std::strcpy(_content, content); std::cout << "拷貝一次1" << std::endl; } Text(const Text &text) { _content = new char[std::strlen(text._content)]; std::strcpy(_content, text._content); std::cout << "拷貝一次2" << std::endl; } Text(Text &&text) { _content = text._content; text._content = nullptr; std::cout << "移動一次" << std::endl; } };
輸出:Article article("C++ is the best language.");
驗證了我們的想法,拷貝一次1 移動一次
總結
- 我們通過實驗看到了
std::move在減少拷貝方面發揮的重要威力,事實上它作為實作zero copy理念的關鍵技術,用途遠遠不止一個建構式,然而,上面的建構式看起來很美好,但還是存在一些問題:- 使用情景1中的方式構造(即傳入左值)的時候,
std::move()并沒有減少拷貝次數甚至還徒增了一次移動的開銷,對于該問題一般解決方法是同時定義接收const&引數的建構式用于接收左值參考, - 引數必須實作了移動建構式(如上文中的
Text類),否則最終呼叫的還是拷貝建構式并且還徒增一次移動開銷, - 這不是唯一的實作方法,并且應對復雜情況時,需要熟練掌握左值右值語意、參考坍縮等概念以及
std::forward才能寫好一個這樣的建構式,
- 使用情景1中的方式構造(即傳入左值)的時候,
- 再往遠點講,
std::move很好用,但是它并不是一個強制性的東西,編譯器不能避免程式員做出在move了之后繼續訪問原物件等種種奇葩行為,有一個語言很好地發展了現代C++的這個優點,從語法層面提供了移動語意,這個語言叫rust, - 但是
C/C++依然是最好的語言,
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/262162.html
標籤:其他
上一篇:售后工程師
下一篇:Maven的環境搭建
