中文字幕av专区_日韩电影在线播放_精品国产精品久久一区免费式_av在线免费观看网站

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

C++中的封裝、繼承、多態理解

發布時間:2020-09-08 15:02:04 來源:腳本之家 閱讀:314 作者:fengbingchun 欄目:編程語言

封裝(encapsulation):就是將抽象得到的數據和行為(或功能)相結合,形成一個有機的整體,也就是將數據與操作數據的源代碼進行有機的結合,形成”類”,其中數據和函數都是類的成員。封裝的目的是增強安全性和簡化編程,使用者不必了解具體的實現細節,而只是要通過外部接口,特定的訪問權限來使用類的成員。封裝可以隱藏實現細節,使得代碼模塊化。

繼承(inheritance):C++通過類派生機制來支持繼承。被繼承的類型稱為基類或超類,新產生的類為派生類或子類。保持已有類的特性而構造新類的過程稱為繼承。在已有類的基礎上新增自己的特性而產生新類的過程稱為派生。繼承和派生的目的是保持已有類的特性并構造新類。繼承的目的:實現代碼重用。派生的目的:實現代碼擴充。三種繼承方式:public、protected、private。

繼承時的構造函數:(1)、基類的構造函數不能被繼承,派生類中需要聲明自己的構造函數;(2)、聲明構造函數時,只需要對本類中新增成員進行初始化,對繼承來的基類成員的初始化,自動調用基類構造函數完成;(3)、派生類的構造函數需要給基類的構造函數傳遞參數;(4)、單一繼承時的構造函數:派生類名::派生類名(基類所需的形參,本類成員所需的形參):基類名(參數表) {本類成員初始化賦值語句;};(5)、當基類中聲明有默認形式的構造函數或未聲明構造函數時,派生類構造函數可以不向基類構造函數傳遞參數;(6)、若基類中未聲明構造函數,派生類中也可以不聲明,全采用缺省形式構造函數;(7)、當基類聲明有帶形參的構造函數時,派生類也應聲明帶形參的構造函數,并將參數傳遞給基類構造函數;(8)、構造函數的調用次序:A、調用基類構造函數,調用順序按照它們被繼承時聲明的順序(從左向右);B、調用成員對象的構造函數,調用順序按照它們在類中的聲明的順序;C、派生類的構造函數體中的內容。

繼承時的析構函數:(1)、析構函數也不被繼承,派生類自行聲明;(2)、聲明方法與一般(無繼承關系時)類的析構函數相同;(3)、不需要顯示地調用基類的析構函數,系統會自動隱式調用;(4)、析構函數的調用次序與構造函數相反。

同名隱藏規則:當派生類與基類中有相同成員時:(1)、若未強行指名,則通過派生類對象使用的是派生類中的同名成員;(2)、如要通過派生類對象訪問基類中被覆蓋的同名成員,應使用基類名限定:基類名::數據成員名。

虛基類:作用:(1)、主要用來解決多繼承時可能發生的對同一基類繼承多次而產生的二義性問題;(2)、為最遠的派生類提供唯一的基類成員,而不重復產生多次拷貝。

繼承、組合:組合是將其它類的對象作為成員使用,繼承是子類可以使用父類的成員方法。(1)、A繼承B,說明A是B的一種,并且B的所有行為對A都有意義;(2)、若在邏輯上A是B的“一部分”,則不允許B從A派生,而是要用A和其它東西組合出B;(3)、繼承屬于”白盒”復用,組合屬于”黑盒”復用。

多態(Polymorphic)性可以簡單地概括為“一個接口,多種方法”程序在運行時才決定調用的函數C++多態性是通過虛函數來實現的,虛函數允許子類重新定義成員函數,而子類重新定義父類的做法稱為覆蓋或者稱為重寫。而重載則是允許有多個同名的函數,而這些函數的參數列表不同,允許參數個數不同,參數類型不同,或者兩者都不同。關于多態,簡而言之就是用父類型別的指針指向其子類的實例,然后通過父類的指針調用實際子類的成員函數

多態與非多態的實質區別就是函數地址是早綁定還是晚綁定。如果函數的調用,在編譯器編譯期間就可以確定函數的調用地址,并產生代碼,是靜態的,就是說地址是早綁定的。而如果函數調用的地址不能在編譯期間確定,需要在運行時才確定,這就是屬于晚綁定。

封裝可以使得代碼模塊化,繼承可以擴展已存在的代碼,它們的目的都是為了代碼重用。而多態的目的則是為了接口重用。也就是說不論傳遞過來的究竟是哪個類的對象,函數都能夠通過同一個接口調用到適應各自對象的實現方法。

最常見的用法就是聲明基類的指針,利用該指針指向任意一個子類對象,調用相應的虛函數,可以根據指向的子類的不同而實現不同的方法。如果沒有使用虛函數的話,即沒有利用C++多態性,則利用基類指針調用相應的函數的時候,將總被限制在基類函數本身,而無法調用到子類中被重寫過的函數。因為沒有多態性,函數調用的地址將是一定的,而固定的地址將始終調用到同一個函數,這就無法實現一個接口,多種方法的目的了。

純虛函數是在基類中聲明的虛函數,它在基類中沒有定義,但要求任何派生類都要定義自己的實現方法。在基類中實現純虛函數的方法是在函數原型后加“= 0”。為了方便使用多態特性,常常需要在基類中定義虛函數,在很多情況下,基類本身生成對象是不合情理的。為了解決這些問題,引入了純虛函數的概念,將函數定義為純虛函數,則編譯器要求在派生類中必須予以重寫以實現多態性。同時含有純虛函數的類稱為抽象類,它不能生成對象。由于純虛函數所在的類中沒有它的定義,在該類的構造函數和析構函數中不允許調用純虛函數,否則會導致程序運行錯誤,但其它成員函數可以調用純虛函數。

C++支持兩種多態性:(1)、編譯時多態性(靜態多態,在編譯時就可以確定對象使用的形式):通過重載函數實現;(2)、運行時多態性(動態多態,其具體引用的對象在運行時才能確定):通過虛函數實現。

C++中,實現多態有以下方法:虛函數、抽象類、重載、覆蓋、模板。

函數重載(Overload):指在相同作用域里(如同一類中),函數同名不同參,返回值則不用理會,不同參可以是不同個數,也可以是不同類型。效果:根據實參的個數和類型調用對應的函數體。

函數覆蓋(Override)(函數重寫):指派生類中的函數覆蓋基類中的同名同參虛函數,因此作用域不同。效果:基類指針或引用訪問虛函數時會根據實例的類型調用對應的函數。

函數隱藏(Hide):對于子類中與基類同名的函數,如果不是覆蓋那就成了隱藏。兩種情況:(1)、同名不同參;(2)、同名同參但基類不是virtual函數。

派生類的構造函數使用說明:(1)、在派生類構造函數中,只要基類不是僅使用無參的默認構造函數,都要顯示的給出基類名稱參數表;(2)、基類沒有定義構造函數,派生類也可以不定義,使用默認構造函數;(3)、基類有帶參構造函數,派生類必須定義構造函數。

虛函數的重載函數仍是虛函數。在派生類重定義虛函數時必須有相同的函數原型,包括返回類型、函數名、參數個數、參數類型的順序必須相同。虛函數必須是類的成員函數,不能為全局函數,也不能為靜態函數。不能將友員說明為虛函數,但虛函數可以是另一個類的友員。析構函數可以是虛函數,但構造函數不能為虛函數。一般地講,若某類中定義有虛函數,則其析構函數也應當說明為虛函數。特別是在析構函數需要完成一些有意義的操作,比如釋放內存時,尤其應當如此。在類系統中訪問一個虛函數時,應使用指向基類類型的指針或對基類類型的引用,以滿足運行時多態性的要求。當然也可以像調用普通成員函數那樣利用對象名來調用一個函數。若在派生類中沒有重新定義虛函數,則該類的對象將使用其基類中的虛函數代碼。

抽象類:如果一個類中至少有一個純虛函數,那么這個類被稱為抽象類。抽象類不僅包括純虛函數,也可包括虛函數。抽象類中的純虛函數可能是在抽象類中定義的,也可能是從它的抽象基類中繼承下來且重定義的。抽象類有一個重要特點,即抽象類必須用作派生其它類的基類,而不能用于直接創建對象實例。抽象類不能直接創建對象的原因是其中有一個或多個函數沒有定義,但仍可使用指向抽象類的指針支持運行時多態性。派生類中必須重載基類中的純虛函數,否則它仍將被看作一個抽象類。從基類繼承來的純虛函數,在派生類中仍是虛函數。

虛函數表:虛函數是通過一張虛函數表來實現的。簡稱為V-Table,在這個表中,主要是一個類的虛函數的地址表,這張表解決了繼承、覆蓋的問題,保證其真實反應實際的函數。這樣,在有虛函數的類的實例中這個表被分配在了這個實例的內存中,所以,當我們用父類的指針來操作一個子類的時候,這張虛函數表就顯得有無重要了,它就像一個地圖一樣,指明了實際所應該調用的函數。

一個多態的例子:

#include <iostream>
using namespace std;
 
class A
{
public:
	void foo()
	{
		printf("1\n");
	}
 
	virtual void fun()
	{
		printf("2\n");
	}
};
 
class B : public A
{
public:
	void foo()
	{
		printf("3\n");
	}
 
	void fun()
	{
		printf("4\n");
	}
};
 
int main(void)
{
	A a;
	B b;
 
	A* p = &a;
	p->foo();//1
	p->fun();//2
 
	p = &b;
	p->foo();//1
	p->fun();//4
 
	B* ptr = (B*)&a;
	ptr->foo();//3
	ptr->fun();//2
 
	return 0;
}

另一個例子:

#include <iostream>
using namespace std;
 
int main(void)
{
	class CA 
	{
	public:
		virtual ~CA() {cout<<"delete CA"<<endl;}
		virtual int GetValue() {return 1;}
	};
 
	class CB : public CA
	{
	public:
		~CB() {cout<<"delete CB"<<endl;}
		virtual int GetValue() {return 2;}
	};
 
	CA* pA = new CB;
	cout<<pA->GetValue()<<endl;
	delete pA;
 
	/* result:
		2
		delete CB
		delete CA
	*/
	/*若父類CA中沒有將析構函數定義為虛函數,則result:
		2
		delete CA
		由結果看出,如果不將父類CA的析構函數定義為虛函數,則不會調用到子類的析構函數
	*/
	/*若父類CA中的成員函數GetValue沒有定義為虛函數,則result:
		1
		delete CA
	*/
 
	return 0;
}

對C++繼承,封裝,多態的理解

用了C++一段時間,感覺對C++慢慢有了一點認識,在這和大家分享一下。
C++是一款面向對象的語言,擁有面向對象語言的三大核心特性:繼承,封裝,多態。每一個特性的良好理解與使用都會為我們的編程帶來莫大的幫助。下面我就這三個特性講一下我對C++的理解。

繼承

      學過面向對象語言的人基本都可以理解什么是繼承,但我們為什么要使用繼承?
      很多人說繼承可以使代碼得到良好的復用,當然這個是繼承的一個優點,但代碼復用的方法除了繼承還有很多,而且有些比繼承更好。我認為使用繼承最重要的原因是繼承可以使整個程序設計更符合人們的邏輯,從而方便的設計出想要表達的意思。比如我們要設計一堆蘋果,橘子,梨等水果類,使用面向對象的方法,我們首先會抽象出一個水果的基類,而后繼承這個基類,派生出具體的水果類。如果要設計的水果很多,我們還可以在水果基類基礎上,繼續生成新的基類,比如熱帶水果類,溫帶水果類,寒帶水果類等,而后再繼承這些基類。這樣的設計思想就相當于人類的分類思想,簡單易懂,而且設計出來的程序層次分明,容易掌握。
      既然繼承這么好,那該如何使用繼承?
      繼承雖好但不能濫用,否則設計出來的程序會雜亂不堪。根據上面的介紹,可以發現繼承主要用來定義一個東西是什么,比如熱帶水果是水果,菠蘿是熱帶水果等,即繼承主要用來設計一個程序的類的框架,將所要設計的東西用繼承來設立一個基本結構。如果想為一個類添加一個行為或格外的功能,最好是使用組合的方式。如果想了解組合的方式,可以看一下比較著名的策略模式。

封裝

      封裝是什么?
      在C++中,比較狹隘的解釋就是將數據與操作數據的方法放在一個類中,而后給每個成員設置相應的權限。從大一點的角度來說,封裝就是將完成一個功能所需要的所有東西放在一起,對外部只開放調用它的接口。
       為什么要封裝?
       我認為模塊化設計是封裝的本質原因。
       對軟件設計或其他工程設計,特別是比較復雜的工程,一般都是模塊化設計。模塊化設計的好處就是可以將一個復雜的系統拆分成不同的模塊。每一個模塊又可以獨立的設計,調試,這就讓多人一起做一個復雜的工程成為現實。如果想做到模塊化設計,封裝是不可缺少的一部分。一個好的模塊,比如一塊inter的CPU芯片,它有強大的功能,雖然我們不知道它內部是如何實現的,但卻可以很好的使用它。 

多態

        什么是多態?
        多態簡單的說就是“一個函數,多種實現”,或是“一個接口,多種方法”。多態性表現在程序運行時根據傳入的對象調用不同的函數。
        C++的多態是通過虛函數來實現的,在基類中定義一個函數為虛函數,該函數就可以在運行時,根據傳入的對象調用不同的實現方法。而如果該函數不設為虛函數,則在調用的過程中調用的函數就是固定的。比如下面一個例子

//
//定義一個Duck基類,而后繼承Duck派生出一個RedHandDuck類。
//其中display()方法,第一次運行設為普通函數,第二次設為虛函數
 
#include "iostream"
 
class Duck {
 
public:
	Duck(){}
	~Duck(){}
 
	//定義一個虛函數display
	virtual void display(){
 
		std::cout<<" I am a Duck !"<<std::endl;
	}
};
 
class RedHandDuck:public Duck{
 
public:
	RedHandDuck(){}
	~RedHandDuck(){}
 
	//重寫display
	void display(){
 
		std::cout<<" I am a RedHandDuck !"<<std::endl;
	}
};
 
int main(){
 
	RedHandDuck* duck1 = new RedHandDuck();
	Duck* duck2 = duck1;
 
	duck1->display();
	duck2->display();
 
	std::getchar();
}

第一次運行結果(不使用虛函數):

C++中的封裝、繼承、多態理解

第二次運行結果(使用虛函數):

C++中的封裝、繼承、多態理解

由結果可以看到,由于虛函數的使用,Duck對象(可以理解為接口),調用的display()方法是根據傳入的對象決定的。這就實現了“一個接口,多種方法”。

從網上看到一個關于多態的介紹,非常精辟,分享給大家

  多態與非多態的實質區別就是函數地址是早綁定還是晚綁定。如果函數的調用,在編譯器編譯期間就可以確定函數的調用地址,并生產代碼,是靜態的,就是說地址是早綁定的。而如果函數調用的地址不能在編譯器期間確定,需要在運行時才確定,這就屬于晚綁定。

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

晋州市| 台山市| 正蓝旗| 晴隆县| 七台河市| 秭归县| 延庆县| 临潭县| 五家渠市| 余庆县| 肥城市| 吴忠市| 曲松县| 白山市| 咸阳市| 都昌县| 八宿县| 嵊泗县| 北海市| 彭水| 汪清县| 东方市| 伊金霍洛旗| 钟山县| 黎平县| 塔城市| 图木舒克市| 漳平市| 定远县| 普兰店市| 泰宁县| 太康县| 淳安县| 榆树市| 元氏县| 黔东| 昌吉市| 安仁县| 莒南县| 承德县| 屯昌县|