C++ 大學MOOC 北大課程(郭煒老師)聽課整理 第五週(類的關係)

繼承與派生概念

1)如果新定義的類B和已有的類A有相似的地方,則可以將類A作爲類B的基類,類B是類A的派生類
2)派生類是對基類的擴充
3)派生類繼承基類中所有的成員,包括成員變量和成員函數
4)在派生類中的成員函數中不可訪問基類的私有成員變量
派生類的寫法:
class 派生類名:public 基類名{

}
5)派生類的內存空間包括了基類的成員變量和新定義的成員變量
例如:

class cstudent{ //基類
private:
 string name; //學生姓名
 string id; //學生學號 
 char gender;  //性別
 int age;  //年齡
public:
 void printinfo();  //打印學生信息
 void setinfo(const string& name_, const string& id_, char gender_, int age_);  //錄入學生信息
 string getname(){ return name; }  //返回學生姓名
};
void cstudent::printinfo(){
 cout << "Name:" << name << endl;
 cout << "ID:" << id << endl;
 cout << "Gender:" << gender << endl;
 cout << "Age:" << age << endl;
}
void cstudent::setinfo(const string& name_, const string& id_, char gender_, int age_){
 name = name_;
 id = id_;
 gender = gender_;
 age = age_;
}
class cundergraduatestudent :public cstudent{  //派生類 本科生
private:
 string department;  //所在系
public:
 void canbaoyan(){
  cout << "qualified for baoyan" << endl;
 }
 void printinfo(){
  cstudent::printinfo();  //調用基類函數打印本科生學生信息
  cout << "Department:" << department << endl;
 }
 void setinfo(const string& name_, const string& id_, char gender_, int age_, const string& department_){
  cstudent::setinfo(name_, id_, gender_, age_);  //調用基類函數例如本科生學生信息
  department = department_;
 }
};
int main(){
 cundergraduatestudent s1;
 s1.setinfo("Hery", "123456", 'M', 16, "Vehicle Engineering");
 cout<<s1.getname()<<' ';
 s1.canbaoyan();
 s1.printinfo();
 return 0;
}

輸出:

Hery qualified for baoyan
Name:Hery
ID:123456
Gender:M
Age:16
Department:Vehicle Engineering

繼承和複合關係

1)類和類之間有兩種關係:繼承和複合
2)繼承關係講究”是“:類A是類B,則類A是類B的派生
3)複合關係講究”有“:類A有類B,即類A當中包含類B對象成員
例1:繼承的使用
男人類和女人類都是”人“,則可以定義人類,將男人類和女人類定義爲人類的派生類
例2:複合的使用
圓類和點類,圓可以通過兩個元素來確定:半徑和圓心座標,圓心屬於點類的對象,所以圓類和點類是複合關係,圓類中有點類成員
例3:複合的使用
需要寫一個小區的養狗程序,需要寫”業主“類和”狗“類。規定每個狗只有一個主人,一個主人最多有十隻狗
問題寫法一:

class CDog;
class CMaster{
 CDog  dogs[10];
};
class CDog{
 CMaster m;
};

問題所在:出現”狗中有人,人中有狗“循環定義現象,比如定義類CDog對象時,編譯器分配內存空間時需要分配一個類CMaster對象的空間,而類CMaster對象又包含是個定義類CDog對象的空間。
問題寫法二:

class CDog;
class CMaster{
 CDog *dogs[10];  //定義指針數組 每一個指針元素指向一個類CDog對象
};
class CDog{
 CMaster m;
};

問題所在:每一個狗都包含一個人,但當存在不同狗都屬於同一個主人的情況時,其中一個狗的主人的信息一旦修改,就需要設法保證同屬一個主人的其他狗中的主人對象的信息也得修改。
問題寫法三:

class CDog;
class CMaster{
 CDog dogs[10];
};
class CDog{
 CMaster * m;  //狗中存放指向主人的指針
};

問題所在:必須通過主人對象修改狗的信息,將致成諸多不便
正確寫法:

class CDog;
class CMaster{
 CDog *dogs[10];
};
class CDog{
 CMaster * m;
};

狗的對象包含指向人的指針,人的最想包含指向狗的指針 既保證人與狗的關係 又能保證兩者的相互獨立。

覆蓋和保護成員

覆蓋

1)當派生類中定義了和基類同名的成員,直接調用時缺省爲派生類的成員
2)要在派生類中訪問與基類同名成員時,需要使用作用域符號::
例如:

