【C++】繼承相關(基類與派生類的繼承關系以及細節整理)

目錄

00.引言

01.繼承的定義

02.基類和派生類對象

03.繼承中的作用域

04.派生類的默認成員函數

05.友元、靜態成員


00.引言

繼承是面向對象編程中的一個重要概念,它的作用是創建一個新的類,該類可以從一個已存在的類(父類/基類)繼承屬性和方法。

通過下面一個例子可以理解繼承的作用:

class teacher
{
private:char _sex;int _age;char _name;
private:int _jobid; // 工號
};class student
{
private:char _sex;int _age;char _name;
private:int _stuid; // 學號
};

在創建學校學校成員信息的時候,需要定義不同的兩個類分別用于存放老師和學生的信息種類,但是兩者的信息類別是存在重復的(性別、年齡、姓名)?,定義兩個類還好,但是如果定義需要更多的類呢,那樣就會進行過多的重復工作。此時就可以用繼承的方法,將同樣的信息類別在父類中定義:

class people
{
public:char _sex;int _age;char _name;
};

使用正確的繼承語法,繼承父類person的屬性:

class teacher : public people
{
public:teacher(): _sex('男') // 在成員初始化列表進行初始化 報錯, _age(40), _name('張'){}private:int _jobid; // 工號
};class student : public people
{
public:student(){_sex = '男';_age = 20;_name = '李';}
private:int _stuid; // 學號
};

這里注意:在定義默認構造函數時,不能夠通過初始化列表初始化其父類的成員,因為初始化列表只能初始化其自身的成員,所以 teacher() 函數會報錯。

01.繼承的定義

如圖:

我們可以看到,student是子類,也叫派生類,他繼承的是父類people,也叫基類,而public是繼承方式,繼承方式也可以是protected、private,和訪問限定符是一樣的。

使用不同的繼承方式繼承的基類成員的訪問權限是不一樣的,具體關系可以看下面這張表:

可以看出基類的私有成員在派生類中是不可見的,而其他成員的訪問方式取決于基類中成員的訪問限定符和繼承方式。通常情況下,使用 public 繼承是最常見的方式,因為它保留了基類的接口,并且派生類可以訪問基類的公共和受保護成員。?

注意:

使用關鍵字class時默認的繼承方式是private使用struct時默認的繼承方式是public,不過 最好顯示的寫出繼承方式。

在實際運用中一般使用都是public繼承,幾乎很少使用protetced/private繼承,也不提倡使用protetced/private繼承,因為protetced/private繼承下來的成員都只能在派生類的類里 面使用,實際中擴展維護性不強。

02.基類和派生類對象

當一個派生類對象被賦值給一個基類對象時,如果使用的是賦值操作符 = 或者拷貝構造函數,那么只會復制派生類對象中基類部分的內容,而派生類特有的成員會被截斷

這種行為稱為對象切片,因為派生類對象被“切片”成了基類對象

基類的指針或者引用可以通過強制類型轉換賦值給派生類的指針或者引用。但是必須是基類的指針是指向派生類對象時才是安全的。

class people
{
public:char _sex;int _age;char _name;
};class student : public people
{
public:student(){_sex = '男';_age = 20;_name = '李';}
public:int _stuid; // 學號
};int main()
{student s1;s1._stuid; // 可以訪問people p1 = s1;p1._stuid; // 無法訪問return 0;
}

這里將派生類對象s1賦值給基類對象p1后,?p1就無法訪問s1中的_stuid元素,因為s1是被切片賦值給p1的,如圖:

下面的示例用來解釋基類的指針賦值:

class Person
{protected :string _name; // 姓名string _sex;  // 性別int _age;    // 年齡
};
class Student : public Person
{
public:int _No; // 學號
};
void Test ()
{Student sobj;// 1.子類對象可以賦值給父類對象/指針/引用Person pobj = sobj;Person* pp = &sobj;Person& rp = sobj;//2.基類對象不能賦值給派生類對象sobj = pobj;//3.基類的指針可以通過強制類型轉換賦值給派生類的指針pp = &sobjStudent* ps1 = (Student*)pp; // 這種情況轉換時可以的。ps1->_No = 10;pp = &pobj;Student* ps2 = (Student*)pp; // 這種情況轉換時雖然可以,但是會存在越界訪問的問ps2->_No = 10;
}

03.繼承中的作用域

在繼承體系中,基類和派生類都有獨立的作用域。

訪問限定符決定了類的成員在類內部和外部的可見性可訪問性。繼承方法決定了基類的的成員繼承到派生類之后的作用域。

比如用public方法繼承基類的成員,那么就在派生類的內部和外部都可以調用基類的成員,但是如果派生類內部也定義了一個和基類名字相同的成員呢?就像這樣:

class Person
{
protected:string _name = "小李子"; // 姓名int _num = 111;// 身份證號
};
class Student : public Person
{
public:void Print(){cout << " 姓名:" << _name << endl;cout << " 身份證號:" << Person::_num << endl;cout << " 學號:" << _num << endl;}
protected:int _num = 999; // 學號
};

此時派生類Student和基類Person都定義了成員變量_num,此時在派生類內部調用_num,會默認調用派生類本身的,而基類的_num會被隱藏掉,因為,當子類和父類中有同名成員,子類成員將屏蔽父類對同名成員的直接訪問,這種情況叫隱藏,也叫重定義。但是在子類成員函數中,也可以使用 基類::基類成員 顯示訪問。

同名成員的定義降低了代碼的可讀性,所以我們在實際繼承體系中最好不要定義同名的成員,上述的代碼就可以這么修改:

class Person
{
protected:string _name = "小李子"; // 姓名int _IDnum = 111;// 身份證號
};
class Student : public Person
{
public:void Print(){cout << " 姓名:" << _name << endl;cout << " 身份證號:" << _IDnum << endl;cout << " 學號:" << _STnum << endl;}
protected:int _STnum = 999; // 學號
};

04.派生類的默認成員函數

?首先我們要知道默認成員函數是什么:默認成員函數提供默認行為,包括默認構造函數、拷貝構造函數、運算符重載等,這樣即使我們沒有顯式地編寫這些函數,類仍然可以正常地進行對象的創建、復制和賦值等操作。那么在派生類中,這幾個成員函數是如何生成的呢?

1.默認構造函數

由于派生類繼承了基類的成員,在調用派生類的默認構造函數時必須調用基類的默認構造函數初始化基類的那一部分成員:


class Person
{
public:Person(const char* name = "peter"): _name(name){cout << "Person()" << endl;}
protected:string _name; // 姓名
};
class Student : public Person
{
public:Student(const char* name, int num): Person(name), _num(num){cout << "Student()" << endl;}
protected:int _num; //學號
};
int main()
{Student s1("jack", 18);return 0;
}

運行結果:

Person()
Student()

可以看出,編譯器先調用基類的默認構造函數后再調用的派生類的默認構造函數。

2.析構函數

派生類的析構函數會在被調用完成后自動調用基類的析構函數清理基類成員。如此一來保證了先清理派生類成員再清理基類成員的順序

class Person
{
public:Person(const char* name = "peter"): _name(name){cout << "Person()" << endl;}~Person(){cout << "~Person()" << endl;}
protected:string _name; // 姓名
};
class Student : public Person
{
public:Student(const char* name, int num): Person(name), _num(num){cout << "Student()" << endl;}~Student(){cout << "~Student()" << endl;}
protected:int _num; //學號
};
int main()
{Student s1("jack", 18);return 0;
}

運行結果:

Person()
Student()

~Student()
~Person()

因為子類是對基類的繼承與延伸,子類受基類的影響,但是基類并不受子類影響,所以在銷毀子類對象時,首先釋放子類特有的資源,然后再釋放基類的資源。

3.拷貝構造函數

派生類的拷貝構造函數需要對兩部分進行拷貝構造,一個是派生類自身的成員變量,一個是基類的成員變量,所以派生類的拷貝構造函數必須調用基類的拷貝構造完成基類的拷貝初始化。

class Person
{
public:Person(const Person& p): _name(p._name){cout << "Person(const Person& p)" << endl;}
protected:string _name; // 姓名
};
class Student : public Person
{
public:Student(const Student& s): Person(s), _num(s._num){cout << "Student(const Student& s)" << endl;}
protected:int _num; //學號
};int main()
{Student s1("jack", 18);Student s2(s1);return 0;
}

運行結果:

Person(const Person& p)
Student(const Student& s)

可以看出,也是先調用了基類的拷貝構造函數再調用派生類的。?

4.賦值運算符重載

與拷貝構造類似,派生類的operator=也必須要調用基類的operator=完成基類的復制

class Person
{
public:Person& operator=(const Person& p){cout << "Person operator=(const Person& p)" << endl;if (this != &p)_name = p._name;return *this;}
protected:string _name; // 姓名
};
class Student : public Person
{
public:Student& operator = (const Student& s){cout << "Student& operator= (const Student& s)" << endl;if (this != &s){Person::operator =(s);_num = s._num;}return *this;}
protected:int _num; //學號
};int main()
{Student s1("jack", 18);Student s2(s1);Student s3("rose", 17);s1 = s3;return 0;
}

運行結果:

Student& operator= (const Student& s)
Person operator=(const Person& p)

因為 s3Student 類型的對象。因此,會先輸出 Student& operator= (const Student& s)。然后在 Student 類的賦值運算符內部,通過 Person::operator=(s); 調用了基類 Person 的賦值運算符重載函數。

05.友元、靜態成員

友元關系不能繼承,基類的友元不能訪問子類私有和保護成員,但是可以通過將派生類定義成友元的方式,使得派生類也可以訪問基類的私有成員

