C++繼承(基礎)

賦值兼容規則

      public繼承方式下,派生類對象可以賦值給基類的對象/指針/引用,但是基類不能賦值給派生類。

  • 可以將派生類的對象看成是基類的對象   
  • 在使用基類對象的位置,都可以使用派生類對象進行替換
  • 基類的指針/引用可以指向派生類的對象

派生類的默認成員函數

若基類的構造函數存在:

  1. 基類爲缺省的構造函數:如果派生類沒有顯式定義構造函數,編譯器就生成一個默認的構造函數,要在生成默認的構造函數的初始化列表位置調用基類的構造函數,來完成基類成員的初始化。
  2. 基類爲非缺省的構造函數:此時用戶必須顯式給派生類定義構造函數(否則不會通過編譯),並且在其構造函數初始化列表的位置顯式調用基類的構造函數,來完成基類成員的初始化。
  • 派生類的構造函數的初始化列表可以包含基類的構造函數、派生類成員的初始化,但是不能有基類成員的初始化!

        (因爲構造函數是不可繼承的。所以派生類的構造函數必須通過調用基類的構造函數初始化基類成員)

        若派生類中包含對象成員,則派生類的構造函數初始化成員列表中既要列出 基類的構造函數 也要列出 對象的構造函數。派生類定義對象時,先調用基類的構造函數,再調用對象的構造函數,最後調用派生類的構造函數。

  • 派生類對象構造的次序先完成對基類成員的構造,再初始化派生類自己的成員

        創建派生類對象時,先調用派生類的構造函數,在構造函數的初始化列表位置完成基類成員的初始化,調用基類的構造函數,然後初始化派生類自己的成員執行初始化語句,再來執行派生類構造函數的函數體。
        

  •  派生類對象析構的次序:先調用派生類析構再調基類的析構

        析構派生類的對象時,先調用派生類的析構函數,編譯器會在派生類析構函數被調用完成後自動調用基類的析構函數清理基類成員。因爲這樣才能保證派生類 對象先清理派生類成員再清理基類成員的順序。


  • C++11中防止類被繼承,在類名後加 final  將基類構造函數私有化
  • 友元關係不能被繼承,也就是說基類友元不能訪問子類私有和保護成員
  • 靜態成員:可以繼承,並且整個繼承體系中只有一份。

 

常見面試題:實現一個不能被繼承的類。

// C++98中構造函數私有化,派生類中調不到基類的構造函數,則無法繼承。
class NonInherit
{
public:
 static NonInherit GetInstance()
 {
 return NonInherit();
 }
private:
 NonInherit()
 {}
};


// C++11給出了新的關鍵字final禁止繼承
class NonInherit final
{};

 


同名隱藏

定義:基類和派生類中具有相同名稱的成員(成員變量/成員函數), 派生類會自動屏蔽基類中此成員,而優先訪問派生類自己的成員,基類同名成員就被隱藏了。同名隱藏分爲兩類:類成員隱藏類成員函數隱藏。

  • 類成員隱藏:派生類和基類中有同名成員變量,派生類成員將屏蔽基類對同名成員的直接訪問,這種情況叫隱藏,也叫重定義。
//A中的_a與B中的_a構成隱藏關係
class A
{
public:
	void fun()
	{
		cout << "func()" << endl;
	}
public:
	int _a = 5;
};
class B : public A
{
public:
	void fun(int i)
	{
		A::fun();
		cout << "func(int i)->" << i << endl;
	}
public:
	int _a = 10;
};
void Test()
{
	B b;
	cout << b._a << endl;
};

運行程序:

   

由這個例子我們發現:派生類成員B在訪問與基類成員A同名的成員_a時,直接訪問了自己的類成員_a,而屏蔽了基類對同名成員的直接訪問,印證上述定義。

注意:在實際中的繼承體系裏面最好不要定義同名的成員,否則非常容易混淆。

 

  • 類成員函數隱藏:只要函數名相同就構成隱藏。(在派生類成員函數中,可以使用 基類::基類成員 顯示訪問)

按照定義,派生類與基類存在相同函數名的成員函數那麼這個函數會不會發生函數重載呢?

答案是不會,因爲函數重載需要在同一作用域內,而繼承中派生類和基類有各自獨立的作用域。

class A
{
public:
	void fun()
	{
		cout << "func()" << endl;
	}
public:
	int _a = 5;
};
class B : public A
{
public:
	void fun(int i)
	{
		cout << "func(int i)->" << i << endl;
	}
public:
	int _a = 10;
};
void Test()
{
	B b;
	b.fun(10);
};

 運行程序:

B中的fun和A中的fun構成隱藏,由此我們得出結論:成員函數滿足函數名相同就構成隱藏。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章