天天看點

【C++】---封裝、繼承、多态

面向對象的三個基本特征

面向對象的三個基本特征是:封裝、繼承、多态。其中,封裝可以隐藏實作細節,使得代碼子產品化;繼承可以擴充已存在的代碼子產品(類);它們的目的都是為了——代碼重用。而多态則是為了實作另一個目的——接口重用!

封裝

  封裝可以隐藏實作細節,使得代碼子產品化;封裝是把過程和資料包圍起來,對資料的通路隻能通過已定義的界面。面向對象計算始于這個基本概念,即現實世界可以被描繪成一系列完全自治、封裝的對象,這些對象通過一個受保護的接口通路其他對象。在面向對象程式設計上可了解為:把客觀事物封裝成抽象的類,并且類可以把自己的資料和方法隻讓可信的類或者對象操作,對不可信的進行資訊隐藏。

繼承

繼承是指這樣一種能力:它可以使用現有類的所有功能,并在無需重新編寫原來的類的情況下對這些功能進行擴充。其繼承的過程,就是從一般到特殊的過程。

通過繼承建立的新類稱為“子類”或“派生類”。被繼承的類稱為“基類”、“父類”或“超類”。要實作繼承,可以通過“繼承”(Inheritance)和“組合”(Composition)來實作。在某些OOP 語言中,一個子類可以繼承多個基類。但是一般情況下,一個子類隻能有一個基類,要實作多重繼承,可以通過多級繼承來實作。

多态

多态性(polymorphisn)是允許你将父對象設定成為和一個或更多的他的子對象相等的技術,指派之後,父對象就可以根據目前指派給它的子對象的特性以不同的方式運作。簡單的說,就是一句話:允許将子類類型的指針指派給父類類型的指針。

繼承

繼承性是面向對象程式設計的第二大特性,它允許在既有類的基礎上建立新類,新類可以繼承既有類的資料成員和成員函數,可以添加自己特有的資料成員和成員函數,還可以對既有類中的成員函數重新定義。利用類的繼承和派生實作了更高層次的代碼可重用性,符合現代軟體開發的思想。

      C++語言同時支援單一繼承和多重繼承。單一繼承是指派生類隻從一個基類繼承而來;相應的,多重繼承指派生類同時從兩個或更多的基類繼承而來。java隻支援單一繼承。

【C++】---封裝、繼承、多态

派生類:

派生類的定義格式如下:

      class <派生類名>:[繼承方式]<基類名1>

                                 [,[繼承方式]<基類名2>,...,[繼承方式]<基類名n>]

      {

              <派生類新增的資料成員和成員函數定義>

      };

