C++中的封裝、繼承、多態(tài)理解

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

成都創(chuàng)新互聯(lián)公司專注為客戶提供全方位的互聯(lián)網(wǎng)綜合服務(wù),包含不限于網(wǎng)站建設(shè)、網(wǎng)站制作、左權(quán)網(wǎng)絡(luò)推廣、小程序開發(fā)、左權(quán)網(wǎng)絡(luò)營銷、左權(quán)企業(yè)策劃、左權(quán)品牌公關(guān)、搜索引擎seo、人物專訪、企業(yè)宣傳片、企業(yè)代運(yùn)營等,從售前售中售后,我們都將竭誠為您服務(wù),您的肯定,是我們最大的嘉獎;成都創(chuàng)新互聯(lián)公司為所有大學(xué)生創(chuàng)業(yè)者提供左權(quán)建站搭建服務(wù),24小時服務(wù)熱線:18982081108,官方網(wǎng)址:bm7419.com

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

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

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

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

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

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

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

多態(tài)與非多態(tài)的實(shí)質(zhì)區(qū)別就是函數(shù)地址是早綁定還是晚綁定。如果函數(shù)的調(diào)用,在編譯器編譯期間就可以確定函數(shù)的調(diào)用地址,并產(chǎn)生代碼,是靜態(tài)的,就是說地址是早綁定的。而如果函數(shù)調(diào)用的地址不能在編譯期間確定,需要在運(yùn)行時才確定,這就是屬于晚綁定。

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

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

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

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

C++中,實(shí)現(xiàn)多態(tài)有以下方法:虛函數(shù)、抽象類、重載、覆蓋、模板。

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

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

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

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

虛函數(shù)的重載函數(shù)仍是虛函數(shù)。在派生類重定義虛函數(shù)時必須有相同的函數(shù)原型,包括返回類型、函數(shù)名、參數(shù)個數(shù)、參數(shù)類型的順序必須相同。虛函數(shù)必須是類的成員函數(shù),不能為全局函數(shù),也不能為靜態(tài)函數(shù)。不能將友員說明為虛函數(shù),但虛函數(shù)可以是另一個類的友員。析構(gòu)函數(shù)可以是虛函數(shù),但構(gòu)造函數(shù)不能為虛函數(shù)。一般地講,若某類中定義有虛函數(shù),則其析構(gòu)函數(shù)也應(yīng)當(dāng)說明為虛函數(shù)。特別是在析構(gòu)函數(shù)需要完成一些有意義的操作,比如釋放內(nèi)存時,尤其應(yīng)當(dāng)如此。在類系統(tǒng)中訪問一個虛函數(shù)時,應(yīng)使用指向基類類型的指針或?qū)愵愋偷囊?,以滿足運(yùn)行時多態(tài)性的要求。當(dāng)然也可以像調(diào)用普通成員函數(shù)那樣利用對象名來調(diào)用一個函數(shù)。若在派生類中沒有重新定義虛函數(shù),則該類的對象將使用其基類中的虛函數(shù)代碼。

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

虛函數(shù)表:虛函數(shù)是通過一張?zhí)摵瘮?shù)表來實(shí)現(xiàn)的。簡稱為V-Table,在這個表中,主要是一個類的虛函數(shù)的地址表,這張表解決了繼承、覆蓋的問題,保證其真實(shí)反應(yīng)實(shí)際的函數(shù)。這樣,在有虛函數(shù)的類的實(shí)例中這個表被分配在了這個實(shí)例的內(nèi)存中,所以,當(dāng)我們用父類的指針來操作一個子類的時候,這張?zhí)摵瘮?shù)表就顯得有無重要了,它就像一個地圖一樣,指明了實(shí)際所應(yīng)該調(diào)用的函數(shù)。

一個多態(tài)的例子:

#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中沒有將析構(gòu)函數(shù)定義為虛函數(shù),則result:
		2
		delete CA
		由結(jié)果看出,如果不將父類CA的析構(gòu)函數(shù)定義為虛函數(shù),則不會調(diào)用到子類的析構(gòu)函數(shù)
	*/
	/*若父類CA中的成員函數(shù)GetValue沒有定義為虛函數(shù),則result:
		1
		delete CA
	*/
 
	return 0;
}