class base{
 int j;
public:
 int i;
 void func();
};
class derived :public base{
public:
 int i;  //與基類成員變量同名
 void access();
 void func();  //與基類成員函數同名
};
void derived::access(){
 j = 5;  //編譯出錯,派生類成員函數不能訪問從基類繼承的私有成員變量
 i = 5;  //訪問的是派生類的名 i 的成員變量
 base::i = 8;  //訪問從基類繼承的同名爲 i 的成員變量
 func();  //調用派生類的名 func 的成員函數
 base::func();  //調用從基類繼承的同名爲 func 的成員函數
}
int main(){
 derived r;
 r.i = 1;  //修改派生類對象中名 i 的成員變量
 r.base::i = 1;  //修該派生類對象中繼承於基類的同名爲 i 的成員變量
 return 0;
}

一般 不要將成員變量覆蓋

保護成員

1)用成員訪問範圍說明符protected標識 的成員爲保護成員,其訪問範圍:基類成員函數、基類友元函數、派生類對象成員函數(能訪問所作用的對象繼承的基類保護成員)
2)由private標識的成員爲私有成員,其只有基類的成員函數和基類友元函數能訪問
3)由public標識的成員爲公有成員,其可以在任何地方訪問
例如:

class father{
private:
 int nprivate;
public:
 int npublic;
protected:
 int nprotected;
};
class son :public father{
 void access(){
  npublic = 1;  
  nprotected = 3;  //正確 可以訪問基類的保護成員
  nprivate = 4;    //錯誤 不可訪問基類私有成員
  son f;
  f.nprotected = 8;  //錯誤  f不是當前對象
 }
};

派生類的構造函數

1)初始化派生類對象時,需要調用基類的構造函數:先執行基類的構造函數,再執行派生類的構造函數
2)調用基類構造函數的方式有兩種
3)一種是顯式方式:使用初始化列表調用基類構造函數
4)一種是隱式方式:在派生類的構造函數中省略基類的構造函數,則缺省調用基類的無參構造函數
5)調用析構函數時:先調用派生類的析構函數,再調用基類的析構函數
例如:

class Base{
public:
 int n;
 Base(int i) :n(i){
  cout << "Base " << n << " constructed" << endl;
 }
 ~Base(){
  cout << "Base " << n << " destructed" << endl;
 }
};
class Derived :public Base{
public:
 Derived(int i) :Base(i){  //顯式調用基類構造函數
  cout << "Derived " << "constructed" << endl;
 }
 ~Derived(){
  cout << "Derived " << "destructed" << endl;
 }
};
int main(){
 Derived obj(4);
 return 0;
}

輸出:

Base 4 constructed
Derived constructed
Derived destructed
Base 4 destructed

封閉派生類對象構造函數

1)封閉派生類對象初始化時,先執行基類的構造函數,再執行成員對象類的構造函數,最後執行自己的構造函數
2)封閉派生類對象消亡時,先執行自己的析構函數,再執行成員對象類的析構函數,最後執行基類的析構函數
例如:

class Bug{
private:
 int nLegs;
 int nColor;
public:
 int nType;
 Bug(int legs, int color);
 void PrintBug();
};
class skill{
public:
 skill(int n);
};
class FlyBug :public Bug{  //這是一個封閉派生類
private:
 int nWings;   //成員變量
 skill sk1, sk2;  //含有成員對象
public:
 FlyBug(int legs, int color, int wings);  //聲明構造函數
};
FlyBug::FlyBug(int legs, int color, int wings)
 :Bug(legs,color),sk1(4),sk2(color),nWings(wings){} //初始化列表對基類、對象、成員變量初始化

公有繼承的賦值兼容規則

1)派生類的對象可以賦值給基類對象
2)派生類對象可以初始化基類引用
3)派生類對象地址可以賦值給基類指針
例如:

Base * ptrBase = &objDrived;

objDrived 是類Base的派生類對象,可以通過指針 ptrBase 直接訪問基類的公有成員,但不能訪問在 objDrived 對象中屬於派生類而不屬於基類的成員。

直接基類和間接基類

1)類A派生類B,類B派生類C,則稱類A是類C的間接基類而類B是類C的直接基類
2)派生類繼承直接類和所有間接類的所有成員
3)聲明派生類時,只需要列出它的直接基類
例如:

class Base{
public:
 int n;
 Base(int i):n(i){
  cout << "Base " << n << " constructed" << endl;
 }
 ~Base(){
  cout << "Base " << n << " destructed" << endl;
 }
};
class Derived :public Base{
public:
 Derived(int i) :Base(i){
  cout << "Derived " << "constructed" << endl;
 }
 ~Derived(){
  cout << "Derived " << "destructed" << endl;
 }
};
class moreDerived :public Derived{  //間接基類是 Base 
public:
 moreDerived(int i) :Derived(i){
  cout << "moreDerived " << "constructed" << endl;
 }
 ~moreDerived(){
  cout << "moreDerived " << "destructed" << endl;
 }
};
int main(){
 moreDerived obj(4);
 return 0;
}

輸出:

Base 4 constructed
Derived constructed
moreDerived constructed
moreDerived destructed
Derived destructed
Base 4 destructed
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章