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

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

派生訪問說明符還可以繼承自派生類的新類的訪問權限。

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

友元關系不具有繼承性,每個類負責控制各自成員的訪問權限。

當我們需要改變派生類繼承的某個名字的訪問級別,可以通過使用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;//改變訪問權限};默認的繼承保護級別:

繼承中的類作用域:
當存在繼承關系時,派生類的作用域嵌套在其基類的作用域之內。如果一個名字在派生類的作用域內無法正確解析,則編譯器將繼續在外層的基類作用域尋找該名字的定義。
派生類的成員將隱藏同名的基類成員。

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

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

通過基類調用隱藏的虛函數,調用的時候主要是看是什么類的指針,是否有虛函數覆蓋。
#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,虛調用,Base::fcn() std::cout<<bp2->fcn()<<std::endl;//10,虛調用,Base::fcn() std::cout<<bp3->fcn()<<std::endl;//110,調用D2::fcn() D1 *d1p=&d1; D2 *d2p=&d2;// bp2->f2();//錯誤,base沒有f2的成員 d1p->f2();//D1::f2() d2p->f2();//D2::f2() Base *p1=&d2; D1 *p2=&d2; D2 *p3=&d2; //p1->fcn(42);//錯誤,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; }虛折構函數: 繼承關系對基類拷貝控制最直接的影響是基類通常應該定義一個虛折構函數,這樣我們就能動態分配繼承體系中的對象了。如果基類的折構函數不是虛函數,則delete一個指向派生類對象的基類指針將產生未定義的行為。
Quote *itemp=new Quote;delete itemp;//調用Quote的折構函數itemp=new Bulk_quote;delete itemp;//調用Bulk_quote的折構函數基類或派生類的合成拷貝控制成員的行為與其他合成的構造函數、賦值運算符或折構函數類似,即它們對類本身的成員依次進行初始化,賦值或銷毀操作。此外,這些合成的成員還負責使用直接基類中對應的操作對一個對象的直接基類進行初始化、賦值或銷毀操作(從上到下)。
對于派生類的折構函數來說,它除了銷毀派生類自己的成員外,還負責銷毀派生類的直接基類,該直接基類又銷毀它自己的直接基類,以此類推直至繼承鏈的頂端。(自下而上)。
某些定義基類的方式可能導致有的派生類成員成為被刪除的函數:

因為基類缺少移動操作會阻止派生類擁有自己的合成移動操作,所以當我們卻是需要執行移動操作時應該首先在基類進行定義。但必須顯式的定義這些成員。

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