你最喜歡的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;  }