七、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
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...