91超碰碰碰碰久久久久久综合_超碰av人澡人澡人澡人澡人掠_国产黄大片在线观看画质优化_txt小说免费全本

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

C++私有繼承與EBO實例分析

發布時間:2022-08-15 16:12:25 來源:億速云 閱讀:138 作者:iii 欄目:開發技術

這篇文章主要介紹“C++私有繼承與EBO實例分析”,在日常操作中,相信很多人在C++私有繼承與EBO實例分析問題上存在疑惑,小編查閱了各式資料,整理出簡單好用的操作方法,希望對大家解答”C++私有繼承與EBO實例分析”的疑惑有所幫助!接下來,請跟著小編一起來學習吧!

私有繼承本質不是繼承

在此強調,這個標題中,第一個“繼承”指的是一種C++語法,也就是class A : B {};這種寫法。而第二個“繼承”指的是OOP(面向對象編程)的理論,也就是A is a B的抽象關系,類似于“狗”繼承自“動物”的這種關系。

所以我們說,私有繼承本質是表示組合的,而不是繼承關系,要驗證這個說法,只需要做一個小實驗即可。我們知道最能體現繼承關系的應該就是多態了,如果父類指針能夠指向子類對象,那么即可實現多態效應。

請看下面的例程:

class Base {};
class A : public Base {};
class B : private Base {};
class C : protected Base {};
void Demo() {
  A a;
  B b;
  C c;
  Base *p = &a; // OK
  p = &b; // ERR
  p = &c; // ERR
}

這里我們給Base類分別編寫了A、B、C三個子類,分別是public、private個protected繼承。然后用Base *類型的指針去分別指向a、b、c。發現只有public繼承的a對象可以用p直接指向,而b和c都會報這樣的錯:

Cannot cast 'B' to its private base class 'Base'
Cannot cast 'C' to its protected base class 'Base'

也就是說,私有繼承是不支持多態的,那么也就印證了,他并不是OOP理論中的“繼承關系”,但是,由于私有繼承會繼承成員變量,也就是可以通過b和c去使用a的成員,那么其實這是一種組合關系。或者,大家可以理解為,把b.a.member改寫成了b.A::member而已。

那么私有繼承既然是用來表示組合關系的,那我們為什么不直接用成員對象呢?為什么要使用私有繼承?這是因為用成員對象在某種情況下是有缺陷的。

空類大小

在解釋私有繼承的意義之前,我們先來看一個問題,請看下面例程

class T {};
// sizeof(T) = ?

T是一個空類,里面什么都沒有,那么這時T的大小是多少?有的同學可能不假思索就會回答0。照理說,空類的大小就是應該是0,但如果真的設置為0的話,會有很嚴重的副作用,請看例程:

class T {};
void Demo() {
  T arr[10];
  sizeof(arr); // 0
  T *p = arr + 5;
  // 此時p==arr
  p++; // ++其實無效
}

發現了嗎?假如T的大小是0,那么T指針的偏移量就永遠是0,T類型的數組大小也將是0,而如果它成為了一個成員的話,問題會更嚴重:

struct Test {
  T t;
  int a;
};
// t和a首地址相同

由于T是0大小,那么此時Test結構體中,t和a就會在同一首地址。

所以,為了避免這種0長的問題,編譯器會針對于空類自動補一個字節的大小,也就是說其實sizeof(T)是1,而不是0。

這里需要注意的是,不僅是絕對的空類會有這樣的問題,只要是不含有非靜態成員變量的類都有同樣的問題,例如下面例程中的幾個類都可以認為是空類:

class A {};
class B {
  static int m1;
  static int f();
};
class C {
public:
  C();
  ~C();
  void f1();
  double f2(int arg) const;
};

有了自動補1字節,T的長度變成了1,那么T*的偏移量也會變成1,就不會出現0長的問題。但是,這么做就會引入另一個問題,請看例程:

