天天看點

c++中繼承知識點詳解

    • 繼承基本知識
      • 定義:
      • 繼承關系:
      • 繼承圖例解釋:
      • 繼承與轉換–指派相容規則–public繼承
      • 繼承體系中的作用域
      • 派生類的預設成員函數
    • 繼承方式(單繼承,多繼承,菱形繼承)
      • 1.單繼承
        • 定義:一個子類隻有一個直接父類時稱這個繼承關系為單繼承
        • 代碼示例:
      • 2.多繼承
        • 定義:一個子類有兩個或以上直接父類時稱這個繼承關系為多繼承
        • 代碼示例:
      • 3.菱形繼承
        • 代碼示例:
        • 虛繼承:解決菱形繼承的二義性和資料備援的問題

繼承基本知識

定義:

 繼承是面向對複用的重要手段。通過繼承定義一個類,繼承是類型之間的關系模組化,共享公有的東西,實作各自本質不同的東西。

繼承關系:

 三種繼承關系下基類成員的在派生類的通路關系變化(圖)

c++中繼承知識點詳解

 舉個栗子(公有繼承)

class Person
{
public :
    Person(const string& name)
    : _name(name )
    {}
    void Display ()
    {
        cout<<_name <<endl;
    }
protected :
    string _name ; // 姓名
    string _sex ;
};

class Student : public Person //公有繼承
{
protected :
    int _num ; // 學号
};
           

繼承圖例解釋:

c++中繼承知識點詳解

私有繼承和保護繼承很少用到,我們重點要掌握公有繼承

繼承與轉換–指派相容規則–public繼承

  1. 子類對象可以指派給父類對象(切割/切片)
  2. 父類對象不能指派給子類對象
  3. 父類的指針/引用可以指向子類對象
  4. 子類的指針/引用不能指向父類對象(可以通過強制類型轉換完成)
class Person
{
public:
    void Display()
    {
        cout << "AA" << endl;
    }
protected:
    string _name; // 姓名
};
class Student : public Person
{
public:
    int _num; // 學号
};

int main()
{
    Person a;
    Student b;
    a = b; //子類對象指派給基類對象(切片)這個特性是編譯器支援的
    b = a; //父類對象不能指派給子類對象

    Person *p1 = &b; //特性3
    //Person &a1 = b; //特性3
    Student *p2 = (Student*)&a; //特性4
    Student& b1 = (Student&)a; //特性4
    getchar();
    return ;
}
           

繼承體系中的作用域

  1. 在繼承體系中基類和派生類都有獨立的作用域。
  2. 子類和父類中有同名成員(成員函數,成員變量)子類成員将屏蔽父類對成員的直接通路。(在子類成員函數中,可以使用 基類::基類成員 通路)–隐藏(重定義)
  3. ==注意在實際中在繼承體系裡面最好不要定義同名的成員==。
class Person
{
public:
    Person(const char *name = "",int num = )
        :_name(name)
        ,_num(num)
    {}

protected:
    string _name; // 姓名
    int _num;
};
class Student : public Person
{
public:
    Student(const char* name = "", const int num1 = , int num2 = )
        :Person(name,num1)
        ,_num(num2)
    {}
    void Display()
    {
        cout << _num << endl;
        cout <<Person:: _num << endl;//必須顯示指出基類作用域才能列印基類成員
    }
protected:
    int _num; // 學号
};

int main()
{
    Person a("boday",);
    Student b("crash",,);
    b.Display();

    return ;
}
           

運作結果:

c++中繼承知識點詳解

