1. 程式人生 > >C++ 快速入門筆記:面向物件程式設計

C++ 快速入門筆記:面向物件程式設計

類 & 物件

  1. 類定義

    class Box
    {
       public:
          double length;   // Length of a box
          double breadth;  // Breadth of a box
          double height;   // Height of a box
    };
    
  2. 物件定義

    Box Box1;          // 宣告 Box1,型別為 Box
    Box Box2;          // 宣告 Box2,型別為 Box
    
  3. 用 . 運算子訪問資料成員

類成員函式

  1. 在類的外部使用範圍解析運算子 :: 定義函式

    double Box::getVolume(void)
    {
        return length * breadth * height;
    }
    

類訪問修飾符

  1. public
    公有成員在程式中類的外部是可訪問的,可以不使用任何成員函式來設定和獲取公有變數的值。
  2. protected
    私有成員變數或函式在類的外部是不可訪問的,甚至是不可檢視的,只有類和友元函式可以訪問私有成員。
  3. private
    保護成員變數或函式與私有成員十分相似,但有一點不同,保護成員在派生類(即子類)中是可訪問的。

建構函式 & 解構函式

  1. 類的建構函式是類的一種特殊的成員函式,它會在每次建立類的新物件時執行。建構函式的名稱與類的名稱是完全相同的,並且不會返回任何型別,也不會返回 void 。建構函式可用於為某些成員變數設定初始值。

  2. 使用初始化列表來初始化欄位

    Line::Line( double len): length(len)
    {
        cout << "Object is being created, length = " << len << endl;
    }
    

    等價於

    Line::Line( double len)
    {
        cout << "Object is being created, length = " << len << endl;
        length = len;
    }
    
  3. 類的解構函式是類的一種特殊的成員函式,它會在每次刪除所建立的物件時執行。解構函式的名稱與類的名稱是完全相同的,只是在前面加了個波浪號(~)作為字首,它不會返回任何值,也不能帶有任何引數。解構函式有助於在跳出程式(比如關閉檔案、釋放記憶體等)前釋放資源。

拷貝建構函式

  1. 拷貝建構函式是一種特殊的建構函式,它在建立物件時,是使用同一類中之前建立的物件來初始化新建立的物件。拷貝建構函式通常用於:

    • 通過使用另一個同類型的物件來初始化新建立的物件。
    • 複製物件把它作為引數傳遞給函式。
    • 複製物件,並從函式返回這個物件。
  2. 如果類帶有指標變數,並有動態記憶體分配,則它必須有一個拷貝建構函式。

    Line::Line(const Line &obj)
    {
        cout << "Copy constructor allocating ptr." << endl;
        ptr = new int;
        *ptr = *obj.ptr; // copy the value
    }
    
    Line::~Line(void)
    {
        cout << "Freeing memory!" << endl;
        delete ptr;
    }
    

友元函式

  1. 類的友元函式是定義在類外部,但有權訪問類的所有私有(private)成員和保護(protected)成員。儘管友元函式的原型有在類的定義中出現過,但是友元函式並不是成員函式

  2. 友元可以是一個函式,該函式被稱為友元函式;友元也可以是一個類,該類被稱為友元類,在這種情況下,整個類及其所有成員都是友元。如果要宣告函式為一個類的友元,需要在類定義中該函式原型前使用關鍵字 friend 。

    #include <iostream>
    using namespace std;
    class Box
    {
        double width;
        public:
            friend void printWidth( Box box );
            void setWidth( double wid );
    };
    // 成員函式定義
    void Box::setWidth( double wid )
    {
        width = wid;
    }
    // 請注意:printWidth() 不是任何類的成員函式
    void printWidth( Box box )
    {
    /* 因為 printWidth() 是 Box 的友元,它可以直接訪問該類的任何成員 */
        cout << "Width of box : " << box.width <<endl;
    }
    // 程式的主函式
    int main( )
    {
        Box box;
    // 使用成員函式設定寬度
        box.setWidth(10.0);
    // 使用友元函式輸出寬度
        printWidth( box );
        return 0;
    }
    

行內函數

  1. C++ 行內函數是通常與類一起使用。如果一個函式是內聯的,那麼在編譯時,編譯器會把該函式的程式碼副本放置在每個呼叫該函式的地方。對行內函數進行任何修改,都需要重新編譯函式的所有客戶端,因為編譯器需要重新更換一次所有的程式碼,否則將會繼續使用舊的函式。
  2. 如果想把一個函式定義為行內函數,則需要在函式名前面放置關鍵字 inline,在呼叫函式之前需要對函式進行定義。如果已定義的函式多於一行,編譯器會忽略 inline 限定符。在類定義中的定義的函式都是行內函數,即使沒有使用 inline 說明符。

this 指標

  1. 在 C++ 中,每一個物件都能通過 this 指標來訪問自己的地址。this 指標是所有成員函式的隱含引數。因此,在成員函式內部,它可以用來指向呼叫物件。

指向類的指標

  1. 一個指向 C++ 類的指標與指向結構的指標類似,訪問指向類的指標的成員,需要使用成員訪問運算子 -> ,就像訪問指向結構的指標一樣。

類的靜態成員

  1. 我們可以使用 static 關鍵字來把類成員定義為靜態的。當我們宣告類的成員為靜態時,這意味著無論建立多少個類的物件,靜態成員都只有一個副本。
  2. 靜態成員在類的所有物件中是共享的。如果不存在其他的初始化語句,在建立第一個物件時,所有的靜態資料都會被初始化為零。我們不能把靜態成員放置在類的定義中,但是可以在類的外部通過使用範圍解析運算子 :: 來重新宣告靜態變數從而對它進行初始化。
  3. 靜態函式成員
    • 如果把函式成員宣告為靜態的,就可以把函式與類的任何特定物件獨立開來。靜態成員函式即使在類物件不存在的情況下也能被呼叫,靜態函式只要使用類名加範圍解析運算子 :: 就可以訪問。

繼承

  1. 基類 & 派生類
  • 一個類可以派生自多個類,這意味著,它可以從多個基類繼承資料和函式。定義一個派生類,我們使用一個類派生列表來指定基類。類派生列表以一個或多個基類命名,形式如下:

    class derived-class: access-specifier base-class
    

其中,訪問修飾符 access-specifier 是 public、protected 或 private 其中的一個,base-class 是之前定義過的某個類的名稱。如果未使用訪問修飾符 access-specifier,則預設為 private。

  1. 訪問控制和繼承

    • 派生類可以訪問基類中所有的非私有成員。因此基類成員如果不想被派生類的成員函式訪問,則應在基類中宣告為 private。
    • 我們可以根據訪問許可權總結出不同的訪問型別,如下所示:(表格炸了)
    訪問 public protected private
    同一個類 yes yes yes
    派生類 yes yes no
    外部的類 yes no no
    • 一個派生類繼承了所有的基類方法,但下列情況除外:
      • 基類的建構函式、解構函式和拷貝函式。
      • 基類的過載運算子。
      • 基類的友元函式。
  2. 繼承型別

    • 當一個類派生自基類,該基類可以被繼承為 public、protected 或 private 幾種型別。繼承型別是通過上面講解的訪問修飾符 access-specifier 來指定的。
    • 幾乎不使用 protected 或 private 繼承,通常使用 public 繼承。當使用不同型別的繼承時,遵循以下幾個規則:
      • 公有繼承(public):當一個類派生自公有基類時,基類的公有成員也是派生類的公有成員,基類的保護成員也是派生類的保護成員,基類的私有成員不能直接被派生類訪問,但是可以通過呼叫基類的公有和保護成員來訪問。
      • 保護繼承(protected): 當一個類派生自保護基類時,基類的公有和保護成員將成為派生類的保護成員。
      • 私有繼承(private):當一個類派生自私有基類時,基類的公有和保護成員將成為派生類的私有成員。
  3. 多繼承

    class <派生類名>:<繼承方式1><基類名1>,<繼承方式2><基類名2>,…
    {
        <派生類類體>
    };
    

過載運算子和過載函式

  1. C++ 允許在同一作用域中的某個函式和運算子指定多個定義,分別稱為函式過載和運算子過載。
  2. 當呼叫一個過載函式或過載運算子時,編譯器通過把您所使用的引數型別與定義中的引數型別進行比較,決定選用最合適的定義。選擇最合適的過載函式或過載運算子的過程,稱為過載決策
  3. 函式過載
    • 在同一個作用域內,可以宣告幾個功能類似的同名函式,但是這些同名函式的形式引數(指引數的個數、型別或者順序)必須不同。
  4. 運算子過載
    • 過載的運算子是帶有特殊名稱的函式,函式名是由關鍵字 operator 和其後要過載的運算子符號構成的。與其他函式一樣,過載運算子有一個返回型別和一個引數列表。

      Box operator+(const Box& b)
      {
           Box box;
           box.length = this->length + b.length;
           box.breadth = this->breadth + b.breadth;
           box.height = this->height + b.height;
           return box;
      }
      
      

多型

  1. C++ 多型意味著呼叫成員函式時,會根據呼叫函式的物件的型別來執行不同的函式。

  2. 靜態連結 / 靜態多型 / 早繫結

  3. 虛擬函式

    • 虛擬函式 是在基類中使用關鍵字 virtual 宣告的函式。在派生類中重新定義基類中定義的虛擬函式時,會告訴編譯器不要靜態連結到該函式。

    • 我們想要的是在程式中任意點可以根據所呼叫的物件型別來選擇呼叫的函式,這種操作被稱為動態連結,或後期繫結

      class Shape {
      protected:
          int width, height;
      public:
            Shape( int a=0, int b=0)
            {
               width = a;
               height = b;
            }
            virtual int area()
            {
               cout << "Parent class area :" <<endl;
               return 0;
            }
      };
      
  4. 純虛擬函式

    • 可能想要在基類中定義虛擬函式,以便在派生類中重新定義該函式更好地適用於物件,但是您在基類中又不能對虛擬函式給出有意義的實現,這個時候就會用到純虛擬函式。

      class Shape {
      protected:
          int width, height;
      public:
            Shape( int a=0, int b=0)
            {
               width = a;
               height = b;
            }
          // pure virtual function
          virtual int area() = 0;
      };
      
    • = 0 告訴編譯器,函式沒有主體,上邊的虛擬函式是純虛擬函式。

資料抽象

  1. 資料抽象是指,只向外界提供關鍵資訊,並隱藏其後臺的實現細節,即只表現必要的資訊而不呈現細節。
  2. 訪問標籤強制抽象
    • 使用訪問標籤來定義類的抽象介面。一個類可以包含零個或多個訪問標籤
      • 使用公共標籤定義的成員都可以訪問該程式的所有部分。一個型別的資料抽象檢視是由它的公共成員來定義的。
      • 使用私有標籤定義的成員無法訪問到使用類的程式碼。私有部分對使用型別的程式碼隱藏了實現細節。

介面

  1. 介面描述了類的行為和功能,而不需要完成類的特定實現。
  2. 如果類中至少有一個函式被宣告為純虛擬函式,則這個類就是抽象類。