訪問控制與繼承: 一個(gè)類使用PRotected關(guān)鍵字來聲明那些希望與派生類分享但是不想被其他公共訪問的成員。其性質(zhì)如下: (1)和私有成員類似,受保護(hù)的成員對類的用戶是不可訪問的 (2)和公有成員類似,受保護(hù)的成員對于派生類的成員和友元來時(shí)是可訪問的。 (3)派生類的成員或友元只能通過派生類對象來訪問 基類的受保護(hù)成員。派生類對于一個(gè)基類對象中的受保護(hù)成員沒有任何訪問特權(quán)。

某個(gè)類對其繼承而來的成員的訪問權(quán)限受兩個(gè)因素的影響: (1)基類中該成員的訪問說明符 (2)派生類的派生列表中的訪問說明符
對基類成員的訪問權(quán)限只與基類中的訪問說明符有關(guān)。派生訪問說明符的目的是控制派生類用戶(包括派生類的派生類在內(nèi))對于基類成員的訪問權(quán)限。

派生訪問說明符還可以繼承自派生類的新類的訪問權(quán)限。

派生類向基類轉(zhuǎn)換的可訪問性: 對于代碼中的某個(gè)給定節(jié)點(diǎn)來說,如果基類的共有成員是可訪問的,則派生類向基類的類型轉(zhuǎn)換也是可訪問的。反之,則不行。

友元關(guān)系不具有繼承性,每個(gè)類負(fù)責(zé)控制各自成員的訪問權(quán)限。

當(dāng)我們需要改變派生類繼承的某個(gè)名字的訪問級別,可以通過使用using聲明。
class Base{public: std::size_t size() const{return n;}protected: std::size_t n;};class Derived:private Base //Base的成員在Derived是私有的。{public: using Base::size;protected: using Base::n;//改變訪問權(quán)限};默認(rèn)的繼承保護(hù)級別:

繼承中的類作用域:
當(dāng)存在繼承關(guān)系時(shí),派生類的作用域嵌套在其基類的作用域之內(nèi)。如果一個(gè)名字在派生類的作用域內(nèi)無法正確解析,則編譯器將繼續(xù)在外層的基類作用域?qū)ふ以撁值亩x。
派生類的成員將隱藏同名的基類成員。

我們可以通過作用域運(yùn)算符來使用隱藏的成員。除了覆蓋繼承而來的虛函數(shù)之外,派生類最好不要重用其他定義在基類的名字。

如果派生類(內(nèi)層作用域)的成員與基類(外層作用域)的某個(gè)成員同名,則派生類將在其作用域隱藏該基類成員,即使派生類成員和基類成員的形參列表不一致。

