學習鏈表繼承
https://bbs.csdn.net/topics/300237086
https://www.cnblogs.com/gentle-min-601/p/9556920.html
一 單繼承
由基類得到派生類。
派生方式:默認為私有繼承
public 公有繼承
private 私有繼承
protected 保護繼承
注意!!
派生類不能繼承基類的構造函數和析構函數
單繼承示例代碼
#include <iostream> #include<string> #include<cstring> using namespace std; class Student { private: int number; string name; public: Student() { number = 0; name= ""; } void SetValue(int n, string s1) { number = n; name = s1; } void Print() { cout << "Numver:" << number << endl; cout << "Name:" << name << endl; } }; class UGStudent :public Student { private: int age; int grade; public: UGStudent() { SetValue(0, ""); age = 0; grade = 0; } UGStudent(int n, string s1, int a, int g) { SetValue(n,s1); age = a; grade = g; } void PrintExtra() { cout << "Age:" << age << endl; cout << "Grade:" << grade << endl; } }; int main() { UGStudent st1(100, "wang", 18, 1); st1.Print(); //調用基類的函數 st1.PrintExtra(); //調用派生類的新定義的函數 return 0; };
二 單繼承的訪問權限控制
公有繼承的訪問權限控制
基類的public和protected成員的訪問屬性在派生 類中保持不變,但基類的private成員不可訪問。
派生類中的成員函數可以直接訪問基類中的 public和protected成員,但不能訪問基類的 private成員。
通過派生類的對象只能訪問基類的public成員。
#include <iostream> #include<string> #include<cstring> using namespace std; class Person //定義基類Person { public: //外部接口 Person(const string Name, int Age, char Sex); //基類構造函數 string GetName() { return name; } int GetAge(); //基類成員函數的聲明 char GetSex(); void Display(); private: string name; char sex; protected: //保護成員 int age; }; Person::Person(const string Name, int Age, char Sex) //基類構造函數的實現 { name = Name; age = Age; sex = Sex; } //基類成員函數的實現 int Person::GetAge() { return(age); } char Person::GetSex() { return(sex); } //基類成員函數的實現 void Person::Display() { //直接訪問本類私有成員 cout << "name:" << name << '\t'; cout << "age:" << age << '\t'; cout << "sex:" << sex << endl; } class Student :public Person //定義公有繼承的學生類 { public: //外部接口 Student(string pName, int Age, char Sex, string pId, float Score) : Person(pName, Age, Sex) //調用基類的構造函數初始化基類的數據成員 { id=pId; //學生類的數據初始化 score = Score; } string GetId(string pId) //派生類的新成員 { return(id); } float GetScore() //派生類的新成員 { return score; } void Display(); //派生類的新成員 private: string id; float score; }; void Student::Display() //派生類的成員函數的實現 { cout << "id:" << id << '\t'; //直接訪問本類私有成員 cout << "age:" << age << '\t'; //訪問基類的保護成員 cout << "score:" << score << endl; } int main() { string name; cout << "Enter a person′s name:"; cin >> name; Person p1(name, 29, 'm'); //基類對象 p1.Display(); //基類對象訪問基類公有成員函數 string pId; cout << "Enter a student′s name:"; cin >> name; Student s1(name, 19, 'f', "03410101", 95); //派生類對象 //派生類對象訪問基類成員函數 cout << "name:" << s1.GetName() << '\t'; cout << "id:" << s1.GetId(pId) << '\t'; cout << "age:" << s1.GetAge() << '\t'; cout << "sex:" << s1.GetSex() << '\t'; cout << "score:" << s1.GetScore() << endl; return 0; }
私有繼承的訪問權限控制
基類的public和protected成員都以private身份出現 在派生類中,但基類的private成員不可訪問。
派生類中的成員函數可以直接訪問基類中的public 和protected成員,但不能訪問基類的private成員。
通過派生類的對象不能訪問基類中的任何成員。
私有繼承之后,基類的成員再也無法在以后的派生 類中發揮作用,出於這種原因,一般不使用私有繼 承方式。
好處:基類原有的外部接口被派生類封閉和隱藏起來
#include <iostream> #include<string> #include<cstring> using namespace std; class Parent //定義基類Parent { protected: int x; public: void setx() { x = 0; } void display() { cout << "x=" << x << endl; } }; class Son :private Parent //定義派生類Son { public: void increase() //新增成員函數 { x++; //有效 } }; int main() { Son s; //s.setx(); //錯誤:不能訪問基類的任何成員 s.increase(); //有效 //s.display(); //錯誤:不能訪問基類的任何成員 return 0; }
#include <iostream> #include<string> #include<cstring> using namespace std; class Parent //定義基類Parent { public: int pubD; protected: int protD; private: int privD; }; class Son :public Parent //定義公有派生類Son { public: void fn() { int x; x = pubD; //有效 x = protD; //有效 //x = privD; //錯誤:不能訪問 } }; class Daughter :private Parent //定義私有派生類 { public: void fn() { int y; y = pubD; //有效,但pubD被降為私有 y = protD; //有效,但pubD被降為私有 //y = privD; //錯誤 } }; int main() { Parent p; //基類對象 p.pubD = 10; //有效 //p.protD = 10; //錯誤 //p.privD = 10; //錯誤 Son s; //公有派生類對象 s.pubD = 20; //有效 //s.protD = 20; //錯誤 //s.privD = 20; //錯誤 Daughter d; //私有派生類對象 //d.pubD = 30; //錯誤:不能訪問,pubD已降為私有 //d.protD = 30; //錯誤 //d.privD = 30; //錯誤 return 0; }
私有派生類中的基類對象被降低為私有?
保護繼承的訪問權限控制
基類的public和protected成員都以protected身份出 現在派生類中,但基類的private成員不可訪問
派生類中的成員函數可以直接訪問基類中的public 和protected成員,但不能訪問基類的private成員。
通過派生類的對象不能訪問基類中的任何成員。
保護繼承與私有繼承的區別在於能否進一步將基類 成員傳遞給派生類的派生類,保護繼承可以傳遞部 分基類成員,但私有繼承不可以。
#include <iostream> using namespace std; class demo //定義基類demo { protected: int j; public: demo( ) { j = 0; } void add(int i) { j += i; } void sub(int i) { j -= i ; } void display() {cout<<"current value of j is "<<j<<endl; } }; class child: protected demo { public: void sub(int i) { j -= i ;} //有效:訪問基類中的保護數據成員 }; int main( ) { chlid object; object.display(); object.add(10); object.display(); object.sub(5); object.display(); return 0; }
三 protected 成員的特點與作用
對建立其所在類對象的模塊來說(水平訪問時), 它與 private 成員的性質相同。
對於其派生類來說(垂直訪問時),它與 public 成 員的性質相同。
既實現了數據隱藏,又方便繼承,實現代碼重用。
比如這樣,訪問就是錯誤的
// 例 protected 成員舉例 class A { protected: int x; } int main( ) { A a; a.X=5; //錯誤 }
正確
class A { protected: int x; } class B: public A{ public: void Function( ); }; void B:Function( ) { x =5; //正確 }
錯誤
class A { private: int x; } class B: public A{ public: void Function( ); }; void B:Function( ) { x =5; //錯誤:不能訪問基類中的私有成員 }
四 派生類的構造 函數和析構函數
1 派生類的構造函數
基類的構造函數不被繼承,需要在派生類 中自行聲明。
聲明構造函數時,只需要對本類中新增成 員進行初始化,對繼承來的基類成員的初 始化由基類完成。
也就是各自的構造函數各自干自己的活
重要 構造函數的調用順序
#include <iostream> using namespace std; class A { public: A( ){cout<<"A Constructor"<<endl;} }; class B:public A { public: B( ){cout<<"B Constructor"<<endl;} }; int main() { B b; reurn 0; }
運行結果為 先A后B
定義派生類對象時,構造函數的調用順序:
首先調用基類的構造函數;
再調用對象成員類的構造函數;
最后調用派生類的構造函數
注意別忘了對象成員
#include <iostream> using namespace std; class component { public: component() {cout << "construct component" << endl;}; ~component() {cout<< "destruct component" << endl;}; }; class base { public: base() {cout << "construct base" << endl;}; ~base() {cout << "destruct base" << endl;}; }; class derived :base { public: derived() {cout << "construct derived" << endl;}; ~derived() {cout << "destruct derived" << endl;}; component c; }; int main() { derived d; return 0; }
運行結果為
construct base
construct component
construct derived
destruct derived
destruct component
destruct base
2 派生類構造函數的構造規則
(1)在基類中定義了默認構造函數,且該默認構造函 數可以完成派生類對象中 基類成員的構造,則派生 類構造函數的定義可以省略對基類構造函數的顯式 調用,而采用隱含調用。
准確的說,默認構造函數就是在調用時不需要顯示地傳入實參的構造函數。
可以不帶形參,也可以有默認形參
隱含調用就是看起來沒有調用基類的構造函數。
#include <iostream> using namespace std; class Base { public: Base() { a=0; } Base( int i) { a=i; } protected: int a; }; class Derived:public Base { public: Derived() { b=0; } Derived( int i) { b=i; } void Print() { cout<<"a="<<a<<",b="<<b<<endl; } private: int b; }; int main( ) { Derived d1; Derived d2(12); d1.Print(); d2.Print(); cin.ignore(); return 0; }
00 和0 12
(2)當基類的構造函數使用一個或多個參數時,或者 基類的默認構造函數不能完成基類成員的構造時, 派生類必須定義構造函數,
顯式調用基類構造函數, 提供將參數傳遞給基類構造函數的途徑。這時,派 生類構造函數的函數體可能為空,僅起到參數傳遞 作用。 這時需要用到“成員初始化列表”
#include <iostream> using namespace std; class A { public: A() { cout << "A Constructor1" << endl; } A(int i) { x1 = i; cout << "A Constructor2" << endl; } void dispa() { cout << "x1=" << x1 << endl; } private: int x1; }; class B :public A { public: B() { cout << "B Constructor1" << endl; } B(int i) :A(i + 10)//沒加這個東西也不會報錯 //定義派生類構造函數時,傳遞參數到基類構造函數 { x2 = i; cout << "B Constructor2" << endl; } void dispb() { dispa(); //調用基類成員函數 cout << "x2=" << x2 << endl; } private: int x2; }; int main() { B b(2); b.dispb();//12 2 B b1; b1.dispb();//亂碼 return 0; }
注意派生類中構造函數的寫法。
//派生類構造函數顯式調用基類構造函數 #include <iostream> #include <cstring> using namespace std; //日期類 class date { private: int year, mon, day; public: date(int y = 2011, int m = 10, int d = 1) { year = y; mon = m; day = d; } void Print() { cout << year << "-" << mon << "-" << day << endl; } }; //學生基類 class Student { protected: int number; char name[20]; char sex; public: Student() { number = 0; strcpy(name, "No name "); sex = 'M'; } Student(int n, char *s, char x) { number = n; strcpy(name, s); sex = x; } }; //大學生派生類 class UGStudent :public Student { public: UGStudent(int n, char *s, char x, int a, int y, int m, int d) :Student(n, s, x), birth(y, m, d) { age = a; } UGStudent() { age = 0; } void Print() { cout << "number:" << number << endl; cout << "name:" << name << endl; cout << "sex:" << sex << endl; cout << "age:" << age << endl; cout << "birthday:"; birth.Print(); } private: int age; date birth; }; int main() { UGStudent st1; UGStudent st2(1001, "Zhang", 'F', 20, 1991, 6, 11); st1.Print(); st2.Print(); cin.ignore(); return 0; } /* number:0 name:No name sex:M age:0 birthday:2011-10-1 number:1001 name:Zhang sex:F age:20 birthday:1991-6-11 */
注意大學生這個派生類中,有birth這個對象成員。
析構函數很簡單。
五 多繼承
class 派生類名:繼承方式1 基類名1,…,繼承方式n 基類名n { 派生類新定義成員 };
多繼承下構造函數調用順序:
(1)先調用所有基類的構造函數,再調用派生類的構造函數。
(2)處於同一層次的各基類構造函數的調用順序取決於定義 派生類時所指定的基類順序,與派生類構造函數中所定義的成 員函數初始化列表順序無關。
// 示例多繼承方式下構造函數和析構函數的調用順序。 #include <iostream> using namespace std; class A //定義基類A { public: A(int i) { a = i; cout << "A Constructor" << endl; } void disp() { cout << "a=" << a << endl; } ~A() { cout << "A Destructor" << endl; } private: int a; }; class B //定義基類B { public: B(int j) { b = j; cout << "B Constructor" << endl; } void disp() { cout << "b=" << b << endl; } ~B() { cout << "B Destructor" << endl; } private: int b; }; class C :public B, public A //定義A和B的派生類C。B在前,A在后 { public: C(int k) :A(k + 2), B(k - 2) //包含基類成員初始化列表 { c = k; cout << "C Constructor" << endl; } void disp() { //用類名加作用域運算符限定調用某個基類的同名成員 A::disp(); B::disp(); cout << "c=" << c << endl; } ~C() { cout << "C Destructor" << endl; } private: int c; }; int main() { C obj(10); //調用類C的成員函數disp obj.disp(); cin.ignore(); return 0; } /* B Constructor A Constructor C Constructor a=12 b=8 c=10 C Destructor A Destructor B Destructor */
多繼承且有內嵌對象時的構造函數
1.調用基類構造函數,調用順序按照它們 被繼承時聲明的順序(從左向右)。
2.調用成員對象的構造函數,調用順序按 照它們在類中聲明的順序。
3.派生類的構造函數體中的內容。
// 例 派生類構造函數舉例(多繼承,含有內嵌對象) 。 #include <iostream> using namespace std; class B1 //基類B1,構造函數有參數 { public: B1(int i) { cout << "constructing B1 " << i << endl; } }; class B2 //基類B2,構造函數有參數 { public: B2(int j) { cout << "constructing B2 " << j << endl; } }; class B3 //基類B3,構造函數無參數 { public: B3(){ cout << "constructing B3 *" << endl; } }; class C : public B2, public B1, public B3 { public: //派生類的公有成員 //注意基類名的個數與順序 //注意成員對象名的個數與順序 C(int a, int b, int c, int d) : B1(a), memberB2(d), memberB1(c), B2(b) { } private: //派生類的私有對象成員 B1 memberB1; B2 memberB2; B3 memberB3; }; int main() { C obj(1,2,3,4); cin.ignore(); return 0; } /* constructing B2 2 constructing B1 1 constructing B3 * constructing B1 3 constructing B2 4 constructing B3 * */
B1 memberB1;
B3 memberB3;
B2 memberB2;
最下面三個也是這個順序了
再來一個順序的例子
// 例題5.6,多繼承范例 #include <iostream> #include <cstring> using namespace std; //定義研究生基類 class GStudent { protected: int number; char name[20]; char sex; public: GStudent(int n, char *s, char x) { number = n; strcpy(name, s); sex = x; cout << "Construct GStudent. " << endl; } ~GStudent() { cout << "Destruct GStudent. " << endl; } }; //定義職員基類 class Emplyee { protected: char ename[20]; char jobname[20]; public: Emplyee(char *sn, char *sj) { strcpy(ename, sn); strcpy(jobname, sj); cout << "Construct Employee. " << endl; } ~Emplyee() { cout << "Destruct Employee. " << endl; } }; //定義在職研究生類,從兩個基類派生 class GStudentHasJob :public GStudent, public Emplyee { public: GStudentHasJob(int n, char *s, char x, char *sj) : GStudent(n, s, x), Emplyee(s, sj) { cout << "Construct GStudentHasJob. " << endl; } ~GStudentHasJob() { cout << "Destruct GStudentHasJob. " << endl; } void Print() { cout << "number:" << number << endl; cout << "name:" << name << endl; cout << "sex:" << sex << endl; cout << "job:" << jobname << endl; } }; int main() { GStudentHasJob st(1001, "zhang", 'F', "teacher"); st.Print(); cin.ignore(); return 0; } /* Construct GStudent. Construct Employee. Construct GStudentHasJob. number:1001 name:zhang sex:F job:teacher Destruct GStudentHasJob. Destruct Employee. Destruct GStudent. */
六 基類成員訪問 和賦值兼容性
1 基類成員名的限定訪問 和同名覆蓋
在多繼承中出現?
當派生類的多個基類存在同名成員時: 則派生類對些同名成員的訪問就可能存在沖 突
解決辦法:顯式調用要訪問的成員,成員名 限定訪問法 基類名::成員名
2 同名覆蓋原則
當派生類與基類中有相同成員時: 若未強行指名,則通過派生類對象使用的 是派生類中的同名成員。
如要通過派生類對象訪問基類中被覆蓋的 同名成員,應使用基類名限定//同上
// 多繼承同名覆蓋舉例 #include <iostream> using namespace std; class B1 //聲明基類B1 { public: //外部接口 int nV; void fun(){ cout << "Member of B1" << endl; } }; class B2 //聲明基類B2 { public: //外部接口 int nV; void fun(){ cout << "Member of B2" << endl; } }; class D1 : public B1, public B2 //定義派生類 { public: int nV; //同名數據成員 void fun(){ cout << "Member of D1" << endl; } //同名函數成員 }; int main() { D1 d1; d1.nV = 1; //對象名.成員名標識, 訪問D1類成員 d1.fun(); d1.B1::nV = 2; //作用域分辨符標識, 訪問基類B1成員 d1.B1::fun(); d1.B2::nV = 3; //作用域分辨符標識, 訪問基類B2成員 d1.B2::fun(); return 0; } /* Member of D1 Member of B1 Member of B2 */
3 二義性問題
同名覆蓋,無聲明就二義性
在多繼承時,基類與派生類之間,或基類之間出現同 名成員時,將出現訪問時的二義性(不確定性)—— 采用虛函數或支配(同名覆蓋)原則來解決。
當派生類從多個基類派生,而這些基類又從同一個基 類派生,則在訪問此共同基類中的成員時,將產生二 義性——采用虛基類來解決。
// 二義性問題舉例 class A { public: void f( ); }; class B { public: void f( ); void g( ); }; class C: public A, piblic B { public: void g( ); void h( ); }; 如果聲明:C c1; 則 c1.f( ); 具有二義性 而 c1.g( ); 無二義性(同名覆 蓋)
解決方法一:用類名來限定 c1.A::f( ) 或 c1.B::f( )
解決方法二:同名覆蓋 在類C 中聲明一個同名成員函數f( ),f( ) 再根據需要調用 A::f( ) 或 B::f( )
再來舉一個例子
這個例子還是比較特別的
class B { public: int b; } class B1 : public B { private: int b1; } class B2 : public B { private: int b2; }; class C : public B1,public B2 { public: int f( ); private: int d; }
下面的訪問是二義性的: C c;
c.b c.B::b
下面是正確的: c.B1::b
c.B2::b
4 賦值兼容規則
派生類對象可以看作是基類對象:派生類包含了基類的 全部成員;
基類對象不能被看作是派生類對象:派生類新增了成員
基類對象可以賦值給基類對象,也可以把派生類對象 賦值給基類對象
基類指針可以指向基類對象,也可以指向派生類對象
基類引用可以指向基類對象,也可以指向派生類對象
一個公有派生類的對象在使用上可以被當作基類的 對象,反之則禁止。
具體表現在:
派生類的對象可以被賦值給基類對象。
派生類的對象可以初始化基類的引用。
指向基類的指針也可以指向派生類。
疑問:為什么是公有派生類? 因為公有派生類和基類之間可以使用相同的函數,而 沒有必要為每一個類設計單獨的模塊
#include <iostream> using namespace std; class B0 //基類B0聲明 { public: void display() { cout << "B0::display( )" << endl; } //公有成員函數 }; class B1 : public B0 { public: void display() { cout << "B1::display( )" << endl; } }; class D1 : public B1 { public: void display() { cout << "D1::display( )" << endl; } }; void fun(B0 *ptr) { ptr->display(); } //"對象指針->成員名" int main() //主函數 {
B0 b0; //聲明B0類對象
B1 b1; //聲明B1類對象
D1 d1; //聲明D1類對象
B0 *p; //聲明B0類指針
p = &b0; //B0類指針指向B0類對象
fun(p);
p = &b1; //B0類指針指向B1類對象
p->display();
cout << 1 << endl;
fun(p);
p = &d1; //B0類指針指向D1類對象
p->display();
cout << 1 << endl;
fun(p);
return 0;
} /*
B0::display( )
B0::display( )
1
B0::display( )
B0::display( )
1
B0::display( )
*/
同樣的例子
// 例題5.10,賦值兼容實例 #include <iostream> using namespace std; class Base { protected: int member; public: Base() { member = 0; } void Show() { cout << "Base::Show():" << member << endl; } }; class Derived1 :public Base { public: Derived1(int a) { member = a; } void Show() { cout << "Derived1::Show():" << member << endl; } }; class Derived2 :public Derived1 { public: Derived2(int a) :Derived1(a) { } void Show() { cout << "Derived2::Show():" << member << endl; } }; void Test(Base *pb) { pb->Show(); } void Test(Base &br) { br.Show(); } int main() { Base b0; Derived1 d1(5); Derived2 d2(10); Base *pb0; pb0 = &b0; Test(pb0); b0 = d1; Test(pb0); pb0 = &d1; Test(pb0); Test(d2); cin.ignore(); return 0; } /* Base::Show():0 Base::Show():5 Base::Show():5 Base::Show():10 */
七 虛基類
一張圖說明多繼承的冗余
虛基類的定義
class 派生類名:virtual 繼承方式 共同基類名
在派生類的對象中,這些同名成員在內存中 同時擁有多個副本,如果將直接基類的共同 基類設置為虛基類,
那么從不同的路徑繼承 過來的該類成員(包括數據成員和函數成員) 在內存中只擁有一個副本(數據成員只
有一 個副本,函數成員只有一個映射),從而解 決了同名成員的唯一標識問題
其實就是上面的6-3.二義性問題的最后一個例子
引入虛基類之后就這樣了:
代碼示例
// 示例虛基類。 #include <iostream> using namespace std; class A { public: A() { a = 10; } protected: int a; }; class A1 :virtual public A //定義虛基類 { public: A1() { cout << a << endl; } }; class A2 :virtual public A //定義虛基類 { public: A2() { cout << a << endl; } }; class B :A1, A2 //私有繼承 { public: B() { cout << a << endl; } }; int main() { B obj; return 0; } /* 10 10 10*/
虛基類的初始化
構造函數的調用順序
先調用虛基類的構造函數,再調用非虛基類的 構造函數。
若同一層次中包含多個虛基類,其調用順序按定 義時順序。
若虛基類由非虛基類派生而來,則仍按先調用基 類構造函數,再調用派生類構造函數的順序
代碼示例
// 示例引入虛基類后構造函數的調用順序。 #include <iostream> using namespace std; class Base1 { public: Base1() { cout << "class Base1" << endl; } }; class Base2 { public: Base2() { cout << "class Base2" << endl; } }; class Level1 :public Base2, virtual public Base1 //定義虛基類 { public: Level1() { cout << "class Level1" << endl; } }; class Level2 :public Base2, virtual public Base1 { public: Level2() { cout << "class Level2" << endl; } }; class TopLevel :public Level1, virtual public Level2 { public: TopLevel() { cout << "class TopLevel" << endl; } }; int main() { TopLevel obj; return 0; } /* class Base1 class Base2 class Level2 class Base2 class Level1 class TopLevel */
這里就是先調用Toplevel的構造函數,先虛基類level2,再level1
level2中先虛基類base1,輸出class base1,再base2構造函數,輸出class base2
然后level2的本身的calss level2
level1中(先虛基類base1,輸出class base1) 錯錯錯,沒了,虛基類只有一次,再base2,輸出class base2
然后level1的本身的class level1
最后本身的構造函數
換個角度講,虛派生只影響從指定了虛基類的派生類中進一步派生出來的類,它不會影響派生類本身。
如果加上
Level1 a;
Level2 b;
結果會是
class Base1
class Base2
class Level1
class Base1
class Base2
class Level2
所以說,虛基類就是保證在多繼承的類似的菱形路線上,后代的后代的身體(類的內存結構上),一個虛基類只會最多生成一次
emmm,感覺就是這樣的,
注意:定義虛基類的構造函數注意:
(1)一般情況下虛基類只允許定義不帶參數的或帶 缺省參數的構造函數
(2)如果多繼承不牽扯到對同一基類的派生,就沒 必要定義虛基類。
虛基類的初始化與一般多繼承的初始化規則如下:
1.所有從虛基類直接或間接派生的類,都必須在該類 構造函數的成員初始化列表中列出對虛基類構造函數 的調用。
但是只有實際構造對象的類的構造函數才會 引發對虛基類構造函數的調用,而其他基類在成員初 始化列表中對
虛基類構造函數的調用都會被忽略,這 樣將確保派生類對象中虛基類成員只被初始化一次。
2.如果某類構造函數的成員初始化列表中同時列出對 虛基類構造函數和非虛基類構造函數的調用,則會優 先執行
虛基類構造函數。
// 示例虛基類的應用。 #include <iostream> #include <string.h> using namespace std; class Data_rec //定義基類Data_rec { public: Data_rec() { name = NULL; } ~Data_rec() { delete[] name; } void insert_name(char* pname) { delete[] name; name = new char[strlen(pname) + 1]; strcpy(name, pname); } void print() { cout << "Name:" << name << endl; } private: char* name; }; class Student :virtual public Data_rec //定義虛基類 { public: Student() :Data_rec() { id = NULL; } ~Student() { delete[] id; } void insert_id(char* pid) { delete[] id; id = new char[strlen(pid) + 1]; strcpy(id, pid); } void print() { Data_rec::print(); //訪問基類的成員函數 cout << "Id:" << id << endl; } private: char* id; }; class Teacher :virtual public Data_rec //定義虛基類 { public: Teacher() :Data_rec() { sal = 0; } void insert_sal(float psal) { sal = psal; } void print() { Data_rec::print(); //訪問基類的成員函數 cout << "Sal:" << sal << endl; } private: float sal; }; class Postgrad :public Student //定義派生類Postgrad { public: Postgrad() :Student() { dn = NULL; } void insert_dn(char* p) { delete[] dn; dn = new char[strlen(p) + 1]; strcpy(dn, p); } void print() { Student::print(); //訪問基類的成員函數 cout << "Dept Name:" << dn << endl; } private: char* dn; }; class Tpost :public Teacher, public Postgrad //定義多繼承派生類Tpost { public: Tpost() :Teacher(), Postgrad() {} void print() { Teacher::print(); Postgrad::print(); } }; int main() { Teacher tobj; Tpost tpobj; tobj.insert_name("Li Min"); tobj.insert_sal(2000); tpobj.insert_name("Zhang Hua"); tpobj.insert_sal(1500); tpobj.insert_id("03410101"); tpobj.insert_dn("Computer"); tobj.print(); tpobj.print(); cin.ignore(); return 0; } /* Name:Li Min Sal:2000 Name:Zhang Hua Sal:1500 Name:Zhang Hua Id:03410101 Dept Name:Computer */
這個例子,,沒看
虛基類的一個重要例子還沒有理解
#include <iostream> using namespace std; class transportation { public: transportation(int i) { cout<<"transportation "<<i<<" born"<<endl; } }; class car:virtual public transportation { public: car(int i):transportation(i) { cout<<"car "<<i<<" born"<<endl; } }; class plane:virtual public transportation { public: plane(int i):transportation(i) { cout<<"plane "<<i<<" born"<<endl; } }; class carplane:public car,public plane { public: carplane(int i ):car(i+2),plane(i+3) { cout<<"carplane "<<i<<" born"<<endl; } }; int main() { carplane t(0); return 0; } /*程序輸出 transportation 1 born car 2 born plane 3 born carplane 0 born */ /*如果沒有使用虛繼承的方式,則程序輸入為: transportation 2 born car 2 born transportation 3 born plane 3 born carplane 0 born */
感覺只是我單純地忘記了派生類構造函數執行之前,必定先執行基類構造函數。