對C++繼承,封裝,多態(tài)的理解

用了C++一段時間,感覺對C++慢慢有了一點(diǎn)認(rèn)識,在這和大家分享一下。
C++是一款面向?qū)ο蟮恼Z言,擁有面向?qū)ο笳Z言的三大核心特性:繼承,封裝,多態(tài)。每一個特性的良好理解與使用都會為我們的編程帶來莫大的幫助。下面我就這三個特性講一下我對C++的理解。

繼承

      學(xué)過面向?qū)ο笳Z言的人基本都可以理解什么是繼承,但我們?yōu)槭裁匆褂美^承?
      很多人說繼承可以使代碼得到良好的復(fù)用,當(dāng)然這個是繼承的一個優(yōu)點(diǎn),但代碼復(fù)用的方法除了繼承還有很多,而且有些比繼承更好。我認(rèn)為使用繼承最重要的原因是繼承可以使整個程序設(shè)計更符合人們的邏輯,從而方便的設(shè)計出想要表達(dá)的意思。比如我們要設(shè)計一堆蘋果,橘子,梨等水果類,使用面向?qū)ο蟮姆椒?,我們首先會抽象出一個水果的基類,而后繼承這個基類,派生出具體的水果類。如果要設(shè)計的水果很多,我們還可以在水果基類基礎(chǔ)上,繼續(xù)生成新的基類,比如熱帶水果類,溫帶水果類,寒帶水果類等,而后再繼承這些基類。這樣的設(shè)計思想就相當(dāng)于人類的分類思想,簡單易懂,而且設(shè)計出來的程序?qū)哟畏置鳎菀渍莆铡?br />      既然繼承這么好,那該如何使用繼承?
      繼承雖好但不能濫用,否則設(shè)計出來的程序會雜亂不堪。根據(jù)上面的介紹,可以發(fā)現(xiàn)繼承主要用來定義一個東西是什么,比如熱帶水果是水果,菠蘿是熱帶水果等,即繼承主要用來設(shè)計一個程序的類的框架,將所要設(shè)計的東西用繼承來設(shè)立一個基本結(jié)構(gòu)。如果想為一個類添加一個行為或格外的功能,最好是使用組合的方式。如果想了解組合的方式,可以看一下比較著名的策略模式。

封裝

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

多態(tài)

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

//
//定義一個Duck基類,而后繼承Duck派生出一個RedHandDuck類。
//其中display()方法,第一次運(yùn)行設(shè)為普通函數(shù),第二次設(shè)為虛函數(shù)
 
#include "iostream"
 
class Duck {
 
public:
	Duck(){}
	~Duck(){}
 
	//定義一個虛函數(shù)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();
}

第一次運(yùn)行結(jié)果(不使用虛函數(shù)):

C++中的封裝、繼承、多態(tài)理解

第二次運(yùn)行結(jié)果(使用虛函數(shù)):

C++中的封裝、繼承、多態(tài)理解

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

從網(wǎng)上看到一個關(guān)于多態(tài)的介紹,非常精辟,分享給大家

多態(tài)與非多態(tài)的實(shí)質(zhì)區(qū)別就是函數(shù)地址是早綁定還是晚綁定。如果函數(shù)的調(diào)用,在編譯器編譯期間就可以確定函數(shù)的調(diào)用地址,并生產(chǎn)代碼,是靜態(tài)的,就是說地址是早綁定的。而如果函數(shù)調(diào)用的地址不能在編譯器期間確定,需要在運(yùn)行時才確定,這就屬于晚綁定。

當(dāng)前題目:C++中的封裝、繼承、多態(tài)理解
網(wǎng)站鏈接:http://bm7419.com/article20/jcshjo.html

成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供商城網(wǎng)站、網(wǎng)站設(shè)計公司、網(wǎng)站維護(hù)品牌網(wǎng)站設(shè)計、品牌網(wǎng)站建設(shè)網(wǎng)站建設(shè)

廣告

聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請盡快告知,我們將會在第一時間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場,如需處理請聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時需注明來源: 創(chuàng)新互聯(lián)

成都定制網(wǎng)站網(wǎng)頁設(shè)計