C++繼承和動態內存分配

文章轉自微信 公眾號:Coder梁(ID:Coder_LT)

1.簡介

這裡面有一個問題,當我們的基類使用動態內存分配,並且重新定義賦值和復制構造函數,這會對派生類的實現有什麼影響呢?

我們來看兩種情況:

2.派生類不用new

假設基類中使用瞭動態內存分配:

class baseDMA {
 private:
     char *label;
     int rating;
    public:
     baseDMA(const char* l="null", int r=0);
     baseDMA(const baseDMA& rs);
     virtual ~baseDMA();
     baseDMA &operator=(const baseDMA& rs);
};

在這個聲明裡包含瞭構造函數、析構函數、復制構造函數和重載賦值運算符。

現在假設我們從baseDMA派生出瞭類lackDMA,但是後者不使用new

class lackDMA: public baseMDA {
   private:
     char color[40];
    public:
     ...
};

問題來瞭,我們要不要給lackDMA這個類定義析構函數、復制構造函數和賦值運算符呢?

答案是不需要。

首先是析構函數,這個很好想明白,如果我們沒有定義析構函數,那麼編譯器會自動定義一個不執行任何操作的默認析構函數。實際上派生類的析構函數往往會在執行一些邏輯之後調用基類的構造函數,因為lackDMA類中的成員不是通過new創建的,因此不需要額外的操作,所以默認析構函數是合適的。

同樣的默認復制構造函數也會執行非new創建成員的復制,所以對於color變量來說是沒問題的。並且在派生類當中,默認復制構造函數除瞭會復制非new創建的成員之外,還會調用基類的復制構造函數來復制父類成員的部分。所以,對於派生類lackDMA來說,我們使用默認的復制構造函數一樣沒有問題。

賦值也是一樣的,默認的賦值運算符也會自動使用基類的賦值運算符來對基類的成員進行賦值。

3.派生類使用new

我們再來看看派生類當中使用瞭new的情況。

class hasDMA: public baseMDA {
   private:
     char *style;
    public:
     ...
};

hasDMA這個類當中,我們添加瞭一個需要使用new創建的char*成員。在這種情況下,我們就沒辦法使用默認的函數瞭,就必須定義顯式析構函數、復制構造函數和賦值運算符瞭,我們一個一個來看。

首先是析構函數,派生類的析構函數會自動調用基類的析構函數,所以我們隻需要在析構函數當中釋放派生類中獨有的成員變量即可。

hasDMA::~hasDMA() {
    delete []style;
}

然後我們再來看看拷貝構造函數,由於派生類不能訪問基類private成員,所以我們需要調用基類的拷貝構造函數。

hasDMA::hasDMA(const hasDMA& hs): baseDMA(hs) {
    style = new char[std::strlen(hs.style) + 1];
    std::strcpy(style, hs.style);
}

最後是賦值運算符,同樣,由於派生類不能訪問基類中私有成員,我們也需要借助基類的賦值運算符:

hasDMA &hasDMA::operator(const hasDMA& hs) {
    if (this == &hs) return *this;
    baseDMA::operator=(hs);
    delete []style;
    style = new char[std::strlen(hs.style) + 1];
    std::strcpy(style, hs.style);
    return *this;
}

這當中有一個語句看起來有些奇怪:

baseDMA::operator=(hs);

這是我們手動顯式調用瞭基類的賦值運算符,我們直接用等於號賦值也有同樣的效果:

*this = hs;

為什麼不這麼幹呢?這是因為編譯器在執行的時候會默認調用子類的賦值運算符hasDMA::operator=,從而導致一直遞歸導致死循環。

所以我們需要手動寫明作用域解析符,表明這是調用的父類賦值運算符,而非派生類的運算符,這一點比較隱晦,要千萬註意。

到此這篇關於C++繼承和動態內存分配的文章就介紹到這瞭,更多相關C++繼承和動態內存分配內容請搜索WalkonNet以前的文章或繼續瀏覽下面的相關文章希望大傢以後多多支持WalkonNet!

推薦閱讀: