面向?qū)ο笕筇卣?#xff1a;封裝,繼承,多態(tài);
一、封裝:該公開(kāi)的就公開(kāi)話,該私有的就隱藏掉,主要是由public,private實(shí)現(xiàn);作用是便于分工和分模塊,防止不必要的擴(kuò)展;
二、繼承:就是一種傳承,可以把父類(lèi)型中的數(shù)據(jù)傳承到子類(lèi)中,子類(lèi)除了傳承了父類(lèi)的數(shù)據(jù)之外,還可以對(duì)父類(lèi)型進(jìn)行擴(kuò)展;
公開(kāi)繼承? public
保護(hù)繼承? protected
私有繼承? private
保護(hù)成員:在子類(lèi)和本類(lèi)中可以訪問(wèn),其他不行;
1、公開(kāi)繼承:在公開(kāi)繼承下,父類(lèi)型中的數(shù)據(jù)是公開(kāi)的到子類(lèi)型中權(quán)限是公開(kāi)的;父類(lèi)型中保護(hù)權(quán)限的數(shù)據(jù)到子類(lèi)中是保護(hù)的;父類(lèi)中私有的數(shù)據(jù)到子類(lèi)中會(huì)隱藏掉(就是說(shuō)看不見(jiàn)權(quán)限,但是實(shí)際上式在子類(lèi)中的);
2、私有繼承:在私有繼承下,父類(lèi)中的公開(kāi)數(shù)據(jù)到子類(lèi)中變成私有的,父類(lèi)中的保護(hù)數(shù)據(jù)到子類(lèi)中稱(chēng)為私有的,父類(lèi)中的私有數(shù)據(jù)到子類(lèi)中隱藏;
3、保護(hù)繼承:保護(hù)繼承下,父類(lèi)中的公開(kāi)數(shù)據(jù)和保護(hù)數(shù)據(jù)到了子類(lèi)中都成為保護(hù)權(quán)限,父類(lèi)中私有的數(shù)據(jù)到了子類(lèi)中就變成了隱藏的;
4、注意:不管何種類(lèi)型的繼承關(guān)系,父類(lèi)私有成員到子類(lèi)中都成為了隱藏掉。
公開(kāi)繼承下的public成員和protected成員的權(quán)限變化:
[cpp] view plain
copy print?
#include?<iostream>??using?namespace?std;????class?A{??public:??????int?a;??????int?geta(){??????????a?=?300;??????????return?a;??????}????????protected:??????int?b;??private:??????int?c;??};????class?B:public?A{????????public:??????void?getb(){??????????b?=?200;??????}??????void?show(){??????????cout?<<?b?<<?endl;??????}??????void?showa(){??????????cout<<?a<<endl;??????}??};?????int?main(){??????B?pex;????????????pex.a?=?100;????????????pex.getb();??????pex.show();????????????pex.geta();??????pex.showa();????????????????????}??
#include <iostream>
using namespace std;class A{
public:int a;int geta(){a = 300;return a;}/*保護(hù)類(lèi)型成員在本類(lèi)和子類(lèi)中可以訪問(wèn)*/
protected:int b;
private:int c;
};class B:public A{//int x;
public:void getb(){b = 200;}void show(){cout << b << endl;}void showa(){cout<< a<<endl;}
};
/*關(guān)鍵在于如何設(shè)置接口,成功合理的訪問(wèn)到各種類(lèi)型的數(shù)據(jù)*/
int main(){B pex;/*公開(kāi)繼承public成員依舊是public,所以可以類(lèi)外訪問(wèn)*/pex.a = 100;/*b是保護(hù)類(lèi)型成員,可以通過(guò)設(shè)置public接口來(lái)訪問(wèn)*/pex.getb();pex.show();/*隱藏成員的問(wèn)題,怎么訪問(wèn)到隱藏的成員*/pex.geta();pex.showa();//A a = pex;//子類(lèi)類(lèi)型賦給了父類(lèi)類(lèi)型//a.geta();//cout << a.a << endl;
}
5、私有繼承下的成員的權(quán)限變化關(guān)系,注意如何設(shè)置合理的 訪問(wèn)接口去訪問(wèn) 隱藏的數(shù)據(jù)成員。
[cpp] view plain
copy print?
???#include?<iostream>??using?namespace?std;????class?A{??private:??????void?showa(){??????????cout?<<?"this?is?showa()"?<<?endl;??????}??protected:??????void?showb(){??????????cout?<<?"this?is?showb"?<<?endl;??????}??public:??????void?showc(){??????????cout?<<?"this?is?showc"?<<?endl;??????}??????void?geta(){??????????showa();??????}??};????class?B:private?A{??public:??????void?show(){??????????showc();??????????showb();??????????geta();??????}??};????int?main(){??????B?b;????????????b.show();??}??
/*私有繼承下的權(quán)限變化,關(guān)鍵是設(shè)置合理的接口訪問(wèn)
父類(lèi)中的各種類(lèi)型的數(shù)據(jù)成員*/
#include <iostream>
using namespace std;class A{
private:void showa(){cout << "this is showa()" << endl;}
protected:void showb(){cout << "this is showb" << endl;}
public:void showc(){cout << "this is showc" << endl;}void geta(){//設(shè)置合理的接口訪問(wèn)A中的私有數(shù)據(jù)showa();}
};class B:private A{
public:void show(){showc();showb();geta();}
};int main(){B b;//A a = b;對(duì)比公開(kāi)繼承,對(duì)比一下b.show();
}
6、突破成員訪問(wèn)權(quán)限,可以設(shè)置合理的訪問(wèn)接口,也可以使用友元類(lèi)。
[cpp] view plain
copy print?
??#include?<iostream>??using?namespace?std;????class?A{??private:??????int?x;??????int?y;??public:??????A():x(10),y(123){}????????????friend?class?B;??????friend?class?C;??};????class?B:public?A{??public:??????void?show(){??????????cout?<<?x?<<?"---"?<<?y?<<?endl;???????}??};????class?C{??public:??????void?show(){??????????A?a;??????????cout?<<a.x<<?"---"?<<?a.y?<<?endl;??????}??};????int?main(){??????B?b;??????b.show();??????C?c;??????c.show();??}??
/*友元類(lèi)*/
#include <iostream>
using namespace std;class A{
private:int x;int y;
public:A():x(10),y(123){}/*B,C聲明為A的友元類(lèi)之后,可以訪問(wèn)到父類(lèi)的所有類(lèi)型成員*/friend class B;friend class C;
};class B:public A{
public:void show(){cout << x << "---" << y << endl; }
};class C{
public:void show(){A a;cout <<a.x<< "---" << a.y << endl;}
};int main(){B b;b.show();C c;c.show();
}
7、繼承中構(gòu)造函數(shù)、析構(gòu)函數(shù)、賦值運(yùn)算符函數(shù)和拷貝構(gòu)造函數(shù)
構(gòu)造函數(shù)和析構(gòu)函數(shù)是不能被繼承的,但是可以被調(diào)用。并且子類(lèi)一定會(huì)調(diào)用父類(lèi)的構(gòu)造函數(shù);
子類(lèi)默認(rèn)調(diào)用父類(lèi)的無(wú)參構(gòu)造,也可以制定調(diào)用構(gòu)造函數(shù);
析構(gòu)函數(shù)的調(diào)用和構(gòu)造函數(shù)的調(diào)用順序相反;
拷貝構(gòu)造函數(shù)和賦值運(yùn)算符函數(shù)也不能被繼承:在子類(lèi)不提供拷貝構(gòu)造和賦值運(yùn)算符時(shí),子類(lèi)默認(rèn)調(diào)用父類(lèi)的賦值運(yùn)算符和拷貝構(gòu)造函數(shù)。但子類(lèi)一旦提供拷貝構(gòu)造和賦值運(yùn)算符函數(shù)則不再調(diào)用父類(lèi)拷貝構(gòu)造和賦值運(yùn)算符函數(shù)。
[cpp] view plain
copy print?
????#include?<iostream>??using?namespace?std;????class?A{??private:??????int?x;??public:????????????A(int?x?=?0):x(x){??????????cout?<<"A()構(gòu)造"<<endl;??????????cout?<<?x?<<?endl;}??????~A(){cout?<<?"~A()"?<<?endl;}??????int?_get(){??????????return?x;??????}??};????class?B:public?A{??public:????????????????????B():A(100){??????????????????????????????cout?<<?"B()"?<<?endl;??????}???????????????????~B(){cout?<<?"~B()"?<<?endl;}??????int?getbx(){??????????return?_get();??????}??};??int?main(){??????A?a;????????B?b;??????????????cout?<<a._get()<<endl;??????????????cout?<<?b.getbx()<<endl;????????}??
/*繼承中構(gòu)造函數(shù)和析構(gòu)函數(shù)的調(diào)用:構(gòu)造函數(shù)和析構(gòu)函數(shù)不可以
被繼承,但是可以被調(diào)用,而且子類(lèi)肯定會(huì)調(diào)用父類(lèi)的構(gòu)造函數(shù)
和析構(gòu)函數(shù)。這種機(jī)制可以很自然的用于訪問(wèn)父類(lèi)的私有成員*/
#include <iostream>
using namespace std;class A{
private:int x;
public://A(){cout << "A()" << endl;}A(int x = 0):x(x){cout <<"A()構(gòu)造"<<endl;cout << x << endl;}~A(){cout << "~A()" << endl;}int _get(){return x;}
};class B:public A{
public:/*在初始化參數(shù)列表中可以指定調(diào)用父類(lèi)的構(gòu)造函數(shù),指定調(diào)用構(gòu)造函數(shù)并且給父類(lèi)中的私有成員賦值*//*注意:子類(lèi)默認(rèn)調(diào)用父類(lèi)的無(wú)參構(gòu)造,如果下面的代碼沒(méi)有:A(100),則會(huì)調(diào)用無(wú)參構(gòu)造,但是父類(lèi)無(wú)參構(gòu)造被注釋掉,所以會(huì)出錯(cuò)*/B():A(100){//x = 200;//A(100);cout << "B()" << endl;}//訪問(wèn)有參構(gòu)造的方式,理解這種方式的作用/*注意,這種機(jī)制下的構(gòu)造函數(shù)所賦的值是賦到了子類(lèi)中的數(shù)據(jù)x中,而父類(lèi)中的x仍然為0*/~B(){cout << "~B()" << endl;}int getbx(){return _get();}
};
int main(){A a;//構(gòu)建A對(duì)象,此時(shí)A類(lèi)構(gòu)造被調(diào)用,并打印出了值B b;//B類(lèi)為無(wú)參構(gòu)造,首先調(diào)用了A的構(gòu)造,在調(diào)用B的構(gòu)造//打印a對(duì)象中的x成員cout <<a._get()<<endl;//a對(duì)象中的x為0//打印b對(duì)象中的xcout << b.getbx()<<endl;//是100/*一層一層的退,先調(diào)用b的析構(gòu),在調(diào)用a的析構(gòu)*/
}
拷貝構(gòu)造和賦值運(yùn)算符的問(wèn)題
[cpp] view plain
copy print?
#include?<iostream>??using?namespace?std;?????class?A{??????int?arr;??public:??????A(){}????????????A(const?A&?a){??????????cout?<<?"父類(lèi)拷貝構(gòu)造"?<<?endl;??????}??????void?operator=(const?A&?a){??????????cout?<<?"父類(lèi)賦值運(yùn)算符函數(shù)"?<<?endl;??????}??};????class?B:public?A{????????public:??????B(){}??????B(const?B&?b):A(b){??????????????????????????????????????????cout?<<?"子類(lèi)拷貝構(gòu)造"?<<?endl;??????}??????void?operator=(const?B&?b){??????????A::operator=(b);???????????????????cout?<<?"子類(lèi)賦值運(yùn)算符函數(shù)"<<?endl;??????}??};????int?main(){??????B?a;??????B?b?=?a;??????B?c;??????c?=?a;??}??
#include <iostream>
using namespace std;
/*系統(tǒng)一旦提供構(gòu)造函數(shù),系統(tǒng)默認(rèn)的構(gòu)造函數(shù)將被回收
記住,拷貝構(gòu)造也是構(gòu)造函數(shù)*/
class A{int arr;
public:A(){}//A(int x = 0):arr(x){}A(const A& a){cout << "父類(lèi)拷貝構(gòu)造" << endl;}void operator=(const A& a){cout << "父類(lèi)賦值運(yùn)算符函數(shù)" << endl;}
};
/*有指針類(lèi)型的成員時(shí),采用默認(rèn)機(jī)制就麻煩了*/
class B:public A{//int * pi;
public:B(){}B(const B& b):A(b){ //子類(lèi)中提供了拷貝構(gòu)造函數(shù)將不再調(diào)用父類(lèi)的拷貝構(gòu)造cout << "子類(lèi)拷貝構(gòu)造" << endl;}void operator=(const B& b){A::operator=(b); //調(diào)用父類(lèi)的拷貝構(gòu)造函數(shù)的機(jī)制cout << "子類(lèi)賦值運(yùn)算符函數(shù)"<< endl;}
};int main(){B a;B b = a;B c;c = a;
}
8,名字隱藏
名字隱藏機(jī)制:子類(lèi)中如果定義了和父類(lèi)中同名的數(shù)據(jù),這些數(shù)據(jù)包括成員變量和成員函數(shù)。則會(huì)把父類(lèi)中的數(shù)據(jù)隱藏掉。
注意:只要名字相同,計(jì)算返回值或者形參列表不同,也會(huì)被隱藏。隱藏不代表就沒(méi)有了,可以通過(guò)類(lèi)名作用域::訪問(wèn)到被隱藏的成員。
[cpp] view plain
copy print?
#include?<iostream>??using?namespace?std;????class?A{??public:??????int?x;??????int?show(){??????????cout?<<?"show?A"?<<?endl;??????????return?0;??????}??????A(){x=20;}??????A(int?x):x(x){cout?<<?"show?A(int?x)"?<<?endl;}??????void?shouu(){??????????cout?<<"shouu()"<<endl;??????}??};????class?B:public?A{??public:??????int?x;??????int?y;??????B(){cout?<<?"B()"?<<?endl;}??????void?show(){??????????cout?<<?"show?B"?<<?endl;????????????????}??};????int?main(){??????B?b;??????b.shouu();??????????????????????????}??
#include <iostream>
using namespace std;class A{
public:int x;int show(){cout << "show A" << endl;return 0;}A(){x=20;}A(int x):x(x){cout << "show A(int x)" << endl;}void shouu(){cout <<"shouu()"<<endl;}
};class B:public A{
public:int x;int y;B(){cout << "B()" << endl;}void show(){cout << "show B" << endl;//A::show();}
};int main(){B b;b.shouu();//cout << b.x << endl;//cout << b.A::x << endl; //突破名字隱藏機(jī)制//int c = b.show();被隱藏,無(wú)法訪問(wèn)//b.A::show();
}
9、多繼承和函數(shù)重寫(xiě)
多繼承是c++特有的語(yǔ)法機(jī)制,表現(xiàn)為一個(gè)子類(lèi)有多個(gè)直接的父類(lèi)。
[cpp] view plain
copy print?
#include?<iostream>??using?namespace?std;????class?phone{??????double?price;??public:????????????phone(double?price?=?15):price(price){cout?<<?"phone"?<<?endl;}??????~phone(){cout?<<?"~phone"?<<?endl;}??????void?call(){??????????cout?<<?"use?calling"?<<?endl;??????}??????double?getprice(){??????????return?price;??????}??};????class?MP3{??????double?price;??public:??????MP3(double?price?=?20):price(price){cout?<<?"MP3"?<<?endl;}??????~MP3(){cout?<<?"~MP3"?<<?endl;}??????void?play(){??????????cout?<<?"use?to?listening?music"?<<?endl;??????}??????double?getprice(){??????????return?price;??????}??};?????class?vedio{??????double?price;??public:??????vedio(double?price?=?0):price(price){cout?<<?"vedio"?<<?endl;}??????~vedio(){cout?<<?"~vedio"?<<?endl;}??????void?vcd(){??????????cout?<<?"watch?vedio"?<<?endl;??????}???????double?getprice(){??????????return?price;??????}??};????class?iphone:public?phone,public?MP3,public?vedio{??public:??????double?getprice(){??????????return?phone::getprice()?+?MP3::getprice()?+?vedio::getprice();??????}??};????int?main(){??????iphone?iphone6;????????????cout?<<?iphone6.MP3::getprice()?<<?endl;??????cout?<<?iphone6.phone::getprice()?<<?endl;??????cout?<<?iphone6.getprice()?<<?endl;??????}??
#include <iostream>
using namespace std;class phone{double price;
public://phone();phone(double price = 15):price(price){cout << "phone" << endl;}~phone(){cout << "~phone" << endl;}void call(){cout << "use calling" << endl;}double getprice(){return price;}
};class MP3{double price;
public:MP3(double price = 20):price(price){cout << "MP3" << endl;}~MP3(){cout << "~MP3" << endl;}void play(){cout << "use to listening music" << endl;}double getprice(){return price;}
}; class vedio{double price;
public:vedio(double price = 0):price(price){cout << "vedio" << endl;}~vedio(){cout << "~vedio" << endl;}void vcd(){cout << "watch vedio" << endl;} double getprice(){return price;}
};
/*多繼承*/
class iphone:public phone,public MP3,public vedio{
public:double getprice(){return phone::getprice() + MP3::getprice() + vedio::getprice();}
};int main(){iphone iphone6;//cout << sizeof(iphone) << endl;cout << iphone6.MP3::getprice() << endl;cout << iphone6.phone::getprice() << endl;cout << iphone6.getprice() << endl; //用名字隱藏機(jī)制解決多分?jǐn)?shù)據(jù)同名沖突的問(wèn)題
}
多繼承遇到的問(wèn)題:上面的代碼用sizeof就可以看到,子類(lèi)在多繼承的時(shí)候會(huì)多次復(fù)制頂層數(shù)據(jù),而我們期望的是price這個(gè)成員只需要復(fù)制一份就可以了,因?yàn)槎嘤嗟膹?fù)制是無(wú)意義的。首先采用頂層抽象的方式,將三個(gè)父類(lèi)抽象到更高的層面上。
[cpp] view plain
copy print?
#include?<iostream>??using?namespace?std;????class?product{??????double?price;??public:??????double?getprice(){??????????return?price;??????}??????product(double?price?=?0):price(price){cout?<<"product"<<endl;}??};??class?phone:public?product{??public:????????????phone(double?price?=?15):product(price){cout?<<?"phone"?<<?endl;}??????~phone(){cout?<<?"~phone"?<<?endl;}??????void?call(){??????????cout?<<?"use?calling"?<<?endl;??????}??};????class?MP3:public?product{??public:??????MP3(double?price?=?20):product(price){cout?<<?"MP3"?<<?endl;}??????~MP3(){cout?<<?"~MP3"?<<?endl;}??????void?play(){??????????cout?<<?"use?to?listening?music"?<<?endl;??????}??};?????class?vedio:public?product{??public:??????vedio(double?price?=?0):product(price){cout?<<?"vedio"?<<?endl;}??????~vedio(){cout?<<?"~vedio"?<<?endl;}??????void?vcd(){??????????cout?<<?"watch?vedio"?<<?endl;??????}???};??class?iphone:public?phone,public?MP3,public?vedio{????????};????int?main(){??????iphone?iphone6;??????????????????cout?<<?iphone6.MP3::getprice()?<<?endl;??????cout?<<?iphone6.phone::getprice()?<<?endl;????????}??
#include <iostream>
using namespace std;
/*抽象到更高層的類(lèi)中*/
class product{double price;
public:double getprice(){return price;}product(double price = 0):price(price){cout <<"product"<<endl;}
};
class phone:public product{
public://phone();phone(double price = 15):product(price){cout << "phone" << endl;}~phone(){cout << "~phone" << endl;}void call(){cout << "use calling" << endl;}
};class MP3:public product{
public:MP3(double price = 20):product(price){cout << "MP3" << endl;}~MP3(){cout << "~MP3" << endl;}void play(){cout << "use to listening music" << endl;}
}; class vedio:public product{
public:vedio(double price = 0):product(price){cout << "vedio" << endl;}~vedio(){cout << "~vedio" << endl;}void vcd(){cout << "watch vedio" << endl;}
};
class iphone:public phone,public MP3,public vedio{};int main(){iphone iphone6;//cout << iphone6.getprice() << endl;同樣會(huì)產(chǎn)生沖突的問(wèn)題//cout << sizeof(iphone) << endl;cout << iphone6.MP3::getprice() << endl;cout << iphone6.phone::getprice() << endl;//cout << iphone6.getprice() << endl;//直接調(diào)用產(chǎn)生沖突問(wèn)題,編譯器不知道該調(diào)用哪一個(gè)
}
上面的代碼中,product的構(gòu)造函數(shù) 被調(diào)用了三次,因?yàn)檫@種繼承是一級(jí)一級(jí)的來(lái)的,構(gòu)造子類(lèi)的時(shí)候找父類(lèi),發(fā)現(xiàn)父類(lèi)還有父類(lèi),就去調(diào)用爺爺類(lèi)的構(gòu)造函數(shù),三次繼承,三次調(diào)用。
這種繼承方式構(gòu)成了一種菱形或者鉆石型的繼承,叫做菱形繼承或者鉆石繼承,但鉆石繼承并沒(méi)有實(shí)際解決數(shù)據(jù)多次復(fù)制的問(wèn)題,為了解決菱形繼承,c++提出了虛繼承。虛繼承就是在繼承的時(shí)候加上virtual關(guān)鍵字修飾即可。虛繼承對(duì)于共同的成員父親類(lèi)從爺爺類(lèi)那里繼承來(lái)的,這里為double price,子類(lèi)直接越級(jí)訪問(wèn),直接從爺爺類(lèi)那里繼承price。
[cpp] view plain
copy print?
??#include?<iostream>??using?namespace?std;????class?product{??????int?price;??public:??????int??getprice(){??????????return?price;??????}??????product(double?price?=?0):price(price){cout?<<?"product"?<<?endl;}??};????class?phone:virtual?public?product{??public:????????????phone(double?price?=?15):product(price){cout?<<?"phone"?<<?endl;}??????~phone(){cout?<<?"~phone"?<<?endl;}??????void?call(){??????????cout?<<?"use?calling"?<<?endl;??????}??};????class?MP3:virtual?public?product{??public:??????MP3(double?price?=?20):product(price){cout?<<?"MP3"?<<?endl;}??????~MP3(){cout?<<?"~MP3"?<<?endl;}??????void?play(){??????????cout?<<?"use?to?listening?music"?<<?endl;??????}??};?????class?vedio:virtual?public?product{??public:??????vedio(double?price?=?0):product(price){cout?<<?"vedio"?<<?endl;}??????~vedio(){cout?<<?"~vedio"?<<?endl;}??????void?vcd(){??????????cout?<<?"watch?vedio"?<<?endl;??????}???};??class?iphone:virtual?public?phone,virtual?public?MP3,virtual?public?vedio{??public:??????iphone(int?m?=?0,int?v?=?0,int?p?=?0):product(m?+?p?+?v){}??};?????int?main(){??????iphone?iphone6(1000,2041,3201);????????????????????????????????????cout?<<?sizeof(iphone)?<<?endl;??????cout?<<?iphone6.getprice()?<<?endl;??}??
/*類(lèi)中也會(huì)有對(duì)齊和補(bǔ)齊*/
#include <iostream>
using namespace std;
/*抽象到更高層的類(lèi)中*/
class product{int price;
public:int getprice(){return price;}product(double price = 0):price(price){cout << "product" << endl;}
};class phone:virtual public product{
public://phone();phone(double price = 15):product(price){cout << "phone" << endl;}~phone(){cout << "~phone" << endl;}void call(){cout << "use calling" << endl;}
};class MP3:virtual public product{
public:MP3(double price = 20):product(price){cout << "MP3" << endl;}~MP3(){cout << "~MP3" << endl;}void play(){cout << "use to listening music" << endl;}
}; class vedio:virtual public product{
public:vedio(double price = 0):product(price){cout << "vedio" << endl;}~vedio(){cout << "~vedio" << endl;}void vcd(){cout << "watch vedio" << endl;}
};
class iphone:virtual public phone,virtual public MP3,virtual public vedio{
public:iphone(int m = 0,int v = 0,int p = 0):product(m + p + v){}
};
/*虛函數(shù)之后,product的構(gòu)造函數(shù)只被調(diào)用了一次,孫子類(lèi)直接越級(jí)訪問(wèn)
了product類(lèi)*/
int main(){iphone iphone6(1000,2041,3201);//cout << iphone6.getprice() << endl;同樣會(huì)產(chǎn)生沖突的問(wèn)題//cout << sizeof(iphone) << endl;//cout << iphone6.MP3::getprice() << endl;//cout << iphone6.phone::getprice() << endl;//cout << iphone6.getprice() << endl;直接調(diào)用產(chǎn)生沖突問(wèn)題,編譯器不知道該調(diào)用哪一個(gè)cout << sizeof(iphone) << endl;cout << iphone6.getprice() << endl;
}
這個(gè)代碼中product的構(gòu)造函數(shù)只調(diào)用了一次,說(shuō)明子類(lèi)直接越級(jí)訪問(wèn)了爺爺類(lèi)的數(shù)據(jù)。而對(duì)于父類(lèi)特有的子類(lèi)照常繼承,只是沒(méi)有通過(guò)父類(lèi)去繼承爺爺類(lèi)的數(shù)據(jù)成員,所以product的構(gòu)造函數(shù)只被調(diào)用了一次。
虛函數(shù):在函數(shù)前面加上virtual關(guān)鍵字修飾過(guò)的就是虛函數(shù).
#include <iostream>
using namespace std;
class A{
int x;
public:
virtual void show(){}
virtual void showa(){}
};
int main(){
cout << sizeof(A) << endl;
}
虛函數(shù)的主要表現(xiàn)為會(huì)占用四個(gè)字節(jié)的空間,只要成員中出現(xiàn)虛函數(shù),不管有多少個(gè)虛函數(shù),都只用四個(gè)字節(jié)來(lái)維護(hù)這個(gè)虛關(guān)系。虛函數(shù)會(huì)影響對(duì)象的大小。維護(hù)虛關(guān)系使用一個(gè)指針來(lái)維護(hù)的,所以是四個(gè)字節(jié)。
函數(shù)重寫(xiě):
在父類(lèi)中出現(xiàn)一個(gè)虛函數(shù),如果在子類(lèi)中提供和父類(lèi)同名的函數(shù)(注意區(qū)分名字隱藏),這就加函數(shù)重寫(xiě)。
函數(shù)重寫(xiě)要求必須有相同函數(shù)名,相同的參數(shù)列表,相同的返回值。
三、c++面向?qū)ο笾鄳B(tài)
1、多態(tài):一個(gè)父類(lèi)型的對(duì)象的指針或者引用指向或者是引用一個(gè)子類(lèi)對(duì)象時(shí),調(diào)用父類(lèi)型中的虛函數(shù),如果子類(lèi)覆蓋了虛函數(shù),則調(diào)用的表現(xiàn)是子類(lèi)覆蓋之后的。
繼承是構(gòu)成多態(tài)的基礎(chǔ);
虛函數(shù)是構(gòu)成多態(tài)的關(guān)鍵;
函數(shù)覆蓋是構(gòu)成多態(tài)的必備條件;
多態(tài)的應(yīng)用:函數(shù)參數(shù),函數(shù)返回值。
多態(tài)的產(chǎn)生必須依靠上面的四點(diǎn),缺一不可。
2、多態(tài)的應(yīng)用,多態(tài)相對(duì)做到了通用類(lèi)型編程,主要用在函數(shù)參數(shù)和函數(shù)的返回值上。
[cpp] view plain
copy print?
????#include?<iostream>??using?namespace?std;??class?Animal{??public:??????virtual?void?run(){??????????cout?<<"Ainimal?run()"<<endl;??????}??????void?show(){??????????cout?<<"Animal?show()"<<endl;??????}??};????class?Dog:public?Animal{??public:??????void?run(){??????????cout?<<"Dog?run()"<<endl;??????}??????void?show(){??????????cout?<<"dog?show()"<<endl;??????}??};????class?Cat:public?Animal{??public:??????void?run(){??????????cout?<<"cat?run()"<<endl;??????}??};????void?showAnimal(Animal?*?animal){??????animal->show();??????animal->run();??}????Animal?*?getAnimal(int?x){??????if?(1?==?x)??????????return?new?Dog();??????if?(2?==?x)??????????return?new?Cat();??}??int?main(){??????Cat?cat;??????showAnimal(&cat);??????Dog?dog;??????showAnimal(&dog);??}??
/*多態(tài)的應(yīng)用:
1、函數(shù)參數(shù)
2、函數(shù)返回值*/
#include <iostream>
using namespace std;
class Animal{
public:virtual void run(){cout <<"Ainimal run()"<<endl;}void show(){cout <<"Animal show()"<<endl;}
};class Dog:public Animal{
public:void run(){cout <<"Dog run()"<<endl;}void show(){cout <<"dog show()"<<endl;}
};class Cat:public Animal{
public:void run(){cout <<"cat run()"<<endl;}
};
/*多態(tài)用作函數(shù)參數(shù)*/
void showAnimal(Animal * animal){animal->show();animal->run();
}
/*多態(tài)用作返回值*/
Animal * getAnimal(int x){if (1 == x)return new Dog();if (2 == x)return new Cat();
}
int main(){Cat cat;showAnimal(&cat);Dog dog;showAnimal(&dog);
}
3、多態(tài)的實(shí)現(xiàn)原理
多態(tài)的實(shí)現(xiàn)主要依賴(lài)于下面的三個(gè)東西:
虛函數(shù):成員函數(shù)加了virtual修飾
虛函數(shù)表指針:一個(gè)類(lèi)型有虛函數(shù),則對(duì)這個(gè)類(lèi)型提供一個(gè)指針,這個(gè)指針?lè)旁谏蓪?duì)象的前四個(gè)字節(jié)。同類(lèi)型的對(duì)象共享一張?zhí)摵瘮?shù)表。并且不同類(lèi)型的虛函數(shù)表地址不同。
虛函數(shù)表:虛函數(shù)表中的每個(gè)元素都是虛函數(shù)的地址。
一個(gè)類(lèi)型一旦出現(xiàn)虛函數(shù),則會(huì)生成一張?zhí)摵瘮?shù)表,虛函數(shù)表中存放的就是虛函數(shù)的函數(shù)地址,通過(guò)這個(gè)函數(shù)地址可以到代碼區(qū)中去執(zhí)行對(duì)應(yīng)的函數(shù)。虛函數(shù)表中只存放類(lèi)型中的虛函數(shù),不是虛函數(shù)的一概不管。在每個(gè)生成的類(lèi)型對(duì)象的前四個(gè)字節(jié)中存放的是虛函數(shù)表指針,通過(guò)這個(gè)指針可以訪問(wèn)到虛函數(shù)表,從而訪問(wèn)其中的函數(shù)。同種類(lèi)型共享虛函數(shù)表,不同類(lèi)型有自己獨(dú)立的虛函數(shù)表,繼承關(guān)系中的子類(lèi)和父類(lèi)屬于不同類(lèi)型,所以有自己獨(dú)立的函數(shù)表。
[cpp] view plain
copy print?
??#include?<iostream>??#include?<cstring>??using?namespace?std;??class?Animal{??????int?x;??public:??????virtual?void?fun(){??????????cout<<?"Aniaml?fun()"<<endl;??????}??????virtual?void?run(){??????????cout?<<"Animal?run()"<<endl;??????}??????void?show(){??????????cout?<<"Animal?show()"<<endl;??????}??};????class?Dog:public?Animal{??public:??????virtual?void?fun(){??????????cout?<<?"dog?run"<<endl;??????}??????void?run(){??????????cout?<<"dog?run"<<endl;??????}??};????class?Cat:public?Animal{??public:??????void?fun(){??????????cout?<<"cat?fun"<<endl;??????}??};????int?main(){??????Animal?a;??????Animal?b;????????????int?*?pi?=?(int*)&a;??????cout?<<showbase<<?hex?<<?*pi<<endl;????????pi?=?reinterpret_cast<int*>(&b);??????cout?<<showbase<<?hex?<<?*pi<<endl;??????????????Dog?dog;??????pi?=?reinterpret_cast<int*>(&dog);??????cout?<<showbase<<?hex?<<?*pi<<endl;????????Animal?*?pcat?=?new?Cat();??????pcat->run();??????pcat->fun();???????????????Animal?*?pdog?=?new?Dog();??????pdog->run();??????pdog->fun();???????????????memcpy(pdog,pcat,4);??????pdog->run();??????pdog->fun();????????}??
/*多態(tài)的原理*/
#include <iostream>
#include <cstring>
using namespace std;
class Animal{int x;
public:virtual void fun(){cout<< "Aniaml fun()"<<endl;}virtual void run(){cout <<"Animal run()"<<endl;}void show(){cout <<"Animal show()"<<endl;}
};class Dog:public Animal{
public:virtual void fun(){cout << "dog run"<<endl;}void run(){cout <<"dog run"<<endl;}
};class Cat:public Animal{
public:void fun(){cout <<"cat fun"<<endl;}
};int main(){Animal a;Animal b;/*取出虛函數(shù)表的地址并打印*/int * pi = (int*)&a;cout <<showbase<< hex << *pi<<endl;pi = reinterpret_cast<int*>(&b);cout <<showbase<< hex << *pi<<endl;/*子類(lèi)不會(huì)和父類(lèi)共享虛函數(shù)表,地址不一樣*/Dog dog;pi = reinterpret_cast<int*>(&dog);cout <<showbase<< hex << *pi<<endl;Animal * pcat = new Cat();pcat->run();pcat->fun();/*更改dog的虛表的值,我們把dog的虛表地址改成cat的虛表地址*/Animal * pdog = new Dog();pdog->run();pdog->fun();/*更換dog的虛表地址,將cat的前四個(gè)字節(jié)移動(dòng)到dog的前四個(gè)字節(jié)*/memcpy(pdog,pcat,4);pdog->run();pdog->fun();/*上面的更改后,狗變成了貓的特性*/
}
上述程序?qū)?yīng)的內(nèi)存圖:
可以看出,一旦滿(mǎn)足了多態(tài)的條件,程序自然按照上圖的流程執(zhí)行。
4、上面既然說(shuō)了,虛函數(shù)表中存放的是函數(shù)的地址,那么能不能直接我們自己取出虛函數(shù)的地址,直接調(diào)用所需要的函數(shù)呢?
[cpp] view plain
copy print?
#include?<iostream>??using?namespace?std;????class?Animal{??public:??????virtual?void?run(int?x){??????????cout?<<"run?x="<<x<<endl;??????}??????virtual?void?fun(int?x){??????????cout?<<"fun?x="<<x<<endl;??????}??????void?show(){??????????cout?<<"this?is?show()"<<endl;??????}??};??????int?main()??{????????????typedef?void?(*MFUN)(Animal*?a,int?x);????????typedef?MFUN*?VTABLE;????????Animal?animal;??????VTABLE?vt?=?*((VTABLE*)&animal);????????????vt[0](&animal,100);??????vt[1](&animal,123);??????return?0;??}??
#include <iostream>
using namespace std;class Animal{
public:virtual void run(int x){cout <<"run x="<<x<<endl;}virtual void fun(int x){cout <<"fun x="<<x<<endl;}void show(){cout <<"this is show()"<<endl;}
};int main()
{/*去掉函數(shù)名就是函數(shù)指針的類(lèi)型,指針簡(jiǎn)化操作*/typedef void (*MFUN)(Animal* a,int x);/*MFUN就是虛表中的函數(shù)指針類(lèi)型*/typedef MFUN* VTABLE;//MFUN*就是虛表類(lèi)型Animal animal;VTABLE vt = *((VTABLE*)&animal);/*虛函數(shù)表表現(xiàn)為函數(shù)指針數(shù)組*/vt[0](&animal,100);vt[1](&animal,123);return 0;
}
虛表中存放的就是虛函數(shù)的函數(shù)指針,可以理解為函數(shù)指針的數(shù)組,通過(guò)typedef將指針降級(jí)。
5,虛析構(gòu)函數(shù)
virtual關(guān)鍵字只能修飾成員函數(shù)或者析構(gòu)函數(shù),其他的函數(shù)都不行。
當(dāng)我們用new創(chuàng)建一個(gè)指向子類(lèi)對(duì)象的父類(lèi)指針時(shí),例如Animal * animal = new Dog()時(shí),其中Animal時(shí)父類(lèi),Dog是子類(lèi),并且delete animal時(shí),其子類(lèi)對(duì)象的析構(gòu)函數(shù)不會(huì)被調(diào)用,只會(huì)調(diào)用父類(lèi)的析構(gòu)函數(shù)。所以就會(huì)遇到一個(gè)問(wèn)題,如果子類(lèi)對(duì)象有自己獨(dú)立的堆內(nèi)存時(shí),這部分內(nèi)存就無(wú)法釋放。這時(shí),我們只需要在父類(lèi)的析構(gòu)函數(shù)上用virtual修飾即可,子類(lèi)析構(gòu)函數(shù)就會(huì)被調(diào)用。
[cpp] view plain
copy print?
#include?<iostream>??using?namespace?std;????class?Animal{??public:??????Animal(){??????????cout?<<"Animal()"<<endl;??????}??????virtual?~Animal(){??????????cout?<<"~Animal()"<<endl;??????}??};??class?Dog:public?Animal{??public:??????Dog(){??????????cout?<<"Dog()"<<endl;??????}??????~Dog(){??????????cout?<<"~Dog()"<<endl;??????}??};??int?main(){??????Animal?*?pa?=?new?Dog();??????delete?pa;????????}??
#include <iostream>
using namespace std;class Animal{
public:Animal(){cout <<"Animal()"<<endl;}virtual ~Animal(){cout <<"~Animal()"<<endl;}
};
class Dog:public Animal{
public:Dog(){cout <<"Dog()"<<endl;}~Dog(){cout <<"~Dog()"<<endl;}
};
int main(){Animal * pa = new Dog();delete pa;/*子類(lèi)析構(gòu)函數(shù)的調(diào)用必然引發(fā)父類(lèi)析構(gòu)*/
}
虛析構(gòu)函數(shù)的應(yīng)用場(chǎng)景:
當(dāng)父類(lèi)中有虛函數(shù)時(shí),應(yīng)該吧父類(lèi)的析構(gòu)函數(shù)定義成虛析構(gòu)函數(shù)。
子類(lèi)和父類(lèi)中都有自己的堆內(nèi)存分配時(shí)。
總結(jié)
以上是生活随笔為你收集整理的c++面向对象三大特征封装、继承和多态知识总结的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
如果覺(jué)得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。