你最喜歡的c++編程風格慣用法是什麼?
- 2019 年 12 月 16 日
- 筆記
你最喜歡的c++編程風格慣用法是什麼?
在stackoverflow上找到了一篇文章,寫的蠻好的,地址如下:
https://stackoverflow.com/questions/276173/what-are-your-favorite-c-coding-style-idioms#comment60171463_2034439
由於是英文的,且比較重要,於是總結成下面幾條!
- 1.類初始化列表
- 2.枚舉類替換命名空間
- 3.RAII(資源獲取即初始化)
- 4.copy and swap
- 5.pImpl(指針指向具體實現)
今天開始學習第一節類初始化列表,程式碼也是會放在《C++那些事》中,鏈接如下:
https://github.com/Light-City/CPlusPlusThings
初始化列表與賦值
- const成員的初始化只能在構造函數初始化列表中進行
- 引用成員的初始化也只能在構造函數初始化列表中進行
- 對象成員(對象成員所對應的類沒有默認構造函數)的初始化,也只能在構造函數初始化列表中進行
類之間嵌套
第一種:使用初始化列表。
class Animal { public: Animal() { std::cout << "Animal() is called" << std::endl; } Animal(const Animal &) { std::cout << "Animal (const Animal &) is called" << std::endl; } Animal &operator=(const Animal &) { std::cout << "Animal & operator=(const Animal &) is called" << std::endl; return *this; } ~Animal() { std::cout << "~Animal() is called" << std::endl; } }; class Dog { public: Dog(const Animal &animal) : __animal(animal) { std::cout << "Dog(const Animal &animal) is called" << std::endl; } ~Dog() { std::cout << "~Dog() is called" << std::endl; } private: Animal __animal; }; int main() { Animal animal; std::cout << std::endl; Dog d(animal); std::cout << std::endl; return 0; }
運行結果:
Animal() is called Animal (const Animal &) is called Dog(const Animal &animal) is called ~Dog() is called ~Animal() is called ~Animal() is called
依次分析從上到下:
main函數中Animal animal;
調用默認構造。
Dog d(animal);
等價於:
Animal __animal = animal;
實際上就是調用了拷貝構造,因此輸出了:
Animal (const Animal &) is called
再然後列印Dog的構造函數裡面的輸出。
最後調用析構,程式結束。
第二種:構造函數賦值來初始化對象。
構造函數修改如下:
Dog(const Animal &animal) { __animal = animal; std::cout << "Dog(const Animal &animal) is called" << std::endl; }
此時輸出結果:
Animal() is called Animal() is called Animal & operator=(const Animal &) is called Dog(const Animal &animal) is called ~Dog() is called ~Animal() is called ~Animal() is called
於是得出:
當調用Dog d(animal);
時,等價於:
先定義對象,再進行賦值,因此先調用了默認構造,再調用=操作符重載函數。
// 假設之前已經有了animal對象 Animal __animal; __animal = animal;
小結
通過上述我們得出如下結論:
- 類中包含其他自定義的class或者struct,採用初始化列表,實際上就是創建對象同時並初始化
- 而採用類中賦值方式,等價於先定義對象,再進行賦值,一般會先調用默認構造,在調用=操作符重載函數。
無默認構造函數的繼承關係中
現考慮把上述的關係改為繼承,並修改Animal與Dog的構造函數,如下程式碼:
class Animal { public: Animal(int age) { std::cout << "Animal(int age) is called" << std::endl; } Animal(const Animal & animal) { std::cout << "Animal (const Animal &) is called" << std::endl; } Animal &operator=(const Animal & amimal) { std::cout << "Animal & operator=(const Animal &) is called" << std::endl; return *this; } ~Animal() { std::cout << "~Animal() is called" << std::endl; } }; class Dog : Animal { public: Dog(int age) : Animal(age) { std::cout << "Dog(int age) is called" << std::endl; } ~Dog() { std::cout << "~Dog() is called" << std::endl; } };
上述是通過初始化列表給基類帶參構造傳遞參數,如果不通過初始化列表傳遞,會發生什麼影響?
去掉初始化列表
Dog(int age) { std::cout << "Dog(int age) is called" << std::endl; }
運行程式:
error: no matching function for call to 『Animal::Animal()』
由於在Animal中沒有默認構造函數,所以報錯,遇到這種問題屬於災難性的,我們應該盡量避免,可以通過初始化列表給基類的構造初始化。
類中const數據成員、引用數據成員
特別是引用數據成員,必須用初始化列表初始化,而不能通過賦值初始化!
例如:在上述的Animal中添加私有成員,並修改構造函數:
class Animal { public: Animal(int age,std::string name) { std::cout << "Animal(int age) is called" << std::endl; } private: int &age_; const std::string name_; };
報下面錯誤:
error: uninitialized reference member in 『int&』
應該改為下面:
Animal(int age, std::string name) : age_(age), name_(name) { std::cout << "Animal(int age) is called" << std::endl; }