C++類繼承和派生


學習鏈表繼承

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
*/

 

 感覺只是我單純地忘記了派生類構造函數執行之前,必定先執行基類構造函數。

 


免責聲明!

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



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