說明:

      (1)定義派生類關鍵字可以是class或者是struct,兩者差別是:用class定義派生類,預設的繼承方式是private,用struct定義派生類,預設的繼承方式為public。新增加的成員預設屬性也是class對應private屬性,struct對應public屬性。

      (2)基類不能被派生類繼承的兩類函數是構造函數和析構函數。(以上借鑒:https://www.cnblogs.com/Rosanna/p/3331578.html)

繼承方式

基類中不同的通路限定符下的成員以不同的繼承方式繼承  在派生類中的方向

【C++】---封裝、繼承、多态

類和類之間的關系

組合:a  part  of                  私有    has a

繼承:a    kind    of              共有/保護   is  a

代理:

【C++】---封裝、繼承、多态

派生類調用構造和析構方式

先調用基類構造函數,構造基類部分成員變量,再調用派生類構造函數構造派生類部分的成員變量。2.基類部分成員的初始化方式在派生類構造函數的初始化清單中指定。3.若基類中還有成員對象,則先調用成員對象的構造函數,再調用基類構造函數,最後是派生類構造函數。析構順序和構造順序相反。

class Base
{
public:
	Base(int a)
	{
		ma = a;
		cout<<"Base::base()"<<endl;
	}
	~Base()
	{
		cout<<"Base::~base()"<<endl;
	}
private:
	int ma;
};

class Derive : public Base
{
public:
	Derive(int b):Base(b)
	{
		mb = b;
		cout<<"Derive::derive()"<<endl;
	}
~Derive()

	{
		cout<<"Derive::~derive()"<<endl;
	}
private:
	int mb;
};

int main()
{
	Derive d(2);
	return 0;
}
           
【C++】---封裝、繼承、多态

同名函數的關系

重載(overload) :是指同一可通路區内被聲明的幾個具有不同參數列(參數的類型,個數,順序不同)的同名函數,根據參數清單确定調用哪個函數,重載不關心函數傳回類型。

class A{
 public:
   void test(int i);
   void test(double i);//overload
   void test(int i, double j);//overload
   void test(double i, int j);//overload
   int test(int i);         //錯誤,非重載。注意重載不關心函數傳回類型。
};
           

隐藏(overhide):是指派生類的函數屏蔽了與其同名的基類函數,注意隻要同名函數,不管參數清單是否相同,基類函數都會被隐藏

隐藏條件----1.繼承    不同作用域    2.同名函數

要不被隐藏就顯式的加作用域ok

class Base
{
public:
    void fun(double ,int ){ cout << "Base::fun(double ,int )" << endl; }
};

class Derive : public Base
{
public:
    void fun(int ){ cout << "Derive::fun(int )" << endl; }
};

int main()
{
    Derive pd;
    pd.fun(1);

    Base *fd = &pd;
    fd->fun(1.0,1);
    //fd->fun(1);//error 
    system("pause");
    return 0;
}
           

那句fd->fun(1);    錯誤原因--->“Base::fun”: 函數不接受 1 個參數

重寫(覆寫overwrite):是指派生類中存在重新定義的函數。其函數名,參數清單,傳回值類型,所有都必須同基類中被重寫的函數一緻。隻有函數體不同(花括号内),派生類調用時會調用派生類的重寫函數,不會調用被重寫函數。重寫的基類中被重寫的函數必須有virtual修飾。

class Base
{
public:
	Base(int a) :ma(a)
	{
		std::cout << "Base::Base(int)" << std::endl;
	}
	virtual ~Base()
	{
		std::cout << "Base::~Base()" << std::endl;
	}
	virtual void Show()
	{
		std::cout << "Base::Show ==> ma:" << ma << std::endl;
	}
protected:
	int ma;

};
class Derive : public Base
{
public:
	Derive(int b) :Base(b), mb(b)
	{
			std::cout << "Derive::Derive(int)" << std::endl;
	}
	~Derive()
	{
		std::cout << "Derive::~Derive()" << std::endl;
	}
	void Show()
	{
		std::cout << "Derive::Show ==> mb:" << mb << std::endl;
	}
private:
	int mb;
};
int main()
{
	Base* pb = new Derive(10);
	pb->Show();
	//delete pb;//pb->~Base() // call Base::~Base();
	//pb->destor pb->~Derive() //  
	return 0;
}
           
【C++】---封裝、繼承、多态

基類Show函數被重寫!

Derive* pb = new Base(10);
           

  這樣不對的!錯誤原因---->從基類型到派生類型的強制轉換需要 dynamic_cast 或 static_cast

【C++】---封裝、繼承、多态

虛函數

https://blog.csdn.net/haoel/article/details/1948051#commentBox大佬的

多态

【C++】---封裝、繼承、多态

靜多态和動多态的差別:

其實隻是差別什麼時間将函數實作和函數調用關聯起來,是在編譯期還是在運作期,即函數位址是在早綁定還是晚綁定的??

①靜多态是指在編譯期就可以确定函數的調用位址,并産生代碼,這就是靜态的,也就是說位址是早早綁定的,靜多态往往也被叫做靜态聯翩。

②動多态則是指函數調用的位址不能在編譯期間确定的,必須要在運作時才能确定,這就屬于晚綁定,動多态往往也被叫做動态聯翩。

詳細博文:https://blog.csdn.net/qq_39412582/article/details/81628254

https://www.cnblogs.com/cxq0017/p/6074247.html

    https://blog.csdn.net/qq_36359022/article/details/81870219#commentBox

那些函數不能定義為虛函數?

經檢驗下面的幾個函數都不能定義為虛函數:

1)友元函數,它不是類的成員函數

2)全局函數

3)靜态成員函數,它沒有this指針

3)構造函數,拷貝構造函數,以及指派運算符重載(可以但是一般不建議作為虛函數)

虛析構

 積累指針指向派生類對象時,最好把析構寫為虛析構,避免通過積累指針釋放派生類對象  派生類中自己的資源無法釋放

動多态的發生時機

  1. 指針調用虛函數
  2. 對象完整 

多繼承

菱形繼承  虛繼承

https://www.cnblogs.com/fire909090/p/7085146.html

怎麼設計一個不能被繼承的類

  1. 類似單例模式設計
  2. 有緣 不能被繼承

友元函數:

  1.  友元關系不能被繼承。 
  2.  友元關系是單向的,不具有交換性。若類B是類A的友元,類A不一定是類B的友元,要看在類中是否有相應的聲明。
  3. 友元關系不具有傳遞性。若類B是類A的友元,類C是B的友元,類C不一定是類A的友元,同樣要看類中是否有相應的申明

詳細:https://blog.csdn.net/pursue_my_life/article/details/80466142

繼續閱讀