C++ 類的繼承


先附上實例:

 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_
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.cpp
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   */
studnetc.h
 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 }
studentc.cpp
 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 }   */
studentc-main

類的繼承格式: class lacksDMA :public baseDMA{… …}; baseDMA是基類,lacksDMA是派生類,public表示公有繼承;如果有多個繼承類需要用逗號隔開

:public baseDMA,public std::string{};默認私有繼承。

一、基類與派生類

數據關系:使用公有派生,基類的公有成員將成為派生類的公有成員,基類的私有部分將成為派生類的一部分,但只能通過基類的公有和保護方法訪問。

隱式向上轉換 意味着無需進行顯示類型轉換,就可以將基類指針或引用指向派生類對象。

各種繼承方式
特征 公有繼承 保護繼承 私有繼承
公有成員變成 派生類公有成員 派生類的保護成員 派生類的私有成員
保護成員變成 派生類的保護成員 派生類的保護成員 派生類的私有成員
私有成員變成 只能通過基類接口訪問 只能通過基類接口訪問 只能通過基類接口訪問
能否隱式向上轉換 是(但只能在派生類中)

類型轉換只有派生類能轉換為基類,但即使轉換為基類也不能調用基類的私有成員,因為私有成員的作用域不在派生類中,基類公有成員則能。

類型關系:①is-a,即派生類is-a-kind-of基類,派生類只是基類數據與接口成員的擴充 如蘋果(名稱,顏色)-水果(熱量,重量);

              ②ABC,如從矩形EllipseCircle類中,抽象出它們的共性,將這些特性放到一個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";
View Code

測試結果:

三、繼承和動態內存分配

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{};

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM