天天看點

【C++筆記】繼承

繼承是面向對象的三大特性之一

定義類時,下級别的成員除了擁有上一級的共性,還有自己的特性

6.1 繼承的基本文法

作用:減少代碼重複量

文法:

class 子類 : 繼承方式 父類

例:

class MyPage : public BasePage

子類也稱為派生類、父類也稱為基類

派生類中的成員,包含兩大部分:一類是從基類繼承過來的,一類是自己增加的成員。從基類繼承過來的表現其共性,新增的表現其個性

6.2 繼承方式

繼承方式一共有三種:

  • 公共繼承
    • 父類中的公共權限成員,子類中也是公共權限
    • 父類中的保護權限成員,子類中也是保護權限
    • 父類的私有權限成員,子類無法通路
  • 保護繼承
    • 父類中的公共權限成員,子類中也是保護權限
    • 父類中的保護權限成員,子類中也是保護權限
    • 父類的私有權限成員,子類無法通路
  • 私有繼承
    • 父類中的公共權限成員,子類中是私有權限
    • 父類中的保護權限成員,子類中是私有權限
    • 父類的私有權限成員,子類無法通路
      【C++筆記】繼承

6.3 繼承中的對象模型

父類中所有非靜态成員屬性都會被子類繼承下去。父類中私有成員屬性,是被編譯器隐藏了,是以是通路不到,但是确實被繼承下去了。

//例:
class Base{
public:
	int m_A;
protected:
	int m_B;
private:
	int m_C;
};

class Son : public Base{
public:
	int m_D;
};

void test(){
	cout << "size of Son = " << sizeof(Son) << endl;
    //輸出結果:16
}
           

6.4 繼承中構造和析構順序

子類繼承父類後,建立子類對象,也會調用父類的構造函數

繼承中的構造和析構順序如下:

  • 構造:先構造父類,在構造子類
  • 析構:順序與構造相反
//例:
class Base{
public:
	Base(){
		cout << "Base的構造函數" << endl;
	}

	~Base(){
		cout << "Base的析構函數" << endl;
	}
};

class Son : public Base{
public:
	Son(){
		cout << "Son的構造函數" << endl;
	}

	~Son(){
		cout << "Son的析構函數" << endl;
	}
};

void test(){
	Son s;
}
------------
//輸出
Base的構造函數
Son的構造函數
Son的析構函數
Base的析構函數
           

6.5 繼承同名成員處理方式

  • 通路子類同名成員,直接通路即可
  • 通路父類同名成員,需要加作用域

例:

s.Base::m_A

s.Base::func()

//測試案例
#include <iostream>
using namespace std;

class Base{
public:
	Base(){
		m_A = 100;
	}

	void func(){
		cout << "Base func 函數調用" << endl;
	}

	int m_A;

};

class Son : public Base{
public:
	Son(){
		m_A = 200;
	}

	void func(){
		cout << "Son func 函數調用" << endl;
	}

	int m_A;
};

void test01(){
	Son s;
	cout << "Son : m_A = " << s.m_A << endl;
	cout << "Base : m_A = " << s.Base::m_A << endl;
}

void test02(){
	Son s;
	s.func();
	s.Base::func();
}

int main(){

	test02();

	system("pause");
	return 0;
}
           

注意事項:

如果子類中出現和父類同名的成員函數,子類中的同名成員會隐藏掉父類中所有同名成員函數(包括重載)。如果先要通路父類同名成員,需要加作用域

6.6 繼承同名靜态成員處理方式

靜态成員和非靜态成員出現同名,處理方式一緻

  • 通路子類同名成員,直接通路即可
  • 通路父類同名成員,需要加作用域
#include <iostream>
using namespace std;

class Base{
public:
	static int m_A;
	static void func(){
		cout << "Base - static void func" << endl;
	}
};
int Base::m_A = 100;

class Son : public Base{
public:
	static int m_A;
	static void func(){
		cout << "Son - static void func" << endl;
	}
};
int Son::m_A = 200;

void test01(){
	//通過對象通路
	Son s;
	cout << "Son - m_A = " << s.m_A << endl;
	cout << "Base - m_A =  " << s.Base::m_A << endl;
	//通過類名通路
	cout << "Son - m_A = " << Son::m_A << endl;
	//第一個::代表通過類名方式通路	第二個::代表通路父類作用域下的成員
	cout << "Base - m_A = " << Son::Base::m_A << endl;
}

void test02(){
	//通過對象通路
	Son s;
	s.func();
	s.Base::func();
	//通過類名通路
	Son::func();
	Son::Base::func();
}

int main(){

	test02();

	system("Pause");
	return 0;
}
           

6.7 多繼承文法

C++允許一個類繼承多個類

文法:

class 子類 : 繼承方式 父類1, 繼承方式 父類2...

//例:
class Son : public Base1, public Base2{
           

如果多繼承中父類出現了同名情況,子類使用時要加作用域

//例:
cout << "Base1 - m_A = " << s.Base1::m_A << endl;
cout << "Base2 - m_A = " << s.Base2::m_A << endl;
           

6.8 菱形繼承

概念:

  1. 兩個派生類繼承同一個類
  2. 又有某個類同時繼承這兩個類
  3. 這種繼承稱為菱形繼承

菱形繼承的問題:

  1. Son1繼承了Base的資料,Son2也繼承了Base的資料,當GrandSon使用資料時,就會産生二義性
    解決方法:兩個父類因為相同的資料,需要加以作用域區分
    g.Son1::m_A = 100
               
  2. GrandSon繼承來自Base的資料有兩份,但實際上有一份就可以

    利用虛繼承,解決菱形繼承資料重複的問題。發生虛繼承之後,資料隻有一個,且可以不加作用域直接通路。

    在繼承前加上關鍵字virtual。此時的Base稱為虛基類。

    class Son1 : virtual public Base
               

參考:黑馬程式員匠心之作|C++教程從0到1入門程式設計,學習程式設計不再難

連結:https://www.bilibili.com/video/BV1et411b73Z

繼續閱讀