天天看點

C++核心程式設計---4.6 類和對象-繼承【P127~P134】4.6 繼承

C++核心程式設計---4.6 類和對象-繼承【P127~P134】

  • 4.6 繼承
    • 4.6.1 繼承的基本文法
    • 4.6.2 繼承方式
    • 4.6.3 繼承中的對象模型
    • 4.6.4 繼承中構造和析構順序
    • 4.6.5 繼承同名成員處理方式
    • 4.6.6 繼承同名靜态成員處理方式
    • 4.6.7 多繼承文法
    • 4.6.8 菱形繼承

4.6 繼承

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

有些類與類之間存在特殊的關系,例如下圖中:

C++核心程式設計---4.6 類和對象-繼承【P127~P134】4.6 繼承

會發現,定義這些類時,下級别的成員除了擁有上一級别的共性,還有自己的特性。

這個時候我們就可以考慮利用繼承的技術,減少重複代碼。

4.6.1 繼承的基本文法

例如我們看到很多的網站中,都有公共的頭部公共的底部,甚至公共的左側清單,隻有中心内容不同,接下來我們分别利用普通寫法和繼承的寫法來實作網頁中的内容,看一下繼承存在的意義以及好處。

文法: class 子類 : 繼承方式 父類

子類 也稱為 派生類

父類 也稱為 基類

派生類中的成員,包含兩大部分:

一類是從基類繼承過來的,一類是自己增加的成員。

從基類繼承過來的表現其共性,而新增的成員表現其個性。

普通實作頁面:

#include<iostream>
#include<string>
using namespace std;

//普通實作頁面

//Java 頁面
class Java
{
public:
	void header()
	{
		cout << "首頁、公開課、登入、注冊...(公共頭部)" << endl;
	}
	void footer()
	{
		cout << "幫助中心、交流合作、站内地圖...(公共底部)" << endl;
	}
	void left()
	{
		cout << "Java、Python、C++...(公共分類清單)" << endl;
	}
	void content()
	{
		cout << "Java 學科視訊" << endl;
	}
};

//Python 頁面
class Python
{
public:
	void header()
	{
		cout << "首頁、公開課、登入、注冊...(公共頭部)" << endl;
	}
	void footer()
	{
		cout << "幫助中心、交流合作、站内地圖...(公共底部)" << endl;
	}
	void left()
	{
		cout << "Java、Python、C++...(公共分類清單)" << endl;
	}
	void content()
	{
		cout << "Python 學科視訊" << endl;
	}
};

//C++ 頁面
class Cpp
{
public:
	void header()
	{
		cout << "首頁、公開課、登入、注冊...(公共頭部)" << endl;
	}
	void footer()
	{
		cout << "幫助中心、交流合作、站内地圖...(公共底部)" << endl;
	}
	void left()
	{
		cout << "Java、Python、C++...(公共分類清單)" << endl;
	}
	void content()
	{
		cout << "C++ 學科視訊" << endl;
	}
};

void test01()
{
	cout << "Java 下載下傳視訊頁面如下:" << endl;
	Java ja;
	ja.header();
	ja.footer();
	ja.left();
	ja.content();

	cout << "--------------------------" << endl;
	cout << "Python 下載下傳視訊頁面如下:" << endl;
	Python py;
	py.header();
	py.footer();
	py.left();
	py.content();

	cout << "--------------------------" << endl;
	cout << "Cpp 下載下傳視訊頁面如下:" << endl;
	Cpp cp;
	cp.header();
	cp.footer();
	cp.left();
	cp.content();
}

int main()
{
	test01();

	system("pause");
	return 0;
}
           

繼承實作頁面:

#include<iostream>
#include<string>
using namespace std;

//繼承實作頁面

class BasePage
{
public:
	void header()
	{
		cout << "首頁、公開課、登入、注冊...(公共頭部)" << endl;
	}
	void footer()
	{
		cout << "幫助中心、交流合作、站内地圖...(公共底部)" << endl;
	}
	void left()
	{
		cout << "Java、Python、C++...(公共分類清單)" << endl;
	}
};

//Java頁面
class Java : public BasePage
{
public:
	void content()
	{
		cout << "Java 學科視訊" << endl;
	}
};
//Python頁面
class Python : public BasePage
{
public:
	void content()
	{
		cout << "Python 學科視訊" << endl;
	}
};
//C++頁面
class Cpp : public BasePage
{
public:
	void content()
	{
		cout << "C++ 學科視訊" << endl;
	}
};

void test01()
{
	cout << "Java 下載下傳視訊頁面如下:" << endl;
	Java ja;
	ja.header();
	ja.footer();
	ja.left();
	ja.content();

	cout << "--------------------------" << endl;
	cout << "Python 下載下傳視訊頁面如下:" << endl;
	Python py;
	py.header();
	py.footer();
	py.left();
	py.content();

	cout << "--------------------------" << endl;
	cout << "Cpp 下載下傳視訊頁面如下:" << endl;
	Cpp cp;
	cp.header();
	cp.footer();
	cp.left();
	cp.content();
}

