七、C++繼承與多態-多重繼承的那些坑該怎麼填

来源:https://www.cnblogs.com/woden3702/archive/2022/05/18/16284553.html
-Advertisement-
Play Games

一個工作了5年的粉絲私信我。 他說自己準備了半年時間,想如螞蟻金服,結果第一面就掛了,非常難過。 問題是: “Redis存線上程安全問題嗎?” 關於這個問題,看看普通人和高手的回答。 普通人: 嗯。。。。。。。。。。。。 高手: 好的,關於這個問題,我從兩個方面來回答。 第一個,從Redis 服務端 ...


理解虛基類和虛繼承

多重繼承:代碼復用,一個派生類有多個基類。如:class C: public A,public B{};

虛基類:virtual可以修飾繼承方式,是虛繼承,被虛繼承的類,稱作虛基類。class A:virtual public B{};

虛繼承的類中會多一個vbptr指向vbtable,Vbtable中保存的是虛基類中數據在派生類中的記憶體偏移量,從虛基類中繼承的成員變數會被放在派生類記憶體的最下端。

虛函數和虛基類在調用的時候是沒有問題的,

但是在delete的時候會發生堆報錯

原因是:基類指針類型的成員p指向派生類對象,永遠指向的是派生類基類部分數據的起始地址,這裡的基類A的起始位置就是vfptr。但是這裡的派生類B是虛繼承的A,虛繼承的部分會放到派生類記憶體的後面,p指向的就是派生類後面的記憶體,這種情況Delete p就不會刪除派生類中的記憶體,造成了上圖中的問題。

class A{
public:
    virtual void func(){cout<<"call A:func"<<endl;}
    void operator delete (void* ptr){
        cout<<"operator delete:"<<ptr<<endl;
        free(ptr);
    }
private:
    int ma;
};
class B:virtual public A{
public:
    void func(){cout<<"call B:func"<<endl;}
    void* operator new (size_t size){
        void *p= malloc(size);
        cout<<"operator new:"<<p<<endl;
        return p;
    }
private:
    int mb;
};
int main(){
    A *p=new B();
    cout<<"main:p"<<p<<endl;
    p->func();
    delete p;
}

delete的記憶體地址與new的記憶體地址不同,所以會造成問題。

菱形繼承問題

繼承的樣子像菱形,叫菱形繼承。類D中會繼承兩個類A中的成員。儘量避開多重繼承。

多重繼承的好處:可以做更多代碼的復用。

用虛繼承解決上面的問題。

class A{
public:
    A(int data):ma(data){
        cout<<"A()"<<endl;
    }
    ~A(){
        cout<<"~A()"<<endl;
    }
protected:
    int ma;
};

class B:public A{
public:
    B(int data):A(data),mb(data){
        cout<<"B()"<<endl;
    }
    ~B(){
        cout<<"~B()"<<endl;
    }
protected:
    int mb;
};

class C:public A{
public:
    C(int data):A(data),mc(data){
        cout<<"C()"<<endl;
    }
    ~C(){
        cout<<"~C()"<<endl;
    }
protected:
    int mc;
};

class D:public B,public C{
public:
    D(int data):B(data),C(data),md(data){
        cout<<"D()"<<endl;
    }
    ~D(){
        cout<<"~D()"<<endl;
    }
protected:
    int md;
};

int main(){
    D d(10);
    return 0;
}
/*
輸出
A()
B()
A()
C()
D()
~D()
~C()
~A()
~B()
~A()*/

如果虛繼承就會在B和C中構造出vbptr指針,在D中指向類A中的成員變數。

使用虛繼承避免繼承多次的問題:

class B:virtual public A{//使用虛繼承避免菱形繼承的問題
public:
    B(int data):A(data),mb(data){
        cout<<"B()"<<endl;
    }
    ~B(){
        cout<<"~B()"<<endl;
    }
protected:
    int mb;
};

class C:virtual public A{
public:
    C(int data):A(data),mc(data){
        cout<<"C()"<<endl;
    }
    ~C(){
        cout<<"~C()"<<endl;
    }
protected:
    int mc;
};