可以很明顯看出此時列印的是子類的成員,而隐藏掉了父類的成員,(

這就是隐藏

派生類的預設成員函數

 在繼承關系裡面,在派生類中如果沒有顯示定義這六個成員函數,編譯系統則會預設合成這六個預設的成員函數。

c++中繼承知識點詳解

來個栗子說說預設成員函數的前四個(

後兩個不常用

class Person
{
public:
    Person(const char *name = "",int num = ) //父類構造函數
        :_name(name)
        ,_num(num)
    {}
    ~Person()//父類析構函數
    {
        cout << "~Person()" << endl;
    }
    Person(const Person& p)//父類拷貝構造函數
        :_name(p._name)
        ,_num(p._num)
    {}
    Person& operator=(const Person& p)//父類指派運算符重載
    {
        if (this != &p)
        {
            _name = p._name;
            _num = p._num;
        }
        return *this;
    }

protected:
    string _name; // 姓名
    int _num;
};
class Student : public Person
{
public:
    Student(const char* name = "", const int num1 = , int num2 = )//子類構造函數
        :Person(name,num1)   
        ,_num(num2)
    {}
    ~Student()//子類析構函數
    {
        cout << "~Student()" << endl;
    }
    Student(const Student& s)//子類拷貝構造函數
        :Person(s)
        ,_num(s._num)
    {}
    Student& operator=(const Student& s)//子類指派運算符重載
    {
        Person::operator=(s); //顯示調用父類指派運算符重載
        _num = s._num;
    }
protected:
    int _num; // 學号
};
           

先調用父類構造函數,在調用基類構造函數;析構函數調用順序與構造函數相反(先構造後析構,這個和棧的規則有關(先入後出))

繼承方式(單繼承,多繼承,菱形繼承)

c++中繼承知識點詳解

1.單繼承

定義:一個子類隻有一個直接父類時稱這個繼承關系為單繼承

代碼示例:

class A
{
protected:
    int _a;
};

class B : public A //B類 繼承 A類
{
protected:
    int _b;
};
           

2.多繼承

定義:一個子類有兩個或以上

直接父類

時稱這個繼承關系為多繼承

代碼示例:

class A
{
protected:
    int _a;
};

class B 
{
protected:
    int _b;
};

class C : public A,B
{
protected:
    int _c;
};
           

3.菱形繼承

代碼示例:

class Person
{
public:
    string _name; // 姓名
};
class Student : public Person
{
protected:
    int _num; //學号
};
class Teacher : public Person
{
protected:
    int _id; // 職工編号
};
class Assistant : public Student, public Teacher
{
protected:
    string _majorCourse; // 主修課程
};
void Test()
{
    // 顯示指定通路哪個父類的成員(二義性問題)
    Assistant a;
    a.Student::_name = "xxx";
    a.Teacher::_name = "yyy";//資料備援問題
}
           

很明顯菱形繼承存在問題,存在二義性和資料備援的問題。為了解決這個問題就引入了虛繼承。

虛繼承:解決菱形繼承的二義性和資料備援的問題

在聲明派生類時,指定其繼承方式時聲明為虛繼承的方式。如

class A
{
public:
    int _a;
};

class B : virtual public A //聲明為虛基類
{
protected:
    int _b;
};

class C : virtual public A //聲明為虛基類
{
protected:
    int _c;
};

class D : public B,public C
{
protected:
    int _d;
};
           

看看測試效果:

void Test()
{
    D d;
    d._a = ;
}
           
c++中繼承知識點詳解

是不是很疑惑到底是如何解決的?那就要深入到底層探索下

c++中繼承知識點詳解

這裡在虛繼承時用一個虛基表存放偏移量,這樣B和C類同時使用一個虛基表存放A相對于B和C的偏移量,當發生虛繼承時A會存放在一個公共區域,這就很好的解決了二義性問題,同時也節省了空間。

當基類通過多條派生路徑被一個派生類繼承時,該派生類隻繼承該基類一次,也就是說,基類成員隻保留一次

為了保證虛基類在派生類中隻繼承一次,應當在該基類的所有直接派生類中聲明為虛基類,否則仍然會出現對基類的多次繼承

虛繼承很好的解決了菱形繼承帶來的問題。

這裡建議大家寫下代碼調試一下,同時檢視記憶體變化。

繼續閱讀