int main()
{
	test01();

	system("pause");
	return 0;
}
           
C++核心程式設計---4.6 類和對象-繼承【P127~P134】4.6 繼承

4.6.2 繼承方式

繼承的文法: class 子類 : 繼承方式 父類

繼承方式一共有三種:

  • 公共繼承
  • 保護繼承
  • 私有繼承
    C++核心程式設計---4.6 類和對象-繼承【P127~P134】4.6 繼承

    第一點:父類中的 private 權限,任何子類均不可以繼承;

    第二點:共有繼承—權限不變

    第三點:保護繼承—父類中的 public 和 protected 均變化為子類中的 protected;

    第四點:私有繼承—父類中的 public 和 protected 均變化為子類中的 private;

#include<iostream>
#include<string>
using namespace std;

//公共繼承
class Base1
{
public:
	int m_A;
protected:
	int m_B;
private:
	int m_C;
};

class Son1 : public Base1
{
public:
	void func()
	{
		m_A = 10;//父類中的公共權限成員  到子類中依然是公共權限
		m_B = 10;//父類中的保護權限成員  到子類中依然是保護權限
		//m_C = 10;//報錯!!父類中的私有權限成員  子類通路不到
	}
};

void test01()
{
	Son1 s1;
	s1.m_A = 100;
	//s1.m_B = 100;//報錯!!到Son1中  m_B是保護權限  類外通路不到
}

int main()
{
	test01();

	system("pause");
	return 0;
}
           

4.6.3 繼承中的對象模型

問題: 從父類繼承過來的成員,哪些屬于子類對象中?

#include<iostream>
#include<string>
using namespace std;

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

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

void test01()
{
	cout << "size of Son = " << sizeof(Son) << endl;
}

int main()
{
	test01();
	//結果是16,父類中所有非靜态成員屬性都會被子類繼承下去,父類中的私有成員屬性是被編譯器隐藏了
	//是以是通路不到,但是确實是被繼承下去了

	system("pause");
	return 0;
}
           

利用開發人員指令提示工具檢視對象模型

Step1: 打開 VS 2017的開發人員指令提示符 視窗

**********************************************************************
** Visual Studio 2017 Developer Command Prompt v15.9.31
** Copyright (c) 2017 Microsoft Corporation
**********************************************************************

D:\software\Visual Studio 2017>
           

Step2: cd 到工程檔案路徑下

D:\software\Visual Studio 2017>cd D:\document\vs2017_code\core\004-6 類和對象-繼承\004-6 類和對象-繼承

D:\document\vs2017_code\core\004-6 類和對象-繼承\004-6 類和對象-繼承>
           

Step3: 檢視對象模型 格式:cl /d1 reportSingleClassLayout類名 "檔案名"

D:\document\vs2017_code\core\004-6 類和對象-繼承\004-6 類和對象-繼承>cl /d1 reportSingleClassLayoutSon "03 繼承中的對象 模型.cpp"
用于 x86 的 Microsoft (R) C/C++ 優化編譯器 19.16.27045 版
版權所有(C) Microsoft Corporation。保留所有權利。

03 繼承中的對象模型.cpp
D:\software\Visual Studio 2017\VC\Tools\MSVC\14.16.27023\include\xlocale(319): warning C4530: 使用了 C++ 異常處理程式, 但未啟用展開語義。請指定 /EHsc

class Son       size(16):
        +---
 0      | +--- (base class Base)
 0      | | m_A
 4      | | m_B
 8      | | m_C
        | +---
12      | m_D
        +---
Microsoft (R) Incremental Linker Version 14.16.27045.0
Copyright (C) Microsoft Corporation.  All rights reserved.

"/out:03 繼承中的對象模型.exe"
"03 繼承中的對象模型.obj"
           

4.6.4 繼承中構造和析構順序

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

問題:父類和子類的構造和析構順序是誰先誰後??

#include<iostream>
#include<string>
using namespace std;

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 test01()
{
	Son s;
}

int main()
{
	test01();

	system("pause");
	return 0;
}
           
C++核心程式設計---4.6 類和對象-繼承【P127~P134】4.6 繼承

4.6.5 繼承同名成員處理方式

問題: 當子類與父類出現同名的成員,如何通過子類對象,通路到子類或父類中同名的資料呢?

  • 通路子類同名成員 直接通路即可
  • 通路父類同名成員 需要加作用域
#include<iostream>
#include<string>
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;
}
           

總結:

  • 子類對象可以直接通路到子類中同名成員
  • 子類對象加作用域可以反複問到父類同名成員
  • 當子類與父類擁有同名的成員函數,子類會隐藏父類中同名成員函數,加作用域可以通路到父類中同名函數。

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

問題: 繼承中同名的靜态成員在子類對象上如何進行通路?

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

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

