1. 程式人生 > >繼承與動態內存分配

繼承與動態內存分配

避免 析構 turn 其中 更多 變量 差異 動態內存分配 構造

當繼承和動態內存分配的問題交織在一起的時候,我們考慮類實現的時候,就需要考慮更多的東西,先上代碼:

 1 #ifndef DMA_H
 2 #define DMA_H
 3 # include"iostream"
 4 using namespace std;
 5 class BaseDMA
 6 {
 7 //protected:    //將父類私有成員變量private聲明為protected,則繼承類也可直接訪問成員變量,private表明除了自己的類,其余的都不可直接訪問(就算是繼承子類也不可以)
 8     //char* label;
 9     //int rating;
10 private
: 11 char* label; 12 int rating; 13 public: 14 BaseDMA(const char*l = "null", int r = 0);//構造函數 15 BaseDMA(const BaseDMA & rs);//拷貝函數 16 virtual ~BaseDMA();//虛析構函數,思考虛析構函數的意義,虛析構函數是為了避免多態時釋放造成的內存泄露,因此,我們將基類的析構函數定義為虛函數是有好處的 17 BaseDMA & operator=(const BaseDMA & rs);//賦值運算符重載,返回 BaseDMA & 是為了連續賦值,那麽這又是為什麽呢???
18 friend ostream& operator<<(ostream& os, const BaseDMA & rs);//實際上,這裏表明了ostream是一個類,返回ostream&是為了連續顯示,友元函數並不是類成員函數 19 }; 20 class LackDMA :public BaseDMA 21 { 22 23 private: 24 enum{COL_LEN=40};//采用這樣的定義類型更便於程序的管理 25 char color[COL_LEN]; 26 public: //思考要不要重新設置構造函數,析構函數,以及賦值拷貝函數,以及為什麽不需要修改
27 LackDMA(const char*c="black" , const char*l = "null", int r =0); 28 LackDMA(const char*c , const BaseDMA & rs); 29 LackDMA(const LackDMA & rs); 30 friend ostream& operator<<(ostream& os, const LackDMA & rs); 31 }; 32 33 class HasDMA :public BaseDMA 34 { 35 private: 36 char *style; 37 public: 38 HasDMA(const char*s = "none", const char*l = "null", int r = 0); 39 HasDMA(const char*s, const BaseDMA & rs); 40 HasDMA(const HasDMA & rs); 41 ~HasDMA();//為何上述lackdma代碼段不需要重新定義析構函數,為何這裏需要顯示定義析構函數 42 HasDMA & operator=(const HasDMA & rs);//為何這裏的賦值拷貝函數也需要重新定義 43 friend ostream& operator<<(ostream& os, const HasDMA & rs); 44 }; 45 #endif

上述類聲明中,定義了一個基類BaseDMA,以及由該基類衍生的兩個子類:LackDMA,HasDMA;其中,LackDMA類不涉及動態內存分配,HasDMA涉及動態內存分配。關於所涉及的其他知識細節,暫時不進行描述。下面給出類實現部分代碼:

  1 #include"dma.h"
  2 # include "cstring"
  3 using namespace std;
  4 /////////BaseDMA類成員函數實現/////////////////////////
  5 BaseDMA::BaseDMA(const char*l = "null", int r = 0)
  6 {
  7     label = new char[strlen(l) + 1];
  8     strcpy(label, l);
  9     rating = r;
 10 }
 11 BaseDMA::BaseDMA(const BaseDMA & rs)
 12 {
 13     label = new char[strlen(rs.label) + 1];
 14     strcpy(label, rs.label);
 15     rating = rs.rating;
 16 }
 17 
 18 BaseDMA::~BaseDMA()//在虛構函數實現的時候,並不需要virtual關鍵字
 19 {
 20     delete[] label;
 21 }
 22 //我們發現下面的賦值函數和采用類引用的拷貝函數基本功能一致,思考其內在聯系
 23 BaseDMA & BaseDMA:: operator=(const BaseDMA & rs)// 註意返回類型要放在作用域的前面
 24 {
 25     if (this == &rs)//思考在什麽時候顯示的使用this 指針
 26         return *this;
 27     delete[] label;//這裏為何要使用delete[]刪除原來
 28     label = new char[strlen(rs.label) + 1];
 29     strcpy(label, rs.label);
 30     rating = rs.rating;
 31     return *this;
 32 }   //仔細思考這一段代碼背後的機制
 33 
 34 ostream& operator<<(ostream& os, const BaseDMA & rs)//需要註意的吧是。友元函數並不屬於類的成員函數,因此這裏並沒有用BaseDMA::進行類作用域約束
 35 {
 36     os << "Label: " << rs.label << endl;
 37     os << "rating: " << rs.rating << endl;
 38     return os;//返回os是為了形成對<<a<<b的連續顯示效果
 39 }
 40 ///////////////////////LackDMA類成員函數實現///////////////////////////
 41 LackDMA::LackDMA(const char*c = "black", const char*l = "null", int r = 0) :BaseDMA(l, r)
 42 {
 43     strncpy(color, c, 39);//註意這裏的拷貝函數不再是strcpy而是strncpy;
 44     color[39] = \0;//結束標誌符
 45 }
 46 
 47 LackDMA::LackDMA(const char*c, const BaseDMA & rs) :BaseDMA(rs)
 48 {
 49     strncpy(color, c, 39);
 50     color[39] = \0;
 51 }
 52 
 53 LackDMA::LackDMA(const LackDMA & rs) :BaseDMA(rs)
 54 {
 55     strncpy(color, rs.color, 39);
 56     color[39] = \0;
 57 }
 58 
 59 ostream& operator<<(ostream& os, const LackDMA & rs)
 60 {
 61     //os << "Label: " << rs.label << endl;//遇到這種子類無法訪問父類,該怎麽辦???除了可以使用將private聲明為protected之外
 62     //os << "rating: " << rs.rating << endl;//我們當然可以使用將private成員聲明成protected成員,使得子類獲得訪問權限。
 63     os << (const BaseDMA&)rs;//本質是通過基類的成員函數訪問基類的成員變量,註意,子類是無法訪問父類的私有成員變量的,必須通過父類的公有的成員函數進行訪問。
 64     os << "color: " << rs.color << endl;
 65     return os;
 66 }
 67 ////////////hasDMA類成員函數實現/////////
 68 HasDMA::HasDMA(const char*s = "none", const char*l = "null", int r = 0) :BaseDMA(l, r)
 69 {
 70     style = new char[strlen(s) + 1];
 71     strcpy(style, s);
 72 }
 73 HasDMA::HasDMA(const char*s, const BaseDMA & rs) : BaseDMA(rs)
 74 {
 75     style = new char[strlen(s) + 1];
 76     strcpy(style, s);
 77 }
 78 HasDMA::HasDMA(const HasDMA & rs) : BaseDMA(rs)
 79 {
 80     style = new char[strlen(rs.style) + 1];
 81     strcpy(style,rs.style);
 82 }
 83 HasDMA::~HasDMA()
 84 {
 85     delete[] style;
 86 }
 87 HasDMA & HasDMA:: operator=(const HasDMA & rs)  //賦值運算符代碼究竟該怎麽寫,寫成:BaseDMA(rs)為什麽是一種錯誤的寫法
 88 {
 89     if (this == &rs)
 90         return *this;
 91     BaseDMA:: operator=(rs);//無論是哪種初始化方式,對基類的賦值是必不可少的。但這裏為何要采用這種形式呢?
 92     delete[] style;
 93     style = new char[strlen(rs.style) + 1];
 94     strcpy(style, rs.style);
 95     return *this;//返回對象自身
 96 }
 97 ostream& operator<<(ostream& os, const HasDMA & rs)
 98 {
 99     os << (const BaseDMA &)rs;//本質上是通過基類的方法(基類的運算符重載)訪問基類的成員變量,原理同LackDMA中描述的相同。
100     os << "style:" << rs.style << endl;
101     return os;
102 }

上述類成員實現的代碼中,涉及了很多的知識細節,我們尤其要關註的是:

1 子類不能直接訪問父類的私有成員,必須通過父類的共有成員函數對其進行訪問(如代碼91行和99行,但這兩處有所區別,思考其中的差異性),

2 將private聲明成protected,可以使得子類獲得原私有成員的訪問權限。(protected的作用就是為子類提供了一個訪問權限,但對外仍然和private相同)

3 友元函數並不是類成員函數,因此在函數實現的時候,並沒有對其約定作用域解析符。

4 對子類進行初始化的時候,一定是先對父類進行初始化。(無論是采用最原始的初始化,還是復制初始化,還是賦值初始化)

5 思考92行的代碼,為何進行delete[],那麽何時進行的new呢???

思考從聲明子類到銷毀子類,整個程序的執行過程???!!!

繼承與動態內存分配