C++ 中的繼承有 3 種方式,分別是 public、protected 和 private,這三種方式分別對應不一樣的父類成員的訪問權限,總結以下:java
這 4 條規則實際上只有第 2 條是經常使用的,下面說下這 3 種做用域的使用場景。ios
咱們常常會將子對象強制轉換(casting)爲父對象,而 public 繼承在這種強制轉換的場景下是無障礙的,這種狀況下,子類對象能夠理解爲一種特殊的父類對象,即它們是一種 is-a 的關係;除此以外,其餘的由 protected 或 private 做用域繼承而來的對象就不具有這樣的關係,下面是一個簡單的例子:spa
#include <iostream>
using namespace std;
class B {
private:
int val_;
public:
B(int val) : val_(val) {}
void print_val() { cout << "val_ = " << val_ << endl; }
};
class D_pub : public B {
public:
D_pub(int val)
: B(val)
{}
};
class D_pro : protected B {
public:
D_pro(int val)
: B(val)
{}
};
int main() {
D_pub pub(1);
B* b = &pub;
b->print_val();
D_pro dpro(1);
B* b2 = &dpro; // error: 'B' is an inaccessible base of 'D_pro'
}
複製代碼
上面例子中,類 B 是一個基類,D_pub 是一個使用 public 做用域的子類,而 D_pro 是使用 protected 做用域的子類,咱們在 main 中分別建立 D_pub 的對象 pub 和 D_pro 的對象 dpro,並分別賦值給父類指針,能夠看到,將 D_pro 對象賦值給父類指針的語句報編譯錯誤,緣由在於類 B 中的可訪問成員在 D_pro 中變成了不可訪問成員,即 D_pro 對象再也不是一個特殊意義的 B 對象,它們之間不具有 is-a 關係。以此類推,private 繼承的子類和父類也沒有 is-a 關係。指針
protected 和 private 繼承相似於組合模式(composition),它是一種 has-a 關係,咱們看一個組合模式的例子:code
class hat {
public:
void wear() {}
};
class child {
hat h_;
public:
void hat_wear() { h_.wear(); }
};
複製代碼
上面的代碼中,child 類是以將 hat 組合進來的方式實現的,即讓 child 類也具有 hat 的方法,一種很好的辦法是將 hat 做爲 child 的一個成員,從語義上,child 和 hat 具有 has-a 的關係。下面咱們看使用 protected 或 private 繼承如何實現 has-a 的關係:對象
class child : private hat {
public:
using hat::wear; // 此時 child 對象就能夠調用 hat::wear 方法了
};
int main() {
child c;
c.wear();
}
複製代碼
咱們將 child 以 private 的方式繼承自 hat,並將 hat::wear
方法放置在 child 的 public 做用域中,這樣 child 就「擁有了 hat 的能力」,它們之間也是一種 has-a 關係。繼承
雖然不一樣的實現達到了相同的效果,但依然不建議使用 private 或 protected 的方式實現 has-a 的關係,而建議更多的使用組合模式,一是由於組合模式更爲直觀,其二是由於組合模式將組合的多個對象解耦(它們沒有多一層繼承關係),其三是組合模式更爲靈活,試想一個類有多個組合對象的狀況。作用域
以上,咱們介紹了 C++ 中繼承的三種做用域,此時咱們須要記住 4 個規則:get
以及 2 個使用場景:it
參考:Advanced C++: Inheritance - Public, Protected, and Private