class Empty {};
class Test {
  Empty m1;
  long m2;
};
// sizeof(Test)==16

由于Empty是空類,編譯器補了1字節,所以此時m1是1字節,而m2是8字節,m1之后要進行字節對齊,因此Test變成了16字節。如果Test中出現了很多空類成員,這種問題就會被繼續放大。

這就是用成員對象來表示組合關系時,可能會出現的問題,而私有繼承就是為了解決這個問題的。

空基類成員壓縮

(EBO,Empty Base Class Optimization)

在上一節最后的歷程中,為了讓m1不再占用空間,但又能讓Test中繼承Empty類的其他內容(例如函數、類型重定義等),我們考慮將其改為繼承來實現,EBO就是說,當父類為空類的時候,子類中不會再去分配父類的空間,也就是說這種情況下編譯器不會再去補那1字節了,節省了空間。

但如果使用public繼承會怎么樣?

class Empty {};
class Test : public Empty {
  long m2;
};
// 假如這里有一個函數讓傳Empty類對象
void f(const Empty &obj) {}
// 那么下面的調用將會合法
void Demo() {
  Test t;
  f(t); // OK
}

Test由于是Empty的子類,所以會觸發多態性,t會當做Empty類型傳入f中。這顯然問題很大呀!如果用這個例子看不出問題的話,我們換一個例子:

class Alloc {
public:
  void *Create();
  void Destroy();
};
class Vector : public Alloc {
};
// 這個函數用來創建buffer
void CreateBuffer(const Alloc &alloc) {
  void *buffer = alloc.Create(); // 調用分配器的Create方法創建空間
}
void Demo() {
  Vector ve; // 這是一個容器
  CreateBuffer(ve); // 語法上是可以通過的,但是顯然不合理
}

內存分配器往往就是個空類,因為它只提供一些方法,不提供具體成員。Vector是一個容器,如果這里用public繼承,那么容器將成為分配器的一種,然后調用CreateBuffer的時候可以傳一個容器進去,這顯然很不合理呀!

那么此時,用私有繼承就可以完美解決這個問題了

class Alloc {
public:
  void *Create();
  void Destroy();
};
class Vector : private Alloc {
private:
  void *buffer;
  size_t size;
  // ...
};
// 這個函數用來創建buffer
void CreateBuffer(const Alloc &alloc) {
  void *buffer = alloc.Create(); // 調用分配器的Create方法創建空間
}
void Demo() {
  Vector ve; // 這是一個容器
  CreateBuffer(ve); // ERR,會報錯,私有繼承關系不可觸發多態
}

此時,由于私有繼承不可觸發多態,那么Vector就并不是Alloc的一種,也就是說,從OOP理論上來說,他們并不是繼承關系。而由于有了私有繼承,在Vector中可以調用Alloc里的方法以及類型重命名,所以這其實是一種組合關系。

而又因為EBO,所以也不用擔心Alloc占用Vector的成員空間的問題。

到此,關于“C++私有繼承與EBO實例分析”的學習就結束了,希望能夠解決大家的疑惑。理論與實踐的搭配能更好的幫助大家學習,快去試試吧!若想繼續學習更多相關知識,請繼續關注億速云網站,小編會繼續努力為大家帶來更多實用的文章!

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

c++
AI

灵璧县| 文化| 汉阴县| 仪陇县| 渝中区| 锦州市| 宁武县| 洛川县| 新丰县| 浮山县| 米林县| 云霄县| 思茅市| 海安县| 佛山市| 宁陕县| 本溪| 巴彦县| 县级市| 繁峙县| 漳平市| 郑州市| 渝北区| 东莞市| 社旗县| 会同县| 淅川县| 东至县| 北辰区| 奇台县| 定结县| 遂溪县| 宝清县| 贵南县| 余江县| 漠河县| 陆良县| 潍坊市| 且末县| 南乐县| 宝兴县|