C++繼承、派生、訪問(wèn)權(quán)限、成員對(duì)象、插入運(yùn)算符重載
#include <iostream>
using namespace std;
//繼承inherit 派生derive。子類(lèi)繼承父類(lèi)?;?lèi)派生出派生類(lèi)。兩個(gè)詞描述的是一件事情
class ClassBase1 //聲明基類(lèi)
{
????public:
???? int pub; //公有
????protected:
???? int pro; //保護(hù),類(lèi)及子類(lèi)外不可調(diào)用
????private:
???? int pri; //私有,類(lèi)外不可調(diào)用
};
class ClassDerive1 :public ClassBase1 //派生類(lèi)。繼承基類(lèi)。繼承的格式為: class 類(lèi)名:[public/protected/private] 基類(lèi)名 ,訪問(wèn)修飾符不寫(xiě)默認(rèn)private
{
???? void accessbasemember()
???? {
???????? pub = 3; //在public繼承基類(lèi)時(shí),基類(lèi)的public成員在子類(lèi)中仍然是public可直接訪問(wèn)
???????? pro = 2; //在public繼承基類(lèi)時(shí),基類(lèi)的protected成員在子類(lèi)中仍然是protected可直接訪問(wèn),但類(lèi)及子類(lèi)外不可訪問(wèn)。比如在main中創(chuàng)建對(duì)象,通過(guò)對(duì)象不能調(diào)用protected成員
???????? //報(bào)錯(cuò):pri = 1; //在public繼承基類(lèi)時(shí),基類(lèi)的private成員在子類(lèi)中不可訪問(wèn),要想訪問(wèn)需要通過(guò)父類(lèi)的接口。所以private成員和protected成員的區(qū)別是子類(lèi)中是否能訪問(wèn),在類(lèi)外通過(guò)創(chuàng)建對(duì)象的話兩者都不能訪問(wèn)。同樣子類(lèi)的子類(lèi)不可以訪問(wèn)父類(lèi)們(無(wú)論哪一級(jí))的private成員。即ClassDerive1的子類(lèi)也不可訪問(wèn)ClassBase1的private成員
???? }
};
class ProtectedDerive1 :protected ClassBase1 //protected繼承,會(huì)將父類(lèi)的所有public成員作為protected訪問(wèn)權(quán)限
{
???? void accessbasemember()
???? {
???????? pub = 20; //這里pub變成了protected。即外部不能通過(guò)創(chuàng)建這個(gè)類(lèi)的對(duì)象來(lái)訪問(wèn)了(不影響父類(lèi)的對(duì)象訪問(wèn)public成員)。且這個(gè)類(lèi)的public/protected子類(lèi)也將pub作為protected訪問(wèn)來(lái)繼承
???????? pro = 10; //依然是protected保持不變
???????? //pri = 0; 依然不能訪問(wèn),且子子類(lèi)也不能,所以如果需要在子類(lèi)中直接使用這個(gè)成員,并且不希望外部訪問(wèn)這個(gè)成員,就應(yīng)該聲明為protected
???? }
};
class PrivateDerive1 :private ClassBase1 //private繼承,可省略,不寫(xiě)修飾詞默認(rèn)是private
{
void accessbasemember()
{
???? pub = 30; //pub作為private繼承。這意味著外部不能調(diào)用。且子子類(lèi)不能訪問(wèn)
???? pro = 20; //pro作為private繼承。這使得這個(gè)成員在子子類(lèi)中不能訪問(wèn)了。任何這個(gè)類(lèi)的子類(lèi)(無(wú)論三種訪問(wèn)修飾)都不能訪問(wèn)這個(gè)類(lèi)的任何成員了
???? //pri = 0; 依然不能訪問(wèn)
}
//雖然父類(lèi)們的private成員不能訪問(wèn),但可以通過(guò)父類(lèi)們的接口訪問(wèn),這說(shuō)明父類(lèi)們所有的數(shù)據(jù)成員都會(huì)被繼承,只是有些不能訪問(wèn)
//三種繼承方式不影響子類(lèi)中的新成員訪問(wèn)權(quán)限
public:
int d1; //依然公有
protected:
int d2;
private:
int d3;
public:
using ClassBase1::pub; //在私有繼承中可以通過(guò)using 基類(lèi)::成員 將基類(lèi)的數(shù)據(jù)成員或成員函數(shù)重新定義訪問(wèn)權(quán)限,原本因?yàn)樗接欣^承而私有的pub在public下using使得對(duì)外部可見(jiàn)
};
//通常使用public繼承
class ClassBase2 //父類(lèi)
{
????public:
???? int base;
???? ClassBase2() { cout << "base no parameter" << endl; } //無(wú)參構(gòu)造器
???? ClassBase2(int base)
???? :base(base) //通過(guò)列表初始化時(shí)不能使用this,也就是說(shuō)這里的初始化發(fā)生在產(chǎn)生對(duì)象之前
???? { cout << "base 1 parameter" << endl; } //有參構(gòu)造器
???? ~ClassBase2() { cout << "base destructor" << endl; } //析構(gòu)器
???? ClassBase2(const ClassBase2& another):base(another.base){ cout << "base copy" << endl; } //拷貝構(gòu)造器
???? ClassBase2& operator=(const ClassBase2& another) //賦值運(yùn)算符重載
???? //: base(another.base)? 報(bào)錯(cuò)。說(shuō)明列表初始化只能在構(gòu)造階段使用。對(duì)已有的對(duì)象重新賦值不能調(diào)用
???? {
???????? if(&another!=this)
???????? ???? base = another.base;
???????? cout << "base operator=" << endl;
???????? return *this;
???? }
};
class ClassDerive2:public ClassBase2{}; //子類(lèi)。子類(lèi)會(huì)繼承父類(lèi) 除構(gòu)造器、析構(gòu)器、拷貝構(gòu)造器、賦值重載 之外的所有內(nèi)容。 這里子類(lèi)沒(méi)有手動(dòng)定義構(gòu)造器等,程序默認(rèn)生成public的四類(lèi)函數(shù)
int maininherit1()
{
???? ClassBase2 b1; //調(diào)用"base no parameter" "base destructor"
???? ClassDerive2 d1; //調(diào)用"base no parameter" "base destructor",即子類(lèi)自動(dòng)生成的構(gòu)造器會(huì)自動(dòng)調(diào)用父類(lèi)的 無(wú)參 構(gòu)造器,且先構(gòu)造父類(lèi)再構(gòu)造子類(lèi)。子類(lèi)自動(dòng)生成的析構(gòu)器會(huì)自動(dòng)調(diào)用父類(lèi)的析構(gòu)器,且先析構(gòu)子類(lèi)再析構(gòu)父類(lèi)(后進(jìn)先出),無(wú)論子類(lèi)是否顯式聲明了析構(gòu)器,子類(lèi)的析構(gòu)器在用戶(hù)定義的代碼之后會(huì)自動(dòng)調(diào)用父類(lèi)的析構(gòu)器
???? ClassBase2 b2(2); //帶參構(gòu)造器 調(diào)用"base 1 parameter" "base destructor"
???? //報(bào)錯(cuò):ClassDerive2 b2(2); 子類(lèi)不繼承父類(lèi)的任何構(gòu)造器
???? ClassBase2 b3(b2); //調(diào)用"base copy" 析構(gòu)器不再贅述
???? ClassDerive2 d2(d1); //調(diào)用"base copy" ,子類(lèi)默認(rèn)生成的拷貝構(gòu)造器會(huì)自動(dòng)調(diào)用父類(lèi)的拷貝構(gòu)造器
???? d2 = d1; //調(diào)用"base operator=" ,子類(lèi)默認(rèn)生成的賦值運(yùn)算符重載函數(shù)會(huì)自動(dòng)調(diào)用父類(lèi)的
???? //所以子類(lèi)雖然不繼承父類(lèi)的這四類(lèi)函數(shù),卻會(huì)在默認(rèn)情況下使用,這里不繼承是指不會(huì)將父類(lèi)的四類(lèi)函數(shù)作為當(dāng)前函數(shù)的四類(lèi)函數(shù)以直接使用
????
???? //對(duì)象的創(chuàng)建必須進(jìn)行初始化。所以ClassDerive2對(duì)象創(chuàng)建時(shí)調(diào)用了ClassDerive2的構(gòu)造器
???? //但ClassDerive2對(duì)象在屬于ClassDerive2類(lèi)之前先屬于ClassBase2類(lèi)。在分配好內(nèi)存之后,程序先對(duì)ClassDerive2內(nèi)存塊中的ClassBase2部分初始化,再對(duì)ClassDerive2部分進(jìn)行初始化。所以雖然看起來(lái)是創(chuàng)建了兩個(gè)對(duì)象,實(shí)際是子類(lèi)對(duì)象中保留的父類(lèi)對(duì)象區(qū)域,而既然這個(gè)區(qū)域是父類(lèi)對(duì)象,就要進(jìn)行對(duì)象的初始化,所以會(huì)執(zhí)行父類(lèi)的構(gòu)造器
???? return 0;
}
class ClassDerive3 :public ClassBase2 //另一個(gè)子類(lèi)
{
????public:
???? ClassDerive3() {} //手動(dòng)聲明默認(rèn)構(gòu)造器,程序不再自動(dòng)生成。這里沒(méi)有顯式調(diào)用父類(lèi)構(gòu)造器,默認(rèn)會(huì)調(diào)用父類(lèi)的無(wú)參構(gòu)造器來(lái)初始化父類(lèi)的區(qū)域。如果父類(lèi)沒(méi)有無(wú)參構(gòu)造器就會(huì)報(bào)錯(cuò)
???? ClassDerive3(int v) //手動(dòng)聲明有參構(gòu)造器,只要是構(gòu)造器(無(wú)論默認(rèn)還是拷貝),只要沒(méi)有顯式調(diào)用父類(lèi)構(gòu)造器,就會(huì)默認(rèn)調(diào)用父類(lèi)無(wú)參構(gòu)造器
???? :ClassBase2(v) //顯式調(diào)用父類(lèi)構(gòu)造器,調(diào)用的位置只能寫(xiě)在這里,不能寫(xiě)在{}中,如果不寫(xiě)的話默認(rèn)在這里寫(xiě)作:ClassBase2()無(wú)參構(gòu)造器。
???? //:ClassBase2() //無(wú)參可省略,也就意味著調(diào)用子類(lèi)的這個(gè)構(gòu)造器,在執(zhí)行到{}之前,都會(huì)調(diào)用冒號(hào)這里指定的父類(lèi)構(gòu)造器來(lái)初始化
???? {
???? //錯(cuò)誤:ClassBase2(v);如果在這里寫(xiě),就是額外生成一個(gè)當(dāng)前塊作用域內(nèi)的對(duì)象,而非對(duì)當(dāng)前對(duì)象的父類(lèi)區(qū)域進(jìn)行父類(lèi)初始化
???? }
???? ClassDerive3(const ClassDerive3& another)
???? //這里不寫(xiě)父類(lèi)構(gòu)造器的話,默認(rèn)使用父類(lèi)無(wú)參構(gòu)造器而不是拷貝構(gòu)造器。因?yàn)榭截悩?gòu)造器也只是構(gòu)造器的一種重載。不手動(dòng)定義拷貝構(gòu)造器會(huì)把調(diào)用父類(lèi)拷貝構(gòu)造器的模板直接粘過(guò)來(lái)。手動(dòng)定義了程序就將這個(gè)構(gòu)造器一視同仁,都添加無(wú)參構(gòu)造器
???? //:ClassBase2(another) 手動(dòng)指定,這時(shí)父類(lèi)構(gòu)造器接收到子類(lèi)的參數(shù),先嚴(yán)格匹配,沒(méi)有找到ClassBase2(ClassDerive3)重載構(gòu)造器時(shí),再將參數(shù)隱式轉(zhuǎn)換為ClassBase2類(lèi)型
???? //:ClassBase2(another.base) 也可以調(diào)用父類(lèi)的有參構(gòu)造器,想用哪個(gè)都可以
???? {}
????
???? ClassDerive3& operator=(const ClassDerive3& another)
???? //注意這里不能用 :xxxxx? 因?yàn)檫@個(gè)函數(shù)不是構(gòu)造器,而是函數(shù)成員
???? {
???????? if (&another == this)
???????????? return *this;
???????? //默認(rèn)生成的operator= 會(huì)在這里調(diào)用父類(lèi)的賦值函數(shù),手動(dòng)定義則需要手動(dòng)寫(xiě),不寫(xiě)不調(diào)用。因?yàn)檫@不同于構(gòu)造器,創(chuàng)建對(duì)象時(shí)必須初始化,所以必須調(diào)用構(gòu)造器,但賦值不是創(chuàng)建對(duì)象,不必須調(diào)用父類(lèi)的賦值函數(shù)
???????? //錯(cuò)誤:operator=(another); 這樣寫(xiě)會(huì)調(diào)用當(dāng)前函數(shù),變成無(wú)限遞歸
???????? ClassBase2::operator=(another); //正確寫(xiě)法。在子類(lèi)中通過(guò)父類(lèi)的名稱(chēng)空間訪問(wèn)父類(lèi)的函數(shù)成員
???????? //ClassDerive3::operator=(another); //有效 當(dāng)前類(lèi)可以通過(guò)當(dāng)前類(lèi)的名稱(chēng)空間訪問(wèn)當(dāng)前類(lèi)的函數(shù)成員
???????? //this->ClassDerive3::operator=(another); //有效
???????? //base; //有效
???????? //ClassBase2::base; //有效
???????? //this->base; //有效
???????? //this->ClassDerive3::base; //有效
???????? //ClassDerive3::base; //有效
???????? //結(jié)論:在類(lèi)外不能通過(guò)類(lèi)的名稱(chēng)空間訪問(wèn)類(lèi)的函數(shù)成員,因?yàn)楹瘮?shù)成員屬于對(duì)象,沒(méi)有對(duì)象不能調(diào)用。但在類(lèi)內(nèi)可以通過(guò)名稱(chēng)空間訪問(wèn)函數(shù)成員。這個(gè)過(guò)程會(huì)默認(rèn)使用當(dāng)前對(duì)象 *this
???????? //當(dāng)前類(lèi)的operator=會(huì)遮擋父類(lèi)的operator=函數(shù),無(wú)論參數(shù)列表是否相同,只要重名就是擋住,不能直接訪問(wèn)。這稱(chēng)為shadow。解決方法為使用父類(lèi)名稱(chēng)空間注明函數(shù)歸屬
???????? return *this;
???? }
};
class ClassDerive4 :public ClassBase2
{
????public:
???? const int conval; //const類(lèi)型。c++中要求必須執(zhí)行初始化,這和對(duì)象相似。
???? ClassBase1 b1; //在類(lèi)中聲明一個(gè)對(duì)象數(shù)據(jù)成員。即在ClassDerive4對(duì)象中保存一個(gè)完整的ClassBase1對(duì)象區(qū)域。因?yàn)槁暶鲗?duì)象必須初始化,所以這也和const成員類(lèi)似
???? ClassDerive4()
???? : conval(0),b1(ClassBase1()), ClassBase2(2) //需要初始化的const成員、父類(lèi)、對(duì)象數(shù)據(jù)成員都需要寫(xiě)在 :這里進(jìn)行初始化。即便將父類(lèi)構(gòu)造器寫(xiě)在最后,也會(huì)最先調(diào)用,再對(duì)其他值進(jìn)行初始化。而成員初始化的順序是按照聲明的順序,所以即便先寫(xiě)b1()再寫(xiě)conval()也是先初始化conval
???? //成員的初始化相當(dāng)于 const int conval = 0;? ClassBase1 b1 = ClassBase1(); 所以b1是直接初始化而不是先構(gòu)造再拷貝構(gòu)造。
???? {
???? //必須初始化的內(nèi)容不能寫(xiě)在這里??梢圆怀跏蓟某蓡T可以寫(xiě)在這里,比如ClassBase1* ptr ,類(lèi)的指針和類(lèi)的對(duì)象不同,因?yàn)轭?lèi)的指針并沒(méi)有創(chuàng)建對(duì)象,所以不必須初始化
???? }
???? //創(chuàng)建ClassDerive4對(duì)象的過(guò)程:分配內(nèi)存->調(diào)用ClassBase2構(gòu)造器初始化父類(lèi)區(qū)域->調(diào)用ClassBase1構(gòu)造器初始化對(duì)象成員->執(zhí)行ClassDerive4構(gòu)造器的函數(shù)體
???? //釋放對(duì)象的過(guò)程永遠(yuǎn)和創(chuàng)建的過(guò)程相反(后進(jìn)先出):執(zhí)行~ClassDerive4析構(gòu)器->調(diào)用~ClassBase1析構(gòu)器->調(diào)用~ClassBase2析構(gòu)器->釋放內(nèi)存 ,析構(gòu)ClassDerive4對(duì)象并不會(huì)清空ClassBase1對(duì)象成員的內(nèi)容,因?yàn)槲鰳?gòu)器的目的是釋放堆內(nèi)存。所以三個(gè)區(qū)域各自析構(gòu)。最后安全釋放內(nèi)存,不需要在ClassDerive4的析構(gòu)器中手動(dòng)調(diào)用ClassBase1的析構(gòu)器,即不用在類(lèi)析構(gòu)器中調(diào)用成員對(duì)象的析構(gòu)器。
};
ostream& operator<<(ostream& out, const ClassBase2& c) //重載全局函數(shù),插入運(yùn)算符,實(shí)現(xiàn)cout<<ClassBase2對(duì)象
{
???? out << c.base;
???? return out; //接收和返回ostream&均為非const,因?yàn)榱鲗?duì)象在使用中可能更改流狀態(tài)
}
ostream& operator<<(ostream& out, const ClassDerive4& c)
{
???? //錯(cuò)誤:out << c; 相當(dāng)于operator<<(out,c)。根據(jù)嚴(yán)格匹配,會(huì)調(diào)用函數(shù)自身,無(wú)限遞歸
???? out << static_cast<const ClassBase2&>(c); //正確用法。需要將 const ClassDerive4& 轉(zhuǎn)換成 const ClassBase2& 來(lái)調(diào)用打印父類(lèi)的函數(shù)。類(lèi)型轉(zhuǎn)換應(yīng)該寫(xiě)完整。有的情況下將const ClassDerive4& c轉(zhuǎn)換成ClassBase2不會(huì)產(chǎn)生問(wèn)題(雖然程序內(nèi)部的執(zhí)行依然有區(qū)別),但只是僥幸運(yùn)行正常。類(lèi)型轉(zhuǎn)換應(yīng)寫(xiě)完整類(lèi)型
???? out << endl << c.conval;
???? return out;
}
class BaseUDM
{
???? char* data;
????public:
???? BaseUDM()
???? {
???????? data = new char[20];
???????? cout << "base constructor" << endl;
???? }
???? BaseUDM(const BaseUDM& another)
???? {
???????? data = new char[20];
???????? cout << "base copy constructor" << endl;
???? }
???? ~BaseUDM()
???? {
???????? delete[]data;
???????? cout << "base destructor" << endl;
???? }
???? BaseUDM& operator=(const BaseUDM& another)
???? {
???????? if (this == &another)return *this;
???????? cout << "base operator" << endl;
???????? //省略賦值過(guò)程
???? }
};
class MemberUDM
{
???? char* data;
????public:
???? MemberUDM()
???? {
???????? data = new char[20];
???????? cout << "member constructor" << endl;
???? }
???? MemberUDM(const MemberUDM& another)
???? {
???????? data = new char[20];
???????? cout << "member copy constructor" << endl;
???? }
???? ~MemberUDM()
???? {
???????? delete[]data;
???????? cout << "member destructor" << endl;
???? }
???? MemberUDM& operator=(const MemberUDM& another)
???? {
???????? if (this == &another)return *this;
?????? ? cout << "member operator" << endl;
???????? //省略賦值過(guò)程
???? }
};
class DeriveUDM:public BaseUDM
{
???? MemberUDM mem;
???? //在派生類(lèi)不使用動(dòng)態(tài)內(nèi)存的情況下,對(duì)于使用動(dòng)態(tài)內(nèi)存的基類(lèi)和使用動(dòng)態(tài)內(nèi)存的成員對(duì)象,默認(rèn)的構(gòu)造器、析構(gòu)器、拷貝構(gòu)造器、賦值運(yùn)算符均可以正確工作,即在派生類(lèi)函數(shù)中自動(dòng)調(diào)用成員、基類(lèi)的對(duì)應(yīng)的函數(shù)
};
class DeriveUDM2 :public BaseUDM
{
???? MemberUDM mem;
???? char* deriveData_;
???? //當(dāng)派生類(lèi)需要使用動(dòng)態(tài)內(nèi)存時(shí),不得不手動(dòng)定義這幾類(lèi)函數(shù)來(lái)完成深拷貝
????public:
???? DeriveUDM2() //如果要使用基類(lèi)默認(rèn)構(gòu)造器、成員默認(rèn)構(gòu)造器以外的構(gòu)造器需要顯式指定
???? {
???? ????deriveData_ = new char[20];
???? }
???? DeriveUDM2(const DeriveUDM2& another)
???? :BaseUDM(another), mem(another.mem) //默認(rèn)的派生類(lèi)拷貝構(gòu)造器會(huì)使用基類(lèi)的拷貝構(gòu)造器及成員的拷貝構(gòu)造器,手動(dòng)定義時(shí)則需要指定否則會(huì)調(diào)用默認(rèn)構(gòu)造器
???? {
???? ????deriveData_ = new char[20];
???? }
???? ~DeriveUDM2()
???? {
???? ????delete[]deriveData_; //析構(gòu)器依然會(huì)自動(dòng)調(diào)用基類(lèi)、成員的析構(gòu)器,所以只需要處理派生類(lèi)的動(dòng)態(tài)內(nèi)存
???? }
???? DeriveUDM2& operator=(const DeriveUDM2& another)
???? {
???????? if (this == &another)return *this;
???????? BaseUDM::operator=(another);//對(duì)于派生類(lèi)中的基類(lèi)部分(子對(duì)象)需要顯式調(diào)用BaseUDM::operator=避免遞歸
???????? mem = another.mem; //等價(jià)于mem.operator=(another.mem)
???????? //省略賦值過(guò)程
???? }
???? //除析構(gòu)器自動(dòng)完成以外,其他函數(shù)都要考慮使用基類(lèi)、成員的哪個(gè)函數(shù)
};