七、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
  • 一:背景 準備開個系列來聊一下 PerfView 這款工具,熟悉我的朋友都知道我喜歡用 WinDbg,這東西雖然很牛,但也不是萬能的,也有一些場景他解決不了或者很難解決,這時候藉助一些其他的工具來輔助,是一個很不錯的主意。 很多朋友喜歡在項目中以記錄日誌的方式來監控項目的流轉情況,其實 CoreCL ...
  • 本來閑來無事,準備看看Dapper擴展的源碼學習學習其中的編程思想,同時整理一下自己代碼的單元測試,為以後的進一步改進打下基礎。 突然就發現問題了,源碼也不看了,開始改代碼,改了好久。 測試Dapper.LiteSql數據批量插入的時候,耗時20秒,感覺不正常,於是我測試了非Dapper版的Lite ...
  • 需求如下,在DEV框架項目中,需要在表格中增加一列顯示圖片,並且能編輯該列圖片,然後進行保存等操作,最終效果如下 這裡使用的是PictureEdit控制項來實現,打開DEV GridControl設計器,在ColumnEdit選擇PictureEdit: 綁定圖片代碼如下: DataTable dtO ...
  • 前兩天微軟偷偷更新了Visual Studio 2022 正式版版本 17.3 發佈,發佈摘要: MAUI 工作負荷 GA 生成 MAUI/Blazor CSS 熱重載支持 現在,你將能夠使用我們的新增功能在 Visual Studio 中使用每個更新試用一系列新功能。 選擇每個功能以瞭解有關特定功 ...
  • 航天和軍工領域的數字化轉型和建設正在積極推進,在與航天二院、航天三院、航天六院、航天九院、無線電廠、兵工廠等單位交流的過程中,用戶更聚焦試驗和生產過程中的痛點,迫切需要解決軟體平臺統一監測和控制設備及軟體與設備協同的問題。 ...
  • .NET 項目預設情況下 日誌是使用的 ILogger 介面,預設提供一下四種日誌記錄程式: 控制台 調試 EventSource EventLog 這四種記錄程式都是預設包含在 .NET 運行時庫中。關於這四種記錄程式的詳細介紹可以直接查看微軟的官方文檔 https://docs.microsof ...
  • 一:背景 上一篇我們聊到瞭如何去找 熱點函數,這一篇我們來看下當你的程式出現了 非托管記憶體泄漏 時如何去尋找可疑的代碼源頭,其實思路很簡單,就是在 HeapAlloc 或者 VirtualAlloc 時做 Hook 攔截,記錄它的調用棧以及分配的記憶體量, PerfView 會將這個 分配量 做成一個 ...
  • 背景 在 CI/CD 流程當中,測試是 CI 中很重要的部分。跟開發人員關係最大的就是單元測試,單元測試編寫完成之後,我們可以使用 IDE 或者 dot cover 等工具獲得單元測試對於業務代碼的覆蓋率。不過我們需要一個獨立的 CLI 工具,這樣我們才能夠在 Jenkins 的 CI 流程集成。 ...
  • 一、應用場景 大家在使用Mybatis進行開發的時候,經常會遇到一種情況:按照月份month將數據放在不同的表裡面,查詢數據的時候需要跟不同的月份month去查詢不同的表。 但是我們都知道,Mybatis是ORM持久層框架,即:實體關係映射,實體Object與資料庫表之間是存在一一對應的映射關係。比 ...
  • 我國目前並未出台專門針對網路爬蟲技術的法律規範,但在司法實踐中,相關判決已屢見不鮮,K 哥特設了“K哥爬蟲普法”專欄,本欄目通過對真實案例的分析,旨在提高廣大爬蟲工程師的法律意識,知曉如何合法合規利用爬蟲技術,警鐘長鳴,做一個守法、護法、有原則的技術人員。 案情介紹 深圳市快鴿互聯網科技有限公司 2 ...