摘自《c++ Primer Plus》第6版 13.7ios
若是基類使用的動態內存分配,並從新定義了賦值和複製構造函數,這將怎樣影響派生類的實現?c++
假設基類在構造函數中使用new,析構函數、複製構造函數和重載賦值運算符也作了相應處理。如今,從base類派生出一個類,這個派生類不使用new,只是包含一些新的數據而已。那麼就不須要爲派生類定義顯式析構函數、複製構造函數、賦值運算符。函數
派生類對象的這些屬性也適用於包含其餘類對象成員的類。例如實現Stock
類時,能夠用string
類而不是char *
來存儲公司名稱。衆所周知string
類有采用動態內存分配,而Stock
的默認構造函數不會產生問題,咱們如今知道了緣由——默認構造函數使用string
的複製構造函數來複制company
成員,Stock
的默認賦值運算符使用string
的賦值運算符來給company
對象賦值,而Stock
的析構函數會自動調用string
類的析構函數。this
在這種狀況下,必須爲派生類定義顯示析構函數、複製構造函數、賦值運算符。spa
//包含動態內存分配的類繼承 #ifndef _DMA_H_ #define _DMA_H_ #include<iostream> 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); friend std::ostream & operator<<(std::ostream & os, const baseDMA & rs); }; class lacksDMA :public baseDMA { private: enum { COL_LEN = 40 }; char color[COL_LEN]; public: lacksDMA(const char * c = "blank", const char * l = "null", int r = 0); lacksDMA(const char * c, const baseDMA & rs); friend std::ostream & operator<<(std::ostream & os, const lacksDMA & rs); }; class hasDMA :public baseDMA { private: char * style; public: hasDMA(const char * c = "none", const char * l = "null", int r = 0); hasDMA(const char * s, const baseDMA & rs); hasDMA(const hasDMA & hs); ~hasDMA(); hasDMA & operator=(const hasDMA & hs); friend std::ostream & operator<<(std::ostream & os, const hasDMA & hs); }; #endif #include<cstring> baseDMA::baseDMA(const char *l, int r) { label = new char[std::strlen(l) + 1]; std::strcpy(label, l); rating = r; } baseDMA::baseDMA(const baseDMA & rs) { label = new char[std::strlen(rs.label) + 1]; std::strcpy(label, rs.label); rating = rs.rating; } baseDMA::~baseDMA() { delete[] label; } baseDMA & baseDMA::operator=(const baseDMA & rs) { if (this == &rs) return *this; delete[]label; label = new char[std::strlen(rs.label) + 1]; std::strcpy(label, rs.label); rating = rs.rating; return *this; } std::ostream & operator<<(std::ostream & os, const baseDMA &rs) { os << "Label: " << rs.label << std::endl; os << "Rating: " << rs.rating << std::endl; return os; } lacksDMA::lacksDMA(const char * c, const char * l, int r) :baseDMA(l,r) { std::strncpy(color, c, 39); color[39] = '\0'; } lacksDMA::lacksDMA(const char * c, const baseDMA & rs) :baseDMA(rs) { std::strncpy(color, c, COL_LEN - 1); color[COL_LEN - 1] = '\0'; } std::ostream & operator<<(std::ostream & os, const lacksDMA & rs) { os << (const baseDMA &)rs; os << "Color: " << rs.color << std::endl; return os; } hasDMA::hasDMA(const char * c, const char * l, int r) :baseDMA(l, r) { style = new char[std::strlen(c) + 1]; std::strcpy(style, c); } hasDMA::hasDMA(const char * s, const baseDMA & rs) :baseDMA(rs) { style = new char[std::strlen(s) + 1]; std::strcpy(style, s); } hasDMA::hasDMA(const hasDMA & hs) :baseDMA(hs) { style = new char[std::strlen(hs.style) + 1]; std::strcpy(style, hs.style); } hasDMA::~hasDMA() { delete[]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; } std::ostream & operator<<(std::ostream & os, const hasDMA & hs) { os << (const baseDMA &)hs; os << "Style: " << hs.style << std::endl; return os; }
基類使用了動態內存分配,因此聲明包含了使用new時所須要的特殊方法:設計
析構函數:virtual ~baseDMA();
code
複製構造函數:baseDMA(const baseDMA & rs);
對象
重載賦值運算符:baseDMA & operator=(const baseDMA & rs);
繼承
lacksDMA類,沒有使用動態內存分配,因此無需提供特殊方法。內存
這裏有兩個派生類,派生類析構函數會自動調用基類的析構函數,因此各自的職責就是對派生類構造函數執行的工做進行清理。hasDMA
類釋放style
,baseDMA
類釋放label
。
做爲派生類的友元,<<
重載函數,不是基類的友元,那怎麼訪問基類成員label
和rating
呢?答案是使用baseDMA::operator<<()
。由於友元不是成員函數,不能經過做用域解析運算符來指示要使用哪一個函數,因此這裏的處理方法是使用強制類型轉換,以便經過匹配正確原型來使用正確的函數。
hasDMA
類的複製構造函數只能訪問派生類的數據,因此它必須調用baseDMA
的複製構造函數來處理共享的基類數據.
baseDMA::operator=(hs);
實際上該語句的語義是:*this = hs;
也就是說,使用基類的賦值運算符,來複制派生類對象的基類部分的數據。爲使用了new的派生類設計賦值運算符時,必須給類的每一個成員都提供賦值運算符,而不單單是新的。
當基類和派生類都採用動態內存分配時,派生類的析構函數、複製構造函數、賦值運算符,都必須使用相應的基類方法來處理基類元素。