C++ 繼承方式的調整
在任何繼承方式中,除了基類的private成員外,都可以在派生類中分別調整其訪問控制。
調整格式
[public: | protected: | private: ] :: ;
class A
{
public:
void f1();
void f2();
void f3();
protected:
void g1();
void g2();
void g3();
}
class B: private A
{
public:
A::f1;//把f1調整為public
A::g1;//把g1調整為public,是否允許弱化基類的訪問控制要視具體的實現而定
protected:
A::f2;//把f2調整為protected
A::g2;//把g2調整為protected
}
class C: class B
{
public:
void h()
{
f1(); f2(); g1(); g2();//OK
f3(); g3(); //Error,此時f3,g3是基類B的private成員
}
}
————————————————
C++繼承及其特點
繼承
繼承機制是面向對象程序設計,使代碼可以複用最重要手段,它允許在保持原有類的基礎上進行擴展,增加功能,這樣產生的類,叫做派生類,被派生類繼承的類叫做基類。繼承呈現了面向對象程序設計的層次結構。體現了由簡單到複雜的認知過程。
繼承方式
派生類名字: 繼承權限:父類的名字
例如:已存在類class A
那麼派生類定義為
class Derived: public A
繼承權限三種格式
繼承的權限跟類中成員的權限一樣,有三種權限
public. protected. pritave
默認派生類的繼承方式為私有繼承方式
如果基類為class,派生類默認繼承方式私有
如果基類為struct,派生類默認繼承方式為共有
派生類定義方式
//Base //基類 父類
//Derived //派生類 子類
class Base
{
public:
int _b;
};
class Derived : public Base//派生類: 繼承方式 基類
{
public:
int _d;
};
繼承類型
基類的成員的三種類型,繼承到派生類。保護的類型,繼承的子類也是保護類型,在類外不可訪問。類內可以。非本類不可訪問。私有成員在派生類中不可訪問。
public(公有方式)
子類Derived繼承Base父類的數據。
共有的繼承方式,可以訪問父類的共有成員與保護成員。但是無法訪問私有成員。
基類中public類的 在 子類中也是public
基類中protected類的成員 在 子類中也是 protected
基類中私有成員,仍然歸父類私有,雖然子類繼承,但是無法訪問。但是可以調用父類中共有的方法來訪問。
protected(保護方式)
保護的繼承方式
基類中:共有的權限 如果是保護的繼承方式, 到了子類中,已經從public類改變為protected類。
基類中保護的成員在子類中訪問的權限不變。
基類中的私有成員, 子類繼承下來了,但是就是用不了。
派生類裡可以訪問 保護類成員。 類外不行。私有類只能本類中訪問。
private(私有方式)
私有的繼承方式,基類中所有的成員變量/函數,在派生類中全部變為私有屬性。
繼承的特點
對象模型: 類中成員變量在內存中的佈局形式(非靜態成員變量)
class Base
{
public:
int _a;
int _b;
int _c;
};
class Derived : public Base
{
public:
int a;
int b;
int c;
};
以上的基類與派生類在對象存儲形式為:
派生類與基類的關係
共有的繼承方式,可以將基類對象,當做是派生類對象。
使用角度:所有用到基類的地方,都可以用派生類來代替。
保護的繼承方式,protected/private: 子類—基類 相當於has-a的關係
相當於在子類中創建了一個基類對象,基類擁有自己的作用域以及對成員的保護權限。
基類/派生類賦值:切片/切割
子類可以給父類賦值。反過來則不行。(這種方式叫切片/切割)
子類本身就是父類的延伸,因此子類中擁有父類擁有的東西。
派生類給基類賦值時,可以將自己切開,將上半部分的name賦值給基類對象。這種方式叫切片/切割
切片/切割一個天然的行為,並沒有伴隨著隱式的類型轉換,如下證實:
class Base
{
public:
int _a;
int _b;
int _c;
};
class Derived : public Base
{
public:
int a;
int b;
int c;
};
int main()
{
Base B;
Derived D;
B = D;
Base& b = D;
//基類引用對象引用派生類是沒有問題的。這個引用變量只能訪問到屬於基類的數據
//此時沒有發生類型轉換,引用的是派生類切出來的基類對象,不是類型轉換後的數據。
int a = 1;
double d = i;//這裡發生隱式類型轉換,但是不影響,因為這只是用常量給變量賦值
//double& d1 = i;//因為發生隱式類型轉換時,會產生一個臨時變量
//此時的double類型的引用變量,引用的是i類型轉換後的臨時變量的值
//臨時變量具有常屬性,因此必須將引用類型用const修飾
//與常量的屬性保持一致,才可以通過編譯。
const double& d1 = i;//此時可以通過編譯。
}
基類 = 派生類:
基類訪問派生類中基類需要的數據,基類指針可以指向派生類對象。這時的指針可以訪問基類的成員。基類的引用,可以引用派生類對象。這是也只是可以訪問基類的數據。
派生類 = 基類:
基類對象不能強轉為派生類對象,賦值給派生類。賦值時,派生類會訪了基類中沒有的數據, 會造成越界訪問。發生錯誤。
派生類指針或引用不可以指向或引用基類對象。可能會發生越界訪問,所以不允許指向或者引用基類。如果給基類進行強轉,將基類引用強轉為派生類,強行訪問派生類對象,訪問到不允許訪問的內容,直接出錯。除非這個基類對象的指針之前就是指向派生類的指針。
同名隱藏
基類中繼承下來的函數,與派生類中的同名函數不能形成重載,而且同名函數還會發生衝突。如果訪問相同名稱的函數,派生類中只能調用自己類中的函數或者成員。如果想在派生類中訪問基類中的同名(成員函數||成員變量)。只能通過加基類類作用域的方式去訪問相同名稱的基類成員。
d.Base::fun();
同名函數不能形成重載,因為函數重載的條件就是同名函數必須要在同一作用域,儘管基類與派生類函數名相同但參數信息不同, 他們沒有在同一個作用域,因此不能形成重載。誤當做重載函數調用時,肯定出錯。
其實編譯器在編譯期間會為基類和派生類成員加上對應的作用域限定符,派生類的成員前會加上派生類的作用域限定符,基類的成員會加上基類的作用域限定符。
Base::基類成員 Derived::派生類成員
派生類對象構造函數
派生類對象構造時,先調用派生類構造函數,在派生類構造函數的初始化列表中會調用基類的構造函數,在進入派生類對象的函數體之前,會先在初始化列表中完成派生類中成員的初始化,而這個初始化列表中就有基類的初始化,因此它會先初始化基類中成員(構造基類成員,調用基類的構造函數),之後再初始化派生類的成員。
默認的派生類構造函數只會調用基類的默認構造函數。所以基類的構造函數如果是定義的有參數的,這時必須在派生類的構造函數初始化列表中給出定義。否則構造派生類對象時,找不到默認的基類構造函數,代碼不能編譯。
派生類的析構函數
(先析構自己的,在最後一個括號處調用基類的析構函數)
如果要銷燬派生類的對象,則需要掉用派生類析構函數來清理派生類對象中的資源,在派生類析構函數的最後,需要調用基類的析構函數,以完成基類部分資源的銷燬。
final修飾的類
final修飾的類,不能被繼承
將類的構造函數定義為私有的, 則變為不可繼承的類。(C++11)中 被final修飾的類,不能被繼承。
繼承與友元函數
友元關係不能繼承,基類友元不能訪問子類私有和保護成員。因為友元函數不是類的成員,所以不能繼承。基類的友元函數因此無法訪問派生類的任何成員。
派生類的友元函數,不能訪問基類的私有成員,如果是私有的繼承方式,那麼基類的所有成員都不能訪問。就跟派生類的訪問權限一致,派生類可以訪問到的基類對象,友元函數也能訪問到。
靜態成員
靜態成員變量可以被繼承。並且派生類與基類公用這個靜態成員。
多繼承
多繼承方式:
class B1; class B2; //已有的類
class D: public B1,public B2;
B1;
B2;
D;
先繼承的基類在上,後繼承的基類在下, 派生類在最下。
菱形繼承模型:
虛擬繼承
虛擬繼承
class D: virtual public B//關鍵字 virtual
1
為了解決菱形繼承中的二義性問題,大佬們設計出了虛擬繼承。虛擬繼承的派生類中,不管派生類中有多少個公用的基類,都公用一個基類。因此在派生類中,前4個字節首先存了一個指針,這個指針指向一個類似數組的區域,裡面裝著要訪問基類對象時,該指針要偏移的偏移量。細節如下:
虛擬繼承存儲
虛擬繼承的派生類,存儲方式會先存放派生類對象,將基類對象放在後面存放。
代碼表現
#include <iostream>
using namespace std;
class A
{
public:
int a;
};
class B:virtual public A
{
public:
int b;
};
class C:virtual public A
{
public:
int c;
};
class D:public B, public C
{
public:
int d;
};
int main()
{
D d;
d.B::a = 2;
d.C::a = 1;
d.b = 3;
d.c = 4;
d.d = 5;
cout << sizeof(d) << endl;
}
內存中體現
如果有不對的地方,希望大佬批評。
————————————————
通過分享實用的計算機編程語言乾貨,推動中國編程到2025年基本實現普及化,使編程變得全民皆知,最終實現中國編程之崛起,這裡是中國編程2025,感謝大家的支持。
原文鏈接:https://blog.csdn.net/haitaolang/article/details/70844494
原文鏈接:https://blog.csdn.net/qq_41804778/article/details/81561547
閱讀更多 中國編程2025 的文章