先附上實例:
1 #pragma once 2 //dma.h -- inheritance and dynamic memory allocation 3 #ifndef DMA_H_ 4 #define DMA_H__ 5 #include<iostream> 6 #include<cstring> 7 //Base Class Using DMA 8 class baseDMA 9 { 10 private: 11 char *label; 12 int rating; 13 void give_label(const char* b_label) //label的初始化 代碼簡化 14 {int len = std::strlen(b_label); label = new char[len + 1]; strcpy_s(label, len+1, b_label);} 15 public: 16 baseDMA(const char* b_label = "null", int b_rating = 0);//一般構造函數 17 baseDMA(const baseDMA& rs); //復制構造函數 18 void View() { std::cout << "baseDMA :" << label << std::endl; }//業務函數View 19 virtual void vView() { std::cout << "virtual baseDMA :" << label << std::endl; }//業務虛函數vView 20 virtual ~baseDMA(); 21 baseDMA& operator=(const baseDMA& rs); //賦值=運算符 22 friend std::ostream& operator<<(std::ostream& os, const baseDMA& rs); 23 }; 24 25 //derived class without DMA 26 //no destructor needed 27 //uses implicit copy constructor 28 //uses implicit assignment operator 29 class lacksDMA :public baseDMA 30 { 31 private: 32 enum { COLOR_LEN = 40 }; 33 char color[COLOR_LEN]; 34 public: 35 lacksDMA(const char* l_color = "blank", const char* l_label = "null", int l_rating = 0);//一般構造函數 36 lacksDMA(const char* l_color, const baseDMA& rs); //基類構造函數 37 void View(){ std::cout << "lacksDMA :" << color << std::endl; }//業務函數View 38 virtual void vView() { std::cout << "virtual lacksDMA :" << color << std::endl; }//業務虛函數vView 39 friend std::ostream& operator<<(std::ostream& os, const lacksDMA& rs); 40 }; 41 //derived class with DMA 42 class hasDMA :public baseDMA 43 { 44 private: 45 char* style; 46 void give_style(const char* h_style) //style的初始化 代碼簡化 47 {int len = std::strlen(h_style); style = new char[len + 1]; strcpy_s(style, len+1, h_style);} 48 public: 49 hasDMA(const char* h_style = "none", const char* h_lable = "null", int h_rating = 0);//一般構造函數 50 hasDMA(const char* s, const baseDMA& rs); //基類構造函數 51 hasDMA(const hasDMA& hs); //復制構造函數 52 void View() { std::cout << "hasDMA :" << this->style << std::endl; }//業務函數View 53 virtual void vView() { std::cout << "virtual hasDMA :" << style << std::endl; }//業務虛函數vView 54 ~hasDMA(); 55 hasDMA&operator=(const hasDMA& rs); //賦值=運算符 56 friend std::ostream& operator<<(std::ostream &os, const hasDMA& rs); 57 }; 58 #endif // !DMA_H_
1 //dma.cpp -- dma class methods 2 #include "dma.h" 3 4 5 //baseDMA methods 6 baseDMA::baseDMA(const char* b_label, int b_rating) //一般構造函數 7 { 8 give_label(b_label); 9 /* int len = strlen(b_lable) + 1; 10 label = new char[len]; 11 strcpy_s(label,len, b_lable); */ 12 rating = b_rating; 13 } 14 baseDMA::baseDMA(const baseDMA& rs) //復制構造函數 15 { 16 give_label(rs.label); 17 /* int len = strlen(b_lable) + 1; 18 label = new char[len]; 19 strcpy_s(label,len, b_lable); */ 20 rating = rs.rating; 21 } 22 baseDMA::~baseDMA() { delete[] label; } 23 baseDMA& baseDMA::operator=(const baseDMA& rs) //賦值運算符 24 { 25 if (this == &rs) 26 return *this; 27 delete[]label; 28 give_label(rs.label); 29 /* int len = strlen(rs.label) + 1; 30 label = new char[len]; 31 strcpy_s(label, len, rs.label); */ 32 rating = rs.rating; 33 return *this; 34 } 35 std::ostream& operator<<(std::ostream& os, const baseDMA& rs) 36 { 37 os << "Label:" << rs.label << std::endl; 38 os << "Rating: " << rs.rating << std::endl; 39 return os; 40 } 41 42 //lacksDMA methods //一般構造函數 43 lacksDMA::lacksDMA(const char* l_color, const char* l_label, int l_rating) :baseDMA(l_label, l_rating) 44 { 45 strncpy_s(color, l_color, 39); 46 color[39] = '\0'; 47 } 48 lacksDMA::lacksDMA(const char* c, const baseDMA& rs) : baseDMA(rs) //基類構造函數 49 { 50 strncpy_s(color, c, COLOR_LEN - 1); 51 color[COLOR_LEN - 1] = '\0'; 52 } 53 std::ostream& operator<<(std::ostream& os,const lacksDMA& ls) 54 { 55 os << (const baseDMA&)ls; 56 os << "Color: " << ls.color << std::endl; 57 return os; 58 } 59 //void lacksDMA::View() { const baseDMA& bs = *this; std::cout << "lacksDMA :" << bs.label; } 60 //hasDMA methods 61 hasDMA::hasDMA(const char* h_style, const char* h_label, int h_rating) :baseDMA(h_label, h_rating) 62 { //一般構造函數 63 give_style(h_style); 64 /* int len = strlen(h_style) + 1; 65 style = new char[len]; 66 strcpy_s(style, len, h_style ); */ 67 } 68 hasDMA::hasDMA(const char* h_style, const baseDMA& rs) : baseDMA(rs) //基類構造函數 69 { 70 give_style(h_style); 71 /* int len = strlen(h_style) + 1; 72 style = new char[len]; 73 strcpy_s(style, len, h_style ); */ 74 } 75 hasDMA::hasDMA(const hasDMA& hs):baseDMA(hs) //復制構造函數 76 { 77 give_style(hs.style); 78 /* int len = strlen(h_style) + 1; 79 style = new char[len]; 80 strcpy_s(style, len, h_style ); */ 81 } 82 hasDMA::~hasDMA() { delete[] style; } 83 hasDMA&hasDMA::operator=(const hasDMA& hs) //賦值運算符 84 { 85 if (this == &hs) 86 return *this; 87 baseDMA::operator=(hs); 88 delete[]style; 89 give_style(hs.style); 90 /* int len = strlen(h_style) + 1; 91 style = new char[len]; 92 strcpy_s(style, len, h_style ); */ 93 return *this; 94 } 95 std::ostream&operator<<(std::ostream &os, const hasDMA&hs) 96 { 97 os << (const baseDMA&)hs; 98 os << "Style: " << hs.style << std::endl; 99 return os; 100 }
dma-mamin.cpp
1 #pragma once 2 //studentc.h -- defining aStudent class using containment 3 #ifndef STUDENTC_H_ 4 #define STUDENTC_H_ 5 6 #include<iostream> 7 #include<string> 8 #include<valarray> 9 class Student 10 { 11 private: 12 typedef std::valarray<double>ArrayDb; 13 std::string name; 14 ArrayDb scores; 15 //prevate method for scores output 16 std::ostream& arr_out(std::ostream& os)const; 17 public: 18 Student():name("Null Student"),scores(){} 19 explicit Student(const std::string &s):name(s),scores(){} 20 explicit Student(int n):name("Nully"),scores(n){} 21 Student(const std::string&s,int n):name(s),scores(n){} 22 Student(const std::string&s,const ArrayDb& a):name(s),scores(a){} 23 Student(const char*str, const double*pd, int n) :name(str), scores(pd, n){} 24 ~Student(){} 25 26 double Average()const; //get average scores 27 const std::string& Name()const; //get name 28 double&operator[](int i); //get scores[i] 29 double operator[](int i)const; 30 //friend input1 a word,2 a line; output 31 friend std::istream& operator>>(std::istream &is, Student &stu); //cin>>name 32 friend std::istream& getline(std::istream&is, Student& stu); //cout<<name 33 friend std::ostream& operator<<(std::ostream&os,const Student &stu); //cout<<name and arr_out()scores 34 }; 35 #endif // ! STUDENTC_H_ 36 /* valarray類簡介 37 函數構造例子 38 valarray<double> v1;//an array of double,size 0 39 valarray<int> v2(8); //an array of 8 int elements 40 valarray<int> v2(8); //an array of 8 int elements, each set 10 41 double gps[5]={1,2,3,4,5}; 42 valarray<double> v2(gps,4); //an array of 4 elements,initalized to the first 4 elements of gps 43 valarray<int> v={6,7,8,9,10,11}; 44 方法: 45 operator[]() size() sum() max() min() 46 */
1 //studentc.cpp -- Student class using containment 2 #include "studentc.h" 3 using std::ostream; 4 using std::istream; 5 using std::endl; 6 using std::string; 7 //public methods 8 double Student::Average()const 9 { 10 if (scores.size() > 0) 11 return scores.sum() / scores.size(); 12 else 13 return 0; 14 } 15 const string& Student::Name()const { return name; } 16 //use valarray<double>::operator[]() 17 double& Student::operator[](int i) { return scores[i]; } 18 double Student::operator[](int i)const { return scores[i]; } 19 //private method 20 ostream& Student::arr_out(ostream& os)const 21 { 22 int i; int lim = scores.size(); 23 if (lim > 0) 24 { 25 for ( i= 0; i < lim; i++) 26 { 27 os << scores[i] << " "; 28 if (i % 5 == 4) os << endl; 29 } 30 if (i % 5 != 0) os << endl; 31 } 32 else 33 os << " empty array "; 34 return os; 35 } 36 //friends 37 //use string version of operator>>() 38 istream& operator>>(istream& is, Student& stu) 39 { 40 is >> stu.name; 41 return is; 42 } 43 istream&getline(istream& is, Student &stu) 44 { 45 getline(is, stu.name); 46 return is; 47 } 48 ostream& operator<<(ostream& os,const Student& stu) 49 { 50 os << "Scores for "<<stu.name<<":\n"; 51 stu.arr_out(os); 52 return os; 53 }
1 //use_stuc.cpp -- using acomposite class 2 //copile with studentc.cpp 3 #include<iostream> 4 #include "studentc.h" 5 using std::cin; 6 using std::cout; 7 using std::endl; 8 //void set(Student&sa, int n); 9 const int NStudent = 3; 10 const int NScoure = 5; 11 int main() 12 { 13 14 return 0; 15 } 16 /* 17 Student ada[NStudent] = { Student(NScoure),Student(NScoure),Student(NScoure) }; 18 int i; 19 for (i = 0; i < NStudent; ++i) 20 set(ada[i], NScoure); 21 22 cout << "\nStudent List:\n"; 23 for (i = 0; i < NStudent; ++i) 24 cout << ada[i].Name() << endl; 25 26 cout << "\nResults:"; 27 for (i = 0; i < NStudent; ++i) 28 { 29 cout << endl << ada[i]; 30 cout << "average: " << ada[i].Average() << endl; 31 } 32 cout << "Done.\n"; 33 cin.get(); 34 35 void set(Student& sa, int n) 36 { 37 cout << "Please enter the student's name: "; 38 getline(cin, sa); 39 cout << "Please enter " << n << " quiz scores:\n"; 40 for (int i = 0; i < n; i++) 41 cin >> sa[i]; 42 while (cin.get() != '\n') 43 continue; 44 } */
類的繼承格式: class lacksDMA :public baseDMA{… …}; baseDMA是基類,lacksDMA是派生類,public表示公有繼承;如果有多個繼承類需要用逗號隔開,
:public baseDMA,public std::string{};默認私有繼承。
一、基類與派生類
數據關系:使用公有派生,基類的公有成員將成為派生類的公有成員,基類的私有部分將成為派生類的一部分,但只能通過基類的公有和保護方法訪問。
隱式向上轉換 意味着無需進行顯示類型轉換,就可以將基類指針或引用指向派生類對象。
| 特征 | 公有繼承 | 保護繼承 | 私有繼承 |
| 公有成員變成 | 派生類公有成員 | 派生類的保護成員 | 派生類的私有成員 |
| 保護成員變成 | 派生類的保護成員 | 派生類的保護成員 | 派生類的私有成員 |
| 私有成員變成 | 只能通過基類接口訪問 | 只能通過基類接口訪問 | 只能通過基類接口訪問 |
| 能否隱式向上轉換 | 是 | 是(但只能在派生類中) | 否 |
類型轉換:只有派生類能轉換為基類,但即使轉換為基類也不能調用基類的私有成員,因為私有成員的作用域不在派生類中,基類公有成員則能。
類型關系:①is-a,即派生類is-a-kind-of基類,派生類只是基類數據與接口成員的擴充 如蘋果(名稱,顏色)-水果(熱量,重量);
②ABC,如從矩形Ellipse和Circle類中,抽象出它們的共性,將這些特性放到一個ABC中,然后從ABC中派生出Circle和Ellipse類。(見dma代碼)
③has-a,方式1私有繼承,:private std::string,private std::valarray<int>{};方式2包含,{private:std::string str1;……};(見studentc代碼)
has-a利用他人開發好的string 為動態內存分配省去了大量支持代碼,方式1不能是公有繼承,因為那樣會使Student繼承string的+接口,
而兩個Student類型相加是無意義的。
建議使用③的包含方式,方式2比1強在string可以聲明多個數據名稱,而且可以直接對string數據操作。
二、重要函數
基類構造函數: 1. hasDMA::hasDMA(const char* h_style, const baseDMA& rs) : baseDMA(rs)
2. explicit Student(const std::string &s):name(s),scores(){}
eg1 :hasDMA是baseDMA的派生類,參數rs將轉換為baseDMA變量去初始化 hasDMA對象的基類數據部分;參數s初始化數據name。
參數 初始化列表格式: ①基類名(能轉換為基類的參數變量) ②類數據名稱(參數變量) 如name(s)。多個參數參數用逗號隔開。
無參 其中score()調用valarray<double>()會默認構造函數,也可以有baseDMA();
注意,關鍵字explicit可防止 stu="hello world";這樣的隱式轉換。
基類與派生類可以定義相同名稱的函數。類對象(或該類引用、指針形式)將調用對應類的方法;(baseDMA)has1,(baseDMA&)has1,(baseDMA*)has1將調用基類方法。
虛函數格式:函數聲明前加上關鍵字virtual。(虛函數可以不必實現)
對象(或該類引用、指針形式)將調用對應類的方法。(baseDMA)has1調用基類虛方法,(baseDMA&)has1,(baseDMA*)has1將調用派生類虛方法。eg:dmp-main.cpp
1 hasDMA has1("blue", "has_rank", 5); 2 baseDMA* has2 = &has1; baseDMA& has3 = has1; 3 cout << "has1實函數 :\nhas1、 (baseDMA*)has1、 (baseDMA&)has1\n"; 4 has1.View(); has2->View(); has3.View(); 5 cout << "\nhas1虛函數virtual :\nhas1、 (baseDMA*)has1、 (baseDMA&)has1\n"; 6 has1.vView(); has2->vView(); has3.vView(); 7 cout << "\n\n";
測試結果:

