天天看點

【Effective C++】讓自己習慣C++

文章目錄

    • 一、視C++為一個語言聯邦
      • 1、C++不同于C的部分
      • 2、可以将C++分為4個層次
    • 二、盡量以const,enum,inline替換#define
      • 1、#define存在的問題
      • 2、const,enum,inline的好處
      • 3、請記住
    • 三、盡可能使用const
      • 1、const修飾變量
      • 2、const修飾函數參數
      • 3、const成員函數
      • 4、函數傳回const
      • 5、請記住
    • 四、确定對象使用前已被初始化
      • 1、成員初始化清單
      • 2、以local static對象替換non-local static對象
      • 3、請記住

一、視C++為一個語言聯邦

  C是面向過程的語言,它的側重點在于算法和資料結構。編寫C代碼,側重點在于通過輸入資料,設計一定的算法過程,得到輸出。C++是面向對象的語言,它的側重點在于抽象出對象模型,使這個模型和問題像契合。通過對對象狀态的控制,來解決問題。

1、C++不同于C的部分

  class:雖然C語言也有結構體struct,但是它更多的側重于資料機構,側重資料的組織。雖然struct在++中也支援class的個各種特性,但是很少用struct去替代class。它們兩個一個不同在于class預設成員的通路是private,而struct是public。

  template:模闆屬于泛型程式設計,泛型程式設計使得代碼獨立于特定的資料類型,可以大大減少代碼量。

  overload:重載是C語言中沒有的,在一些代碼中經常看到external “C",這是表示以C語言方式編譯。因為重載是通過編譯時,在函數明後加上函數參數類型來生成函數名實作的,而C語言則不是,是以如果要給C調用,就要加上extern “C”。

2、可以将C++分為4個層次

  • C:C++實在C語言的基礎上發展而來的。
  • Object-Oriented C++:這是C++中不同于C的部分,這裡主要指面向對象。
  • Template C++:C++中的泛型程式設計。
  • STL:這是一個标準模闆庫,它用模闆實作了很多容器、疊代器和算法,使用STL往往事半功倍。

二、盡量以const,enum,inline替換#define

1、#define存在的問題

【不容易定位錯誤】:

  在預處理時, 所有使用PI的地方都将被替換,之後編譯器在編譯時從未看到過PI。這時如果遇到錯誤,報錯時給出的是3.1415926,而不是PI,因為PI從未進入到符号表,這将導緻錯誤難以了解。一個替換的方法是如下定義:

【class專屬常量】:

  專屬于class作用域的常量。專屬于class的常量将這個常量限定在class的作用域内,而#define定義的常量沒有作用域的限制,一旦在某一處有個宏定義,在其後面都有效(除非#undef)。這意味着#define不僅不能夠用來定義class專屬常量,也不能提供任何封裝性。

class GamePlayer
{
	static const int NumTurns=5; // 所有執行個體共享一份
	int scores[NumTurns];
};
           

2、const,enum,inline的好處

  • const的好處:
    • define直接常量替換,出現編譯錯誤不易定位(不知道常量是哪個變量);
    • define沒有作用域,const有作用域提供了封裝性。
  • enum的好處:
    • 提供了封裝性;
    • 編譯器肯定不會配置設定額外記憶體空間(其實const也不會)。
  • inline的好處:
    • define宏函數容易造成誤用。

3、請記住

  • 對于單純常量,最好以const對象或者enum替換#define。
  • 對于形似函數的宏,最好改用inline函數替換#define。

三、盡可能使用const

1、const修飾變量

// 表示a是一個常量。
const T a; 
//表示a是一個指向常量的指針,*a是不能被修改的,但是a可以被修改。
const T *a; 
// 表示a是一個指向整數的常指針,a是不能被修改的,但是*a是可以被修改的。
T * const a; 
// 表示a是一個指向常量的常指針,*a是不能被修改的,a也是不能被修改的。
const T * const a; 
           

2、const修飾函數參數

  傳遞過來的參數在函數内不能改變,與修飾變量性質一樣。

void display(const int a, int b)
{
	a=5; // 錯誤。
}
           

3、const成員函數

  const成員函數的目的是确認該函數可以用到const對象上。const成員函數使得:

  • class接口更加容易了解,确認哪些接口可以修改class成員。
  • 使操作const對象成為可能。

  關于第2點,是因為const對象隻能調用const成員函數,但是非const對象既可以調用普通成員函數,也可以調用const成員函數。這是因為this指針可以轉換為const this,但是const this不能轉換為非const this。

【Note】:

(1)一個函數是不是const是可以被重載的。

(2)如果某些成員變量可能總是會被修改,即使是在const成員函數中,可以對這一類變量使用mutable關鍵字。

4、函數傳回const

  若函數的傳回值是指針,且用const修飾,則函數傳回值指向的内容是常數,不可被修改,此傳回值僅能指派給const修飾的相同類型的指針。令函數傳回一個常量值,往往可以降低客戶錯誤而造成的意外,又不至于放棄安全性和高效性。

Rational a, b, c;
(a * b) = c; // 避免無意義的指派操作。
           

5、請記住

  • 将某些東西聲明為const可幫助編譯器偵測出錯誤用法。const可被施加于任何作用域内的對象、函數參數、函數傳回類型、成員函數本體。
  • 編譯器強制實施bitwise constness,但你編寫程式時應該使用“概念上的常量性”(conceptual constness)。
  • 當const 和non-const成員函數有着實質等價的實作時,令non-const版本調用const版本可避免代碼重複。

四、确定對象使用前已被初始化

1、成員初始化清單

  在一些語境下會初始化為0,但在另一些語境下可能就不會初始化,例如:

class Point
{
	int x, y;
}
           

  在構造函數中完成初始化時,要區分指派和初始化。在構造函數體内的是指派,在初始化清單中的才是初始化。

class Point
{
public:
	Point(int x_, int y_):x(x_), y(y_) { }
private:
	int x, y;
}
           

  初始化效率往往高于指派。指派是先定義變量,在定義的時候可能已經調用了變量的構造函數,之後指派是調用了指派操作符;而初始化是直接調用了複制構造函數。

【Note】:

(1)初始化順序要和聲明順序一緻。

(2)在一些情況下必須使用初始化方式,有些周遊在定義時就要求初始化,例如const和引用。

2、以local static對象替換non-local static對象

  将每個non-local static對象搬到自己的專屬函數内(該對象在此函數内被聲明為static)。這些函數傳回一個reference指向它所含的對象。然後使用者調用這些函數,而不是直接指涉這些對象。換句話說,non-local static對象被local static對象替換了。

class FileSystem{
  /..../
  std::size_t numDisks() const;   
  /..../
};

FileSystem& tfs()
{
    static FileSystem fs;
    return fs;
}

class Directory {   
   /..../
   Directory (params);
   /..../
};

Directory::Directory(params)
{
     /..../
     // 如果是tfs.numDisks()則無法保證tfs在tempDir之前被初始化。
     std::size_t disks = tfs().numDisks();
     /..../
}

Directory& tempDir()
{
    static Directory td;
    return td;
}
           

3、請記住

  • 為内置型對象進行手工初始化,因為C++不保證初始化它們(C part of C++)。
  • 構造函數最好使用成員初始化清單,而不要在構造函數本體内使用指派操作。初值列列出的成員變量,其排列次序應該和它們在class中的聲明次序相同。
  • 為避免“跨編譯單元之初始化次序”問題,請以local static 對象替換 non-local static對象。

繼續閱讀