網站首頁 編程語言 正文
菱形繼承形成原因
多繼承,呈菱形狀
菱形繼承代碼:
class A
{
public:
A() {}
int _a ;
};
class B :public A
{
public:
//會存在父的int a;
B() {}
int _b ;
};
class C :public A
{
public:
//會存在父的int a;
C() {}
int _c ;
};
class D :public B,public C
{
public:
//會存在父的int a; 問題來了這個a是 父親b中的a 還是 父親c中的a -- 二義性;
//父的int b;
//父的int c;
D() {}
int _d ;
};
int main()
{
D d;
d.B::_a = 1;
d.C::_a = 2;
d._b = 3;
d._c = 4;
b._d = 5;
cout << d._a << endl;//報錯 提示b.a具有二義性
return 0;
}
出現二義性變量的內存布局
可以看到上圖中紫色框兩部分,是出現二義性的兩份A::_a變量,編譯器無法自主確定需要用哪一個,可以d::A_a 或者 d::B_a這樣使用;
應對方案
虛繼承 vitrual
vitrual修飾使派生類出現二義性的父類繼承部分(菱形的腰部)
class A
{
public:
A() {}
int _a ;
};
class B :virtual public A //virtual修飾
{
public:
//父的int a;
B() {}
int _b;
};
class C :virtual public A //virtual修飾
{
public:
//父的int a;
C() {}
int _c ;
};
class D :public B,public C
{
public:
//自己的 int a;(二義性的父親繼承經virtual修飾)
//父的int b;
//父的int c;
D() {}
int _d;
};
int main()
{
D d;
d.B::_a = 1;
d.C::_a = 2;
d._b = 3;
d._c = 4;
b._d = 5;
cout << d._a << endl;//沒問題,輸出2,這個2是D類成員通過繼承,使得d自己獨有且只有一份的 A::_a成員變量;
return 0;
}
解決二義性變量內存布局–虛基表
可以看到原先存放兩個二義性數據A::a和B::a的位置,變成了一個地址?
這個兩個地址就叫虛基表指針;
通過對虛基表的進一步內存研究,發現了虛基表緊著得下一個位置存放了一個偏移量,這個偏移量是存放該虛基表指針的內存位置,與當前派生類獨有一份的成員變量_a之間的偏移長度;
這樣我們直接使用d::a的時候,因為獨有一份,也不會出現數據二義性的問題了;
注意,這和多態中的虛表(虛函數表兩回事)
其次,虛基表的指針,經過測試,也是某個多繼承派生類的多對象共用的;
eg: D d1,d2; 其中d1,d2兩個對象 上圖的這兩張虛基表指針是一樣的,原因很簡單,D類類型都一樣,那么某個位置起,到另一個相對位置偏移量肯定是固定的!
(同類型的虛表指針也如此,復用節省空間嘛)
感悟
繼承,多態無疑為我們創造了很多的價值,但是像菱形繼承這種弊端也是存在的,本質是多繼承而引起的問題,在一些語言中禁止了多繼承的行為,總之有利有弊,雖然C++允許了多繼承,但還是盡量別寫多繼承這種模式,復雜度和出現問題的概率都很大;
關于代碼復用等的另一種關系-組合
繼承是每個派生類都能相對于繼承is-a的關系; 每個派生類對象都是一個基類對象; 耦合度高
// Car和BMW Car和Benz構成is-a的關系
class Car{
protected:
string _colour ="白色"; // 顏色
string _num ="陜ABITB";// 車牌號
};
class BMW:public Car{
public:
void Drive() {cout <<"好開-操控"<< end1;}
};
class Benz : public Car{
public:
void Drive() {cout <<"好坐-舒適"<< endl;}
};
優先考慮組合has-a的關系; eg: 汽車-輪胎,has-a,組合(class 輪胎 作為class 車的成員嵌套) 耦合度低
// Tire和Car構成has-a的關系
class Tiref{
protected:
string _brand = "Michelin";// 品牌
size_t size = 17;// 尺寸
};
class Car{
protected:
string _colour ="白色";// 顏色
string _num ="陜ABITO";// 車牌號
Tire _t;// 輪胎
};
原文鏈接:https://blog.csdn.net/wtl666_6/article/details/128751329
相關推薦
- 2022-05-27 iOS實現拼圖小游戲_IOS
- 2022-09-14 python單鏈路性能測試實踐_python
- 2024-04-02 docker開機自啟設置
- 2023-07-09 Hive常見時間日期函數的使用與問題整理
- 2022-12-22 Go語言編程通過dwarf獲取內聯函數_Golang
- 2022-12-07 C++小游戲教程之猜數游戲的實現_C 語言
- 2022-04-17 C語言?動態內存管理全面解析_C 語言
- 2022-10-10 pycharm創建并使用虛擬環境的詳細圖文教程_python
- 最近更新
-
- window11 系統安裝 yarn
- 超詳細win安裝深度學習環境2025年最新版(
- Linux 中運行的top命令 怎么退出?
- MySQL 中decimal 的用法? 存儲小
- get 、set 、toString 方法的使
- @Resource和 @Autowired注解
- Java基礎操作-- 運算符,流程控制 Flo
- 1. Int 和Integer 的區別,Jav
- spring @retryable不生效的一種
- Spring Security之認證信息的處理
- Spring Security之認證過濾器
- Spring Security概述快速入門
- Spring Security之配置體系
- 【SpringBoot】SpringCache
- Spring Security之基于方法配置權
- redisson分布式鎖中waittime的設
- maven:解決release錯誤:Artif
- restTemplate使用總結
- Spring Security之安全異常處理
- MybatisPlus優雅實現加密?
- Spring ioc容器與Bean的生命周期。
- 【探索SpringCloud】服務發現-Nac
- Spring Security之基于HttpR
- Redis 底層數據結構-簡單動態字符串(SD
- arthas操作spring被代理目標對象命令
- Spring中的單例模式應用詳解
- 聊聊消息隊列,發送消息的4種方式
- bootspring第三方資源配置管理
- GIT同步修改后的遠程分支