通過基類調(diào)用隱藏的虛函數(shù),調(diào)用的時(shí)候主要是看是什么類的指針,是否有虛函數(shù)覆蓋。
#include <iostream>class Base{public: Base(int v):v(v){} virtual int fcn() { return v; } int v=0;};class D1:public Base{public: D1(int d1):Base(d1){} int fcn(int d1) { return d1+1; } virtual void f2(){std::cout<<"f2() in D1"<<std::endl;}};class D2:public D1{public: D2(int dd):D1(dd){} int fcn(int d2) { return d2; } int fcn() { return v+100; } void f2(){std::cout<<"f2() in D2"<<std::endl;}};int main(){ Base b(10); D1 d1(10); D2 d2(10); Base *bp1=&b,*bp2=&d1,*bp3=&d2; std::cout<<bp1->fcn()<<std::endl;//10,虛調(diào)用,Base::fcn() std::cout<<bp2->fcn()<<std::endl;//10,虛調(diào)用,Base::fcn() std::cout<<bp3->fcn()<<std::endl;//110,調(diào)用D2::fcn() D1 *d1p=&d1; D2 *d2p=&d2;// bp2->f2();//錯(cuò)誤,base沒有f2的成員 d1p->f2();//D1::f2() d2p->f2();//D2::f2() Base *p1=&d2; D1 *p2=&d2; D2 *p3=&d2; //p1->fcn(42);//錯(cuò)誤,Base沒有fcn(int) std::cout<< p2->fcn(42)<<std::endl;//43,D1::fcn(42) std::cout<< p3->fcn(42)<<std::endl;//42,D2::fcn(42) return 0; }虛折構(gòu)函數(shù): 繼承關(guān)系對基類拷貝控制最直接的影響是基類通常應(yīng)該定義一個(gè)虛折構(gòu)函數(shù),這樣我們就能動態(tài)分配繼承體系中的對象了。如果基類的折構(gòu)函數(shù)不是虛函數(shù),則delete一個(gè)指向派生類對象的基類指針將產(chǎn)生未定義的行為。
Quote *itemp=new Quote;delete itemp;//調(diào)用Quote的折構(gòu)函數(shù)itemp=new Bulk_quote;delete itemp;//調(diào)用Bulk_quote的折構(gòu)函數(shù)基類或派生類的合成拷貝控制成員的行為與其他合成的構(gòu)造函數(shù)、賦值運(yùn)算符或折構(gòu)函數(shù)類似,即它們對類本身的成員依次進(jìn)行初始化,賦值或銷毀操作。此外,這些合成的成員還負(fù)責(zé)使用直接基類中對應(yīng)的操作對一個(gè)對象的直接基類進(jìn)行初始化、賦值或銷毀操作(從上到下)。
對于派生類的折構(gòu)函數(shù)來說,它除了銷毀派生類自己的成員外,還負(fù)責(zé)銷毀派生類的直接基類,該直接基類又銷毀它自己的直接基類,以此類推直至繼承鏈的頂端。(自下而上)。
某些定義基類的方式可能導(dǎo)致有的派生類成員成為被刪除的函數(shù):

因?yàn)榛惾鄙僖苿硬僮鲿柚古缮悡碛凶约旱暮铣梢苿硬僮鳎援?dāng)我們卻是需要執(zhí)行移動操作時(shí)應(yīng)該首先在基類進(jìn)行定義。但必須顯式的定義這些成員。

當(dāng)派生類定義拷貝或移動操作時(shí),該操作負(fù)責(zé)拷貝或移動包括基類部分成員在內(nèi)的整個(gè)對象。 在默認(rèn)情況下,基類默認(rèn)構(gòu)造函數(shù)初始化派生類對象的基類部分。如果我們想拷貝或移動基類部分,則必須在派生類的構(gòu)造函數(shù)初始值列表中顯式地使用基類的拷貝或移動構(gòu)造函數(shù)。
class B {/*......*/};class D:public Base{public: //Base(d)會匹配Base的拷貝構(gòu)造函數(shù)。D類型的對象d被綁定到該構(gòu)造函數(shù)的Base&形參上。Base的拷貝構(gòu)造函數(shù)負(fù)責(zé)將d的基類部分拷貝給要創(chuàng)建的對象。 D (const D& d):Base(d)//拷貝基類成員 /* D的成員的初始值 */{......} D (D&& d):Base(std::move(d))//移動基類成員 /* D的成員的初始值 */{......}};派生類的賦值運(yùn)算符也必須顯式地為其基類部分賦值。
D D&::Operator=(const D &rhs){ Base::operator=(rhs);//為基類部分賦值 ..... return *this}派生類折構(gòu)函數(shù)只負(fù)責(zé)銷毀由派生類自己分配的資源。
class D:public Base{public: //Base::~Base自動執(zhí)行 ~D(){//用戶定義};};我們可以使用基類名的using聲明語句來說明派生類繼承基類構(gòu)造函數(shù)。但是一個(gè)構(gòu)造函數(shù)的using聲明不會改變該構(gòu)造函數(shù)的訪問級別。而且,一個(gè)using聲明的語句不能指定explicit或constexpr.
class B:public A{public: using A::A;//繼承基類A的構(gòu)造函數(shù)};當(dāng)一個(gè)基類構(gòu)造函數(shù)含有默認(rèn)實(shí)參,這些實(shí)參并不會被繼承。相反,派生類將獲得多個(gè)繼承的構(gòu)造函數(shù),其中每個(gè)構(gòu)造函數(shù)分別省略掉一個(gè)含有默認(rèn)實(shí)參的形參。
新聞熱點(diǎn)
疑難解答