class D:public B,public C{
public:
    D(int data):A(data),B(data),C(data),md(data){
        cout<<"D()"<<endl;
    }
    ~D(){
        cout<<"~D()"<<endl;
    }
protected:
    int md;
};

/*
輸出結果:
A()
B()
C()
D()
~D()
~C()
~B()
~A()*/

C++的四種類型轉換

const_cast:去掉(指針或者引用)常量屬性的一個類型轉換

static_cast:提供編譯器認為安全的類型轉換 基類和派生類可以通過static_cast進行轉換

reinterpret_cast:類似於c風格的強制類型轉換(不安全)

dynamic_cast:主要用在繼承結構中,可以支持RTTI類型識別的上下轉換

解釋一下dynamic_cast的用法:

class Base{
public:
    virtual void func()=0;
};

class Derive1:public Base{
public:
    void func() override {
        cout<<"Derive1::func()"<<endl;
    }
};

class Derive2:public Base{
public:
    void func() override {
        cout<<"Derive2::func()"<<endl;
    }
    //如果想要在這個類里添加一個新業務
    void funcDerive2(){
        cout<<"Derive2::funcDerive2()"<<endl;
    }
};

/**
 * 外部調用上面兩個類的介面
 * @param p
 */
void show(Base* p){
    //dynamic_cast會檢查p指針是否指向的是一個Derive2類型的對象
    //如果是,dynamic_cast轉換類型成功,返回Derive2對象的地址給dp2;否則返回nullptr
    Derive2 *dp2=dynamic_cast<Derive2*> (p);
    if(dp2!= nullptr){
        dp2->funcDerive2();
    }else
        p->func();
}

int main(){
    Derive1 d1;
    Derive2 d2;
    show(&d1);
    show(&d2);

    return 0;
}

/*
輸出結果:
Derive1::func()
Derive2::funcDerive2()*/


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 下麵介紹的是JUC包下一些線程安全類的一些簡單使用和一些小demo。 Semaphore 信號量,即可以同時使用的線程數,tryrequire就是將信號量減一,release就是信號量+1,當等於0就會阻塞,大於零才會喚醒。 當需要控制線程訪問數量,可以使用信號量來做控制,比較簡單。 下麵是使用信號 ...
  • Spring Ioc源碼分析系列--Ioc容器註冊BeanPostProcessor後置處理器以及事件消息處理 前言 上一篇分析了BeanFactoryPostProcessor的作用,那麼這一篇繼續在refresh()方法里游蕩,相信對Spring熟悉點的朋友,在看完BeanFactoryPost ...
  • 大家好,這篇文章分享了C程式設計(譚浩強)第五版第十章課後題答案,所有程式已經測試能夠正常運行,如果小伙伴發現有錯誤的的地方,歡迎留言告訴我,我會及時改正!感謝大家的觀看!!! ...
  • 高精度 運算:加法、減法、階乘、乘法 翻轉: 這些運算都是從小位開始,所以一般需要翻轉。以字元串儲存:reverse(a.begin(),a,end())。以數組儲存: for (int i1 = lena1 - 1; i1 >= 0; i1--) { a1[lena1-1-i1] = a[i1] ...
  • 大家好,這篇文章分享了C程式設計(譚浩強)第五版第九章課後題答案,所有程式已經測試能夠正常運行,如果小伙伴發現有錯誤的的地方,歡迎留言告訴我,我會及時改正!感謝大家的觀看!!! ...
  • 回溯法(98條消息) (新手向)遞歸與回溯演算法學習(一)——n位逐位整除數_TripleGold.的博客-CSDN博客 演算法思想: (通用的解題法)窮舉的搜索嘗試過程,主要是在搜索嘗試過程中尋找問題的解,當發現不滿足求解條件時就回退,嘗試其他路徑 回溯法的解題步驟: 針對給定問題確定問題的解空間樹, ...
  • 大家好,這篇文章分享了C程式設計(譚浩強)第五版第八章課後題答案,所有程式已經測試能夠正常運行,如果小伙伴發現有錯誤的的地方,歡迎留言告訴我,我會及時改正!感謝大家的觀看!!! ...
  • 介面轉發調用 問題描述 在開發BI系統的時候,出現了這樣一個不方便的地方。 BI報表所展示的數據是從WMS系統通過API獲取的,而BI系統也有自己的後臺實現了用戶許可權之類的。 那麼現在如果WMS開發了一個新的介面,我就要在BI系統手動寫一個介面,實在很不方便。解決的方法也很簡單,BI系統開發一個AP ...
一周排行
    -Advertisement-
    Play Games
  • JWT(JSON Web Token)是一種用於在網路應用之間傳遞信息的開放標準(RFC 7519)。它使用 JSON 對象在安全可靠的方式下傳遞信息,通常用於身份驗證和信息交換。 在Web API中,JWT通常用於對用戶進行身份驗證和授權。當用戶登錄成功後,伺服器會生成一個Token並返回給客戶端 ...
  • 老周在幾個世紀前曾寫過樹莓派相關的 iOT 水文,之所以沒寫 Nano Framework 相關的內容,是因為那時候這貨還不成熟,可玩性不高。不過,這貨現在已經相對完善,老周都把它用在項目上了——第一個是自製的智能插座,這個某寶上50多塊可以買到,搜“esp32 插座”就能找到。一種是 86 型盒子 ...
  • 引言 上一篇我們創建了一個Sample.Api項目和Sample.Repository,並且帶大家熟悉了一下Moq的概念,這一章我們來實戰一下在xUnit項目使用依賴註入。 Xunit.DependencyInjection Xunit.DependencyInjection 是一個用於 xUnit ...
  • 在 Avalonia 中,樣式是定義控制項外觀的一種方式,而控制項主題則是一組樣式和資源,用於定義應用程式的整體外觀和感覺。本文將深入探討這些概念,並提供示例代碼以幫助您更好地理解它們。 樣式是什麼? 樣式是一組屬性,用於定義控制項的外觀。它們可以包括背景色、邊框、字體樣式等。在 Avalonia 中,樣 ...
  • 在處理大型Excel工作簿時,有時候我們需要在工作表中凍結窗格,這樣可以在滾動查看數據的同時保持某些行或列固定不動。凍結窗格可以幫助我們更容易地導航和理解複雜的數據集。相反,當你不需要凍結窗格時,你可能需要解凍它們以獲得完整的視野。 下麵將介紹如何使用免費.NET庫通過C#實現凍結Excel視窗以鎖 ...
  • .NET 部署 IIS 的簡單步驟一: 下載 dotnet-hosting-x.y.z-win.exe ,下載地址:.NET Downloads (Linux, macOS, and Windows) (microsoft.com) .NET 部署 IIS 的簡單步驟二: 選擇對應的版本,點擊進入詳 ...
  • 拓展閱讀 資料庫設計工具-08-概覽 資料庫設計工具-08-powerdesigner 資料庫設計工具-09-mysql workbench 資料庫設計工具-10-dbdesign 資料庫設計工具-11-dbeaver 資料庫設計工具-12-pgmodeler 資料庫設計工具-13-erdplus ...
  • 初識STL STL,(Standard Template Library),即"標準模板庫",由惠普實驗室開發,STL中提供了非常多對信息學奧賽很有用的東西。 vector vetor是STL中的一個容器,可以看作一個不定長的數組,其基本形式為: vector<數據類型> 名字; 如: vector ...
  • 前言 最近自己做了個 Falsk 小項目,在部署上伺服器的時候,發現雖然不乏相關教程,但大多都是將自己項目代碼複製出來,不講核心邏輯,不太簡潔,於是將自己部署的經驗寫成內容分享出來。 uWSGI 簡介 uWSGI: 一種實現了多種協議(包括 uwsgi、http)並能提供伺服器搭建功能的 Pytho ...
  • 1 文本Embedding 將整個文本轉化為實數向量的技術。 Embedding優點是可將離散的詞語或句子轉化為連續的向量,就可用數學方法來處理詞語或句子,捕捉到文本的語義信息,文本和文本的關係信息。 ◉ 優質的Embedding通常會讓語義相似的文本在空間中彼此接近 ◉ 優質的Embedding相 ...