class Base
{
public:
	static int m_A;

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

class Son : public Base
{
public:
	static int m_A;

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

void test01()
{
	cout << "通過對象通路:" << endl;
	Son s;
	cout << "m_A = " << s.m_A << endl;
	cout << "m_A = " << s.Base::m_A << endl;

	cout << "通過類名通路:" << 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()
{
	//test01();
	test02();

	system("pause");
	return 0;
}
           

4.6.7 多繼承文法

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

文法:class 子類 : 繼承方式 父類1 , 繼承方式 父類2 …

多繼承問題可能會導緻父類中有同名成員出現,需要加作用域區分

C++ 實際開發過程中不建議使用多繼承

#include<iostream>
#include<string>
using namespace std;

class Base1
{
public:
	Base1()
	{
		m_A = 100;
	}
	int m_A;
};

class Base2
{
public:
	Base2()
	{
		m_A = 200;
	}
	int m_A;
};

class Son : public Base1, public Base2
{
public:
	Son()
	{
		m_C = 300;
		m_D = 400;
	}
	int m_C;
	int m_D;
};

void test()
{
	Son s;
	cout << "sizeof Son = " << sizeof(s) << endl;
	//當父類中出現同名的成員時,需要加作用域區分
	cout << "m_A = " << s.Base1::m_A << endl;
	cout << "m_A = " << s.Base2::m_A << endl;
}

int main()
{
	test();

	system("pause");
	return 0;
}
           

4.6.8 菱形繼承

菱形繼承概念:

  • 兩個派生類繼承同一個基類
  • 又有某個類同時繼承這兩個派生類
  • 這種繼承稱為菱形繼承,或者鑽石繼承

典型繼承案例:

C++核心程式設計---4.6 類和對象-繼承【P127~P134】4.6 繼承

菱形繼承問題:

  • 1、羊繼承了動物的資料,駝同樣繼承了動物的資料,當羊駝使用資料時,就會産生二義性;
  • 2、羊駝繼承自動物的資料繼承了兩份,其實我們應該清楚,這份資料我們隻要一份就可以。
#include<iostream>
#include<string>
using namespace std;
//動物類
class Animal
{
public:
	int m_Age;
};

//利用虛繼承  解決菱形繼承的問題
//繼承之前 加上關鍵字 virtual 變為虛繼承
//Animal 類稱為  虛基類
//羊類
class Sheep : virtual public Animal{};
//駝類
class Tuo : virtual public Animal{};
//羊駝類
class SheepTuo : public Sheep , public Tuo{};

int main()
{
	SheepTuo st;
	st.Sheep::m_Age = 18;
	st.Tuo::m_Age = 28;
	//當菱形繼承,兩個父類擁有相同資料,需要加以作用域區分
	cout << "st.Sheep::m_Age = " << st.Sheep::m_Age << endl;
	cout << "st.Tuo::m_Age = " << st.Tuo::m_Age << endl;

	system("pause");
	return 0;
}
           
D:\software\Visual Studio 2017\VC\Tools\MSVC\14.16.27023\include\xlocale(319): warning C4530: 使用了 C++ 異常處理程式,但未啟用展開語義。請指定 /EHsc

class SheepTuo  size(8):
        +---
 0      | +--- (base class Sheep)
 0      | | +--- (base class Animal)
 0      | | | m_Age
        | | +---
        | +---
 4      | +--- (base class Tuo)
 4      | | +--- (base class Animal)
 4      | | | m_Age
        | | +---
        | +---
        +---
Microsoft (R) Incremental Linker Version 14.16.27045.0
Copyright (C) Microsoft Corporation.  All rights reserved.

"/out:08 菱形繼承.exe"
"08 菱形繼承.obj"
           
D:\document\vs2017_code\core\004-6 類和對象-繼承\004-6 類和對象-繼承>cl /d1 reportSingleClassLayoutSheepTuo "08 菱形繼承.cpp"
用于 x86 的 Microsoft (R) C/C++ 優化編譯器 19.16.27045 版
版權所有(C) Microsoft Corporation。保留所有權利。

08 菱形繼承.cpp
D:\software\Visual Studio 2017\VC\Tools\MSVC\14.16.27023\include\xlocale(319): warning C4530: 使用了 C++ 異常處理程式,但未啟用展開語義。請指定 /EHsc

class SheepTuo  size(12):
        +---
 0      | +--- (base class Sheep)
 0      | | {vbptr}
        | +---
 4      | +--- (base class Tuo)
 4      | | {vbptr}
        | +---
        +---
        +--- (virtual base Animal)
 8      | m_Age
        +---

SheepTuo::[email protected]@:
 0      | 0
 1      | 8 (SheepTuod(Sheep+0)Animal)

SheepTuo::[email protected]@:
 0      | 0
 1      | 4 (SheepTuod(Tuo+0)Animal)
vbi:       class  offset o.vbptr  o.vbte fVtorDisp
          Animal       8       0       4 0
Microsoft (R) Incremental Linker Version 14.16.27045.0
Copyright (C) Microsoft Corporation.  All rights reserved.

"/out:08 菱形繼承.exe"
"08 菱形繼承.obj"
           

繼續閱讀