三、繼承和動態內存分配
1.派生類不使用new 如dmp中lackDMA類不需要定義析構函數、復制構造函數和賦值運算符。基類構造函數還是要的。
2.派生類使用new 如dmp中hasDMA類需要使用這三個函數。
派生類的基類數據部分:一般構造函數和基類構造函數,復制構造函數和賦值運算符也需要重寫。
所以基類(如果也使用了new)有3部分代碼重復,派生類有4部分代碼重復。
①class baseDMA{ private: char* label; int rating; void give_label(const char* b_label,int b_rating) //label的初始化 代碼簡化 { int len = std::strlen(b_label); label = new char[len + 1]; strcpy_s(label, len + 1, b_label); rating = b_rating; } 1、baseDMA::baseDMA(const char* b_label, int b_rating) //一般構造函數 { give_label(b_label,b_rating);} 2、baseDMA::baseDMA(const baseDMA& rs) //復制構造函數 { give_label(rs.label,rs.rating); } 3、baseDMA& baseDMA::operator=(const baseDMA& rs) //賦值運算符 { if (this == &rs) return *this; delete[]label; give_label(rs.label,rs.rating); return *this; } ②class hasDMA :public baseDMA{ private: char* style; void give_style(const char* h_style) //style的初始化 代碼簡化 {int len = std::strlen(h_style); style = new char[len + 1]; strcpy_s(style, len+1, h_style);} 1、hasDMA::hasDMA(const char* h_style, const char* h_label, int h_rating) :baseDMA(h_label, h_rating) //一般構造函數 參數列表-基類部分 { give_style(h_style); } 2、hasDMA::hasDMA(const char* h_style, const baseDMA& rs) : baseDMA(rs) //基類構造函數 參數列表-基類部分 { give_style(h_style);} 3、hasDMA::hasDMA(const hasDMA& hs):baseDMA(hs) //復制構造函數 參數列表-基類部分 { give_style(hs.style);} 4、hasDMA::~hasDMA() { delete[] style; } hasDMA& hasDMA::operator=(const hasDMA& hs) //賦值運算符 調用處理-基類部分eg,baseDMA::operator=(hs) { if (this == &hs) return *this; baseDMA::operator=(hs); //調用處理基類部分 delete[]style; give_style(hs.style); return *this; }
基類友元函數不是成員函數,派生類不能直接調用,但在可以強制轉換為基類調用eg: os << (const baseDMA&)hs;
os<<hs操作須保證hs為const類型, 而(const hasDMA)類型只能調用const函數如 void View()const{};
