1. 程式人生 > >C++進階--類的繼承

C++進階--類的繼承

//############################################################################
/*
 * 公有,保護,私有繼承
 */

class B { 
};
class D_priv : private   B { };        //私有繼承
class D_prot : protected B { };     //保護繼承
class D_pub : public    B { };        //公有繼承


/*
不同的繼承方法指定了派生類對基類不同的訪問控制權限

上述三個派生類:
1. 都不能訪問B中的私有成員. 
2. D_pub繼承B中的公有成員為公有,繼承B中的保護成員為保護
3. D_priv繼承B中的公有保護成員為私有
4. D_prot繼承B中的公有保護成員為保護

轉換:
1. 任何人都可以將一個D_pub*轉換為B*. D_pub是B的特殊情況
2. D_priv的成員和友元可以將D_priv*轉成B*
3. D_prot的成員,友元及子女可以將D_prot*轉成B*

注意:只有公有繼承是is-a的關係
*/

/* 詳細例項 */

class B { 
   public:
      void f_pub() { cout << "f_pub is called.\n"; }
   protected:
      void f_prot() { cout << "f_prot is called.\n"; }
   private:
      void f_priv() { cout << "f_priv is called.\n"; }
};

class D_pub : public B {  //對於公有繼承
   public:
      void f() { 
         f_pub();   // OK. D_pub的公有成員函式
         f_prot();  // OK. D_pub的保護成員函式
         f_priv();  // Error. B的私有成員函式 
      }
};

class D_priv : private   B { //對於私有繼承
   public:
   using B::f_pub; //使其在D_priv的作用域內可見
      void f() {
         f_pub();   // OK. D_priv的私有成員函式
         f_prot();  // OK. D_priv 的私有成員函式
         f_priv();  // Error. B的私有成員函式 
      }
};

int main() {
   D_pub D1;
   D1.f_pub();  // OK. f_pub()是D_pub的公有成員函式

   D_priv D2;
   D2.f_pub();  // Error. f_pub()是D_priv的私有成員函式,增加using B::f_pub之後OK

   B* pB = &D1;  // OK  可以轉換
   pB = &D2;     // Error  不能轉換
   ...
}

//############################################################################
/*
 * 私有繼承: 類似於has-a關係,跟組合類似
 */
class Ring {
   virtual tremble();
   public:
   void tinkle() {...; tremble(); }
};

/* 組合 */
class Dog {
   Ring m_ring;
   public:
   void tinkle() {m_ring.tinkle();}  // 向前呼叫
};


/* 私有繼承*/
class Dog_Priv : private Ring {
   public:
   using Ring::tinkle;
};

/*
 * 組合的優點: 比如可以有多個ring,ring可以切換 //通常情況下傾向於組合,更低耦合,更靈活
 * 私有繼承的優點:更優雅的多型,比如
 *
 * 在ring類中增加虛擬函式tremble(), 該函式在tinkle中被呼叫
 */



/*
 * 公有繼承 => "is-a" 關係
 *
 * 基類能做的任何事情,派生類需要也能做
 */

//像以下類的設計就是不合適的
class Bird { 
   public:
   void fly();
};

class Penguin : public Bird {};

Penguin p;
p.fly();


// class flyableBird : public bird {};
//   public:
//      void fly();
//penguin p;
//p.fly();


// 看幾個例子
class Dog {
   public:
      void bark() { cout << "Whoof, I am just a dog.\n";};
};

class Yellowdog : public Dog{
   public:
      void bark() { cout << "Whoof, I am a yellow dog.\n";};
};

int main() {
   Yellowdog* py = new Yellowdog();
   py->bark(); 
   Dog* pd = py;
   pd->bark(); 
}

OUTPUT:
Whoof, I am a yellow dog.
Whoof, I am just a dog.
/*
 * 結論:不要覆寫非虛擬函式
 */


class Dog {
   public:
   void bark(int age) { cout << "I am " << age; }
   virtual void bark(string msg = "just a" ) { 
      cout << "Whoof, I am " << msg << " dog." << endl; }
};

class Yellowdog : public Dog {
   public:
   using Dog::bark;
   virtual void bark(string msg="a yellow" ) { 
      cout << "Whoof, I am " << msg << " dog." << endl; }
};

int main() {
   Yellowdog* py = new Yellowdog();
   py->bark(5); 
}

OUTPUT:
Whoof, I am a yellow dog.
Whoof, I am just a dog.

/*
 * 不要重新定義虛擬函式的預設引數
 *   - 預設引數是靜態繫結的
 *   - 虛擬函式是動態繫結的
 */


/*
 * 在類dog中增加如下函式:
 * virtual void bark(int age) { cout << "I am " << age << " years old"<< endl; }
 * in main(),
 *    py->bark(5);  // 編譯不過
 *                  // 可以通過在yellowdog中加"using Dog::bark;"修復  為了保持is-a關係
 */


// 防止意外覆寫,或者沒有覆寫,增加了override關鍵字
class Dog {
   public:
      virtual void bark() { cout << "I am just a dog.\n";};
      void run();
};

class Yellowdog : public Dog{
   public:
      virtual void barks() { cout << "I am a yellow dog.\n";}; //在舊標準中不會報錯,等出錯了除錯比較困難
};


// C++ 11 standard:
class Yellowdog : public Dog{
   public:
      virtual void barks() override;       
            // 編譯錯誤:沒有覆寫的函式

      virtual void bark() const override;  
            // 編譯錯誤:沒有覆寫的函式

      void run() override;  // 壓根不是虛擬函式,錯誤
};


/* 
 * 避免採坑:
 * 1. 類的精確定義
 * 2. 不要覆寫非虛擬函式
 * 3. 不要覆寫虛擬函式的預設引數
 * 4. 強制繼承被遮蓋的函式
 * 5. 小心函式覆寫時的錯字
 */