#include <iostream>class Shape {
private:double _width;double _height;public:Shape(double width, double height) : _width(width), _height(height) {}friend class Rectangle; // 將Rectangle類聲明為Shape類的友元
};class Rectangle : public Shape {
public:Rectangle(double width, double height) : Shape(width, height) {}void displayArea() {double area = _width * _height; // 可以直接訪問基類的私有成員std::cout << "Area of rectangle: " << area << std::endl;}
};int main() {Rectangle rect(5.0, 3.0);rect.displayArea();return 0;
}

在這個例子中,Rectangle 類通過將 Shape 類聲明為友元,可以直接訪問 Shape 類的私有成員 _width_height

如果基類定義了一個static靜態成員,則整個繼承體系里面只有一個這樣的成員。無論派生出多少類,都只有一個static成員實例

class Person
{
public:Person () {++ _count ;}
protected:string _name ; // 姓名
public:static int _count; // 統計人的個數。
};int Person :: _count = 0;
class Student : public Person
{
protected :int _stuNum ; // 學號
};
class Graduate : public Student
{
protected :string _seminarCourse ; // 研究科目
};
void TestPerson()
{Student s1 ;Student s2 ;Student s3 ;Graduate s4 ;cout <<" 人數 :"<< Person ::_count << endl;Student ::_count = 0;cout <<" 人數 :"<< Person ::_count << endl;
}

以上就是繼承相關的知識的整理了,歡迎在評論區留言,覺得這篇博客對你有幫助的,可以點贊收藏關注支持一波~😉

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/web/11595.shtml
繁體地址,請注明出處:http://hk.pswp.cn/web/11595.shtml
英文地址,請注明出處:http://en.pswp.cn/web/11595.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

服務攻防——數據庫安全

第一步: 端口掃描&#xff1a;nmap 掃不到端口&#xff1a;端口被修改&#xff0c;防護軟件&#xff0c;放在內網環境 mysql 內置端口3306 第一種官方漏洞 第一步:先掃描有什么端口開發 用這個錯誤密碼一直訪問&#xff0c;最終就進去了 弱口令猜解 不可以直接猜解&#x…

WEB后端復習——MVC、SSM【含登錄頁面代碼】

MVC&#xff08;Model-View-Controller&#xff09;是一種軟件設計模式&#xff0c;用于將應用程序分解為三個相互關聯的組件&#xff1a;模型&#xff08;Model&#xff09;、視圖&#xff08;View&#xff09;和控制器&#xff08;Controller&#xff09;。這種模式在構建用戶…

機器人學導論實驗1—CoppeliaSim 平臺介紹及初步使用BJTU

1. 實驗內容分析 對實驗內容的理解及關鍵點&#xff1a; 理解這個實驗的關鍵點在于理解如何使用CoppeliaSim和MATLAB來控制和操作機器人。需要熟悉這兩個工具的基本操作&#xff0c;例如如何加載場景、如何修改機器人參數、如何使用MATLAB客戶端程序來控制機器人等。此外&#…

Docker 部署 Prometheus 實現一個極簡的 QPS 監控

背景 : Prometheus 是近年來最流行的開源監控框架, 其功能強大且易于使用, 擁有各種主流后端語言(Java/Go/Python/Node.js等)與各種場景(如web handler/ k8s/Nginx/MySQL等)的客戶端, 并自帶圖形化顯示頁面。分享一個快速入門Prometheus 的教程, 實現一個極簡的, 后端開發需要特…

Nginx-基礎-基礎配置-Location

Location 參數匹配模式 參數匹配方式匹配模式說明注意事項精準匹配普通字符串匹配用于標準uri前&#xff0c;要求請求字符串與uri精準匹配&#xff0c;成功則立即處理&#xff0c;nginx停止搜索其他匹配。~正則匹配正則表達式匹配用于正則uri&#xff0c;表示uri包含正則表達…

使用 Docker 輕松部署 Spring Boot 應用

當今軟件開發領域&#xff0c;Docker 和 Spring Boot 的組合已成為開發和部署應用程序的黃金標準。在這篇博客中&#xff0c;我們將詳細探討如何將 Spring Boot 應用容器化并使用 Docker 進行部署&#xff0c;確保你的部署過程既高效又可靠。 引言 Docker 提供了一個標準化的…

基于SSM的理發店會員管理系統的設計和實現(有報告)。Javaee項目。ssm項目。

演示視頻&#xff1a; 基于SSM的理發店會員管理系統的設計和實現&#xff08;有報告&#xff09;。Javaee項目。ssm項目。 項目介紹&#xff1a; 采用M&#xff08;model&#xff09;V&#xff08;view&#xff09;C&#xff08;controller&#xff09;三層體系結構&#xff0…

Docker安裝達夢數據庫

1.確保已安裝Docker 可參考&#xff1a;Linux安裝Docker-CSDN博客 2.上傳dm鏡像并導入安裝包 可以從&#xff1a;產品下載 | 達夢數據庫下載dm鏡像&#xff0c;如下圖&#xff1a; docker load -i dm8_20230808.tar 3.導入后查看鏡像 docker images 4.啟動容器 docker run …

圖的概念、性質和存儲與簡單遍歷

前置知識&#xff1a;樹的基本概念及性質 為了保證學習效果&#xff0c;請保證已經掌握前置知識之后&#xff0c;再來學習本章節&#xff01;如果在閱讀中遇到困難&#xff0c;也可以回到前面章節查閱。 學習目標 掌握圖的基本概念掌握圖的一些性質 圖的概念 基本概念 圖 (…

Pytorch如何計算網絡參數

方法一. 利用pytorch自身 PyTorch是一個流行的深度學習框架&#xff0c;它允許研究人員和開發者快速構建和訓練神經網絡。計算一個PyTorch網絡的參數量通常涉及兩個步驟&#xff1a;確定網絡中每個層的參數數量&#xff0c;并將它們加起來得到總數。 以下是在PyTorch中計算網…

如何在 CloudFlare 里屏蔽/攔截某個 IP 或者 IP 地址段

最近除了接的 CloudFlare 代配置訂單基本很少折騰自己的 CloudFlare 配置了,今天給大家簡單的講解一下如何在 CloudFlare 里屏蔽/攔截 IP 地址和 IP 地址段,雖然明月一直都很反感針對 IP 的屏蔽攔截,但不得不說有時候還是很有必要的。并且,既然可以攔截屏蔽 IP 自然也可以但…

鴻蒙內核源碼分析(VFS篇) | 文件系統和諧共處的基礎

基本概念 | 官方定義 VFS&#xff08;Virtual File System&#xff09;是文件系統的虛擬層&#xff0c;它不是一個實際的文件系統&#xff0c;而是一個異構文件系統之上的軟件粘合層&#xff0c;為用戶提供統一的類Unix文件操作接口。由于不同類型的文件系統接口不統一&#x…

Flink HA模式下JobManager切換時發送告警

資源&版本信息 Flink版本1.14.6 運行平臺&#xff1a;K8s HA使用ZK&#xff08;使用K8s的ETC應該是一個道理&#xff09; 詳解Flink HA原理 Flink啟動時會創建HighAvailabilityServices提供HA和相關基礎服務&#xff0c;其中包括leaderRetrievalService和LeaderElecti…

搜索引擎的設計與實現(二)

目錄 3 搜索引擎的基本原理 3.1搜索引擎的基本組成及其功能 l.搜索器 (Crawler) 2.索引器(Indexer) 3.檢索器(Searcher) 4.用戶接口(UserInterface) 3.2搜索引擎的詳細工作流程 4 系統分析與設計 4.1系統分析 4.2系統概要設計 4.2系統實現目標 前面內容請移步 搜索引…

Rust 語言不支持 goto 語句

一、Rust 不提供 goto 語句 Rust 語言并沒有提供 goto 語句。goto 語句在很多現代編程語言中已經不再被推薦使用&#xff0c;因為它可能導致代碼的流程變得難以跟蹤和理解&#xff0c;特別是在復雜的程序中。Rust 語言設計者選擇了更加結構化和可預測的控制流語句&#xff0c;…

關于C++多態的復習總結

多態 簡介: 面向對象的三大特性之一&#xff0c;多態顧名思義即具有多種形態&#xff0c;即去執行某個行為時&#xff0c;當不同的對象去執行時會產生不同的狀態 構成多態的條件 條件一 必須通過基類&#xff08;父類&#xff09;的指針或者引用調用虛函數&#xff08;函數…

寧夏銀川市起名專家的老師顏廷利:死神(死亡)并不可怕,可怕的是...

在中國優秀傳統文化之中&#xff0c;漢語‘巳’字與‘四’同音&#xff0c;在阿拉伯數字里面&#xff0c;通常用‘4’來表示&#xff1b; 湖南長沙、四川成都、重慶、寧夏銀川最靠譜最厲害的起名大師的老師顏廷利教授指出&#xff0c;作為漢語‘九’字&#xff0c;倘若是換一個…

FreeRTOS中斷管理

FreeRTOS中斷管理 基于STM32_stm32 freertos 按鍵中斷-CSDN博客 更加詳情請看以上鏈接↑ 中斷優先級 任何中斷的優先級都大于任務! 在我們的操作系統,中斷同樣是具有優先級的,并且我們也可以設置它的優先級,但是他的優先 級并不是從 0~15 ,默認情況下它是從 5~15 ,…

[ACTF新生賽2020]SoulLike

沒見過的錯誤&#xff1a; ida /ctg目錄下的hexrays.cfg文件中的MAX_FUNCSIZE64 改為 MAX_FUNCSIZE1024 然后就是一堆數據 反正就是12個字符 from pwn import * flag"actf{" k0 for n in range(12):for i in range(33,127):pprocess("./SoulLike")_flag…

94.二叉樹的中序遍歷

刷算法題&#xff1a; 第一遍&#xff1a;1.看5分鐘&#xff0c;沒思路看題解 2.通過題解改進自己的解法&#xff0c;并且要寫每行的注釋以及自己的思路。 3.思考自己做到了題解的哪一步&#xff0c;下次怎么才能做對(總結方法) 4.整理到自己的自媒體平臺。 5.再刷重復的類…