C++在單繼承、多繼承、虛繼承時,建構函式、複製建構函式、賦值操作符、解構函式的執行順序和執行內容
一、本文目的與說明
1. 本文目的:理清在各種繼承時,建構函式、複製建構函式、賦值操作符、解構函式的執行順序和執行內容。
2. 說明:雖然複製建構函式屬於建構函式的一種,有共同的地方,但是也具有一定的特殊性,所以在總結它的性質時將它單獨列出來了。
3. 單繼承、多繼承、虛繼承,既然都屬於繼承,那麼雖然有一定的區別,但還是相同點比較多。如果放在一塊講,但為了將內容製作成遞進的,就分開了,對相同點進行重複,(大量的複製貼上哈),但在不同點進行了標註。
注意:三塊內容是逐步遞進的
如果你懂虛擬函式,那麼單繼承和多繼承那塊你就可以不看;
如果你懂多繼承,那單繼承你就不要看了,至於虛繼承就等你懂虛繼承再回來看吧;
如果你只懂單繼承,那你就只看單繼承就好。
二、基本知識
1. 對於一個空類,例如;
- class EmptyClass{};
雖然你沒有宣告任何函式,但是編譯器會自動為你提供上面這四個方法。
- class EmptyClass {
- public:
- EmptyClass(); // 預設建構函式
- EmptyClass(const EmptyClass &rhs); // 複製建構函式
- ~EmptyClass(); // 解構函式
-
EmptyClass& operator=(const
- }
對於這四個方法的任何一個,你的類如果沒有宣告,那麼編譯器就會自動為你對應的提供一個預設的。(在《C++ primer》中,這個編譯器自動提供的版本叫做“合成的***”,例如合成的複製建構函式)當然如果你顯式聲明瞭,編譯器就不會再提供相應的方法。
2. 合成的預設建構函式執行內容:如果有父類,就先呼叫父類的預設建構函式。
3. 合成的複製建構函式執行內容:使用引數中的物件,構造出一個新的物件。
4. 合成的賦值操作符執行內容:使用引數中的物件,使用引數物件的非static成員 依次對 目標物件的成員賦值。注意:在賦值操作符執行之前,目標物件已經存在。
5. 在繼承體系中,要將基類(或稱為父類)的解構函式,宣告為virtual方法(即虛擬函式)。
6. 子類中包含父類的成員。即子類有兩個部分組成,父類部分和子類自己定義的部分。
7. 如果在子類中顯式呼叫父類的建構函式,只能在建構函式的初始化列表中呼叫,並且只能呼叫其直接父類的。
8. 在多重繼承時,按照基類繼承列表中宣告的順序初始化父類。
9. 在虛繼承中,虛基類的初始化 早於 非虛基類,並且子類來初始化虛基類(注意:虛基類不一定是子類的直接父類)。
三、單繼承
核心:在構造子類之前一定要執行父類的一個建構函式。
1.建構函式(不包括複製建構函式)。
順序:①直接父類;②自己
注意:若直接父類還有父類,那麼“直接父類的父類”會在“直接父類” 之前 構造。 可以理解為這是一個遞迴的過程,知道出現一個沒有父類的類才停止。
2.1 如果沒有顯式定義建構函式,則“合成的預設建構函式”會自動呼叫直接父類的“預設建構函式”,然後呼叫編譯器為自己自動生成的“合成的預設建構函式”。
2.2 如果顯式定義了自己的建構函式
2.2.1 如果沒有顯式呼叫直接父類的任意一個建構函式,那麼和“合成的預設建構函式”一樣,會先自動呼叫直接父類的 預設建構函式,然後呼叫自己的建構函式。
2.2.2 如果顯式呼叫了直接父類的任意一個建構函式,那麼會先呼叫直接父類相應的建構函式,然後呼叫自己的建構函式。
2. 複製建構函式
順序:①直接父類;②自己
注意:和建構函式一樣,若直接父類還有父類,那麼“直接父類的父類”會在“直接父類” 之前 構造。 可以理解為這是一個遞迴的過程,知道出現一個沒有父類的類才停止。
2.1 如果 沒有顯式定義複製建構函式,則“合成的複製建構函式”會自動呼叫直接父類的“複製建構函式”,然後呼叫編譯器為自己自動生成的“合成的複製建構函式”(注意:不是預設建構函式)
2.2 如果顯式定義了自己的複製建構函式 (和建構函式類似)
2.2.1 如果沒有顯式呼叫父類的任意一個建構函式,那麼會先呼叫直接父類的 預設建構函式(注意:不是 複製建構函式)。
2.2.2 如果顯式呼叫了直接父類的任意一個建構函式,那麼會先呼叫直接父類相應的建構函式。
3.賦值操作符過載
3.1 如果沒有顯式定義,會自動呼叫直接父類的賦值操作符。(注意:不是 預設建構函式)
3.2 如果顯式定義了,就只執行自己定義的版本,不再自動呼叫直接父類的賦值操作符,只執行自己的賦值操作符。
注意:如有需要對父類子部分進行賦值,應該在自己編寫的程式碼中,顯式呼叫父類的賦值操作符。
4. 解構函式
與建構函式 順序相反。
四、多繼承
和單繼承的差別就是:需要考慮到多個直接父類。其它的都相同
1.建構函式(不包括複製建構函式)。
順序:①所有直接父類;(按照基類繼承列表中宣告的順序)②自己
注意:若直接父類還有父類,那麼“直接父類的父類”會在“直接父類” 之前 構造。 可以理解為這是一個遞迴的過程,知道出現一個沒有父類的類才停止。
2.1 如果 沒有 顯式定義建構函式,則“合成的預設建構函式”會自動依次呼叫所有直接父類的“預設建構函式”,然後呼叫編譯器為自己自動生成的“合成的預設建構函式”。
2.2 如果顯式定義了自己的建構函式
2.2.1 如果沒有顯式呼叫父類的任意一個建構函式,那麼和“合成的預設建構函式”一樣,會自動依次呼叫所有直接父類的 預設建構函式,然後呼叫自己的建構函式。
2.2.2 如果顯式呼叫了父類的任意一個建構函式,那麼按照基類列表的順序,對於每一個父類依次判斷:若顯式呼叫了建構函式,那麼會呼叫該父類相應的建構函式;如果沒有顯式呼叫,就呼叫預設建構函式。最後呼叫自己的建構函式。
2. 複製建構函式
順序:①所有直接父類;(按照基類繼承列表中宣告的順序)②自己
注意:和建構函式一樣,若直接父類還有父類,那麼“直接父類的父類”會在“直接父類” 之前 構造。 可以理解為這是一個遞迴的過程,知道出現一個沒有父類的類才停止。
2.1 如果 沒有顯式定義複製建構函式,則“合成的複製建構函式”會自動依次呼叫所有直接父類的“複製建構函式”,然後呼叫編譯器為自己自動生成的“合成的複製建構函式”(注意:不是預設建構函式)
2.2 如果顯式定義了自己的複製建構函式 (和建構函式類似)
2.2.1 如果沒有顯式呼叫父類的任意一個建構函式,那麼會先自動依次呼叫直接父類的 預設建構函式(注意:不是 複製建構函式)。
2.2.2 如果顯式呼叫了直接父類的任意一個建構函式,那麼按照基類列表的順序,對於每一個父類依次判斷:若顯式呼叫了建構函式,那麼會呼叫該父類相應的建構函式;如果沒有顯式呼叫,就呼叫預設建構函式。最後呼叫自己的複製建構函式。
3.賦值操作符過載
3.1 如果沒有顯式定義,會自動依次呼叫直接父類的賦值操作符。(注意:不是 預設建構函式)
3.2 如果顯式定義了,就只執行自己定義的版本,不再自動呼叫直接父類的賦值操作符,只執行自己的賦值操作符。
注意:如有需要對父類子部分進行賦值,應該在自己編寫的程式碼中,顯式呼叫所有直接父類的賦值操作符。
4. 解構函式
與 建構函式 順序相反。
五、虛繼承
和多繼承的差別就是:要考慮到虛基類,其它的都相同。(虛基類的初始化要早於非虛基類,並且只能由子類對其進行初始化)
1.建構函式(不包括複製建構函式)。
順序:①所有虛基類(按照基類繼承列表中宣告的順序進行查詢);②所有直接父類;(按照基類繼承列表中宣告的順序)③自己
注意:若虛基類或者直接父類還有父類,那麼“直接父類的父類”會在“直接父類” 之前 構造,“虛基類的父類”也會在“虛基類”之前構造。 可以理解為這是一個遞迴的過程,知道出現一個沒有父類的類才停止。
2.1 如果 沒有 顯式定義建構函式,則“合成的預設建構函式”會先依次呼叫所有虛基類的預設建構函式,然後再自動依次呼叫所有直接父類的“預設建構函式”,最後呼叫編譯器為自己自動生成的“合成的預設建構函式”。
2.2 如果顯式定義了自己的建構函式 2.2.1 如果沒有顯式呼叫父類的任意一個建構函式,那麼和“合成的預設建構函式”一樣,會先依次呼叫所有虛基類的預設建構函式,然後再自動依次呼叫所有直接父類的 預設建構函式,最後呼叫自己的建構函式。
2.2.2 如果顯式呼叫了父類的任意一個建構函式,那麼按照基類列表的順序,先初始化所有虛基類,再初始化所有直接父類。對於每一個父類依次判斷:若顯式呼叫了建構函式,那麼會呼叫該父類相應的建構函式;如果沒有顯式呼叫,就呼叫預設建構函式。最後呼叫自己的建構函式。
2. 複製建構函式
順序:①所有虛基類(按照基類繼承列表中宣告的順序進行查詢);②所有直接父類;(按照基類繼承列表中宣告的順序)③自己
注意:和建構函式一樣,若虛基類或者直接父類還有父類,那麼“直接父類的父類”會在“直接父類” 之前 構造,“虛基類的父類”也會在“虛基類”之前構造。 可以理解為這是一個遞迴的過程,知道出現一個沒有父類的類才停止。
2.1 如果 沒有顯式定義複製建構函式,則“合成的複製建構函式”會自動依次呼叫所有直接父類的“複製建構函式”,然後呼叫編譯器為自己自動生成的“合成的複製建構函式”(注意:不是預設建構函式)
2.2 如果顯式定義了自己的複製建構函式 (和建構函式類似)
2.2.1 如果沒有顯式呼叫父類的任意一個建構函式,那麼會先依次呼叫所有虛基類的預設建構函式,然後再依次呼叫所有直接父類的 預設建構函式(注意:不是 複製建構函式)。
2.2.2 如果顯式呼叫了直接父類的任意一個建構函式,那麼按照基類列表的順序,先初始化所有虛基類,再初始化所有直接父類。對於每一個父類依次判斷:若顯式呼叫了建構函式,那麼會呼叫該父類相應的建構函式;如果沒有顯式呼叫,就呼叫預設建構函式。
3.賦值操作符過載
3.1 如果沒有顯式定義,會自動依次呼叫所有虛基類和所有直接父類的賦值操作符。(注意:不是 預設建構函式)
3.2 如果顯式定義了,就只執行自己定義的版本,不再自動呼叫直接父類的賦值操作符,只執行自己的賦值操作符。
注意:如有需要對父類子部分進行賦值,應該在自己編寫的程式碼中,顯式呼叫所有虛基類和所有直接父類的賦值操作符。
4. 解構函式
與 建構函式 順序相反。
六、總結:
1. 整體順序:虛基類 --> 直接父類 -->自己
2. 在任何顯式定義的建構函式中,如果沒有顯式呼叫父類的建構函式,那麼就會呼叫父類的預設建構函式。
3. 合成的複製建構函式、合成的賦值操作符,(當沒有顯式定義時,編譯器自動提供),會自動呼叫的是虛基類和直接父類的複製建構函式和賦值操作符,而不是預設建構函式;
4. 自己顯式定義的複製建構函式,除非在初始化列表中顯示呼叫,否則只會呼叫虛基類和父類的預設建構函式。
5. 自己顯式定義的賦值操作符,除非顯式呼叫,否則只執行自己的程式碼。
6. 解構函式的執行順序與 建構函式 相反。
七、例子程式
話說只有自己寫一個程式,然後研究執行結果,才會掌握的更好。所以下面就是個例子程式了。可以根據需要,註釋掉某個類的相應函式,觀察結果。
1. 該例子的繼承層次圖為:(M和N是虛基類)
2. 程式碼如下
- #include <iostream>
- usingnamespace std;
- class A {
- public:
- A() {
- cout<<"int A::A()"<<endl;
- }
- A(A &a) {
- cout<<"int A::A(A &a)"<<endl;
- }
- A& operator=(A& a) {
- cout<<"int A::operator=(A &a)"<<endl;
- return a;
- }
- virtual ~A() {
- cout<<"int A::~A()"<<endl;
- }
- };
- class M :public A {
- public:
- M() {
- cout<<"int M::M()"<<endl;
- }
- M(M &a) {
- cout<<"int M::M(M &a)"<<endl;
- }
- M& operator=(M& m) {
- cout<<"int M::operator=(M &a)"<<endl;
- return m;
- }
- virtual ~M() {
- cout<<"int M::~M()"<<endl;
- }
- };
- class B:virtualpublic M {
- public:
- B() {
- cout<<"int B::B()"<<endl;
- }
- B(B &a) {
- cout<<"int B::B(B &a)"<<endl;
- }
- B& operator=(B& b) {
- cout<<"int B::operator=(B &a)"<<endl;
- return b;
- }
- virtual ~B() {
- cout<<"int B::~B()"<<endl;
- }
- };
- class N :public A {
- public:
- N() {
- cout<<"int N::N()"<<endl;
- }
- N(N &a) {
- cout<<"int N::N(N &a)"<<endl;
- }
- N& operator=(N& n) {
- cout<<"int N::operator=(N &a)"<<endl;
- return n;
- }
- virtual ~N() {
- cout<<"int N::~N()"<<endl;
- }
- };
- class C:virtualpublic N {
- public:
- C() {
- cout<<"int C::C()"<<endl;
- }
- C(C &a) {
- cout<<"int C::C(C &a)"<<endl;
- }
- C& operator=(C& c) {
- cout<<"int C::operator=(C &a)"<<endl;
- return c;
- }
- virtual ~C() {
- cout<<"int C::~C()"<<endl;
- }
- };
- class E:virtualpublic M{
- public:
- E() {
- cout<<"int E::E()"<<endl;
- }
- E(E &a) {
- cout<<"int E::E(E &a)"<<endl;
- }
- E& operator=(E& e) {
-
相關推薦
C++在單繼承、多繼承、虛繼承時,建構函式、複製建構函式、賦值操作符、解構函式的執行順序和執行內容
一、本文目的與說明 1. 本文目的:理清在各種繼承時,建構函式、複製建構函式、賦值操作符、解構函式的執行順序和執行內容。 2. 說明:雖然複製建構函式屬於建構函式的一種,有共同的地方,但是也具有一定的特殊性,所以在總結它的性質時將它單獨列出來了。
1、【C++】類&物件/建構函式/拷貝建構函式/操作符過載/解構函式
一、C++類 & 物件 C++ 在 C 語言的基礎上增加了面向物件程式設計,C++ 支援面向物件程式設計。類是 C++ 的核心特性,通常被稱為使用者定義的型別。 類用於指定物件的形式,它包含了資料表示法和用於處理資料的方法。類中的資料和方法稱為類的成員。函式在
C++:面試時應該實現的string類(建構函式、拷貝建構函式、賦值運算子過載和解構函式)
一、string類的4個基本函式是什麼? 建構函式 拷貝建構函式 賦值運算子過載 解構函式 二、函式實現 1.建構函式 String(char* pStr = " ")
賦值操作符、比較操作符、算術操作符、邏輯操作符、位域操作符
include pause 比較操作符 int clu put nbsp pan code 賦值操作符、比較操作符、算術操作符、邏輯操作符、位域操作符 , 如“=”、“+=” “>=”、“<=”、“+”、“*”、“%”、“&&”、“||”、“&l
C++繼承、多型,虛成員函式(包括虛解構函式、虛複製建構函式)學習筆記
通過哺乳類派生貓、狗等學習繼承、多型中的知識點 先貼上類的程式碼 #include<iostream> enum BREED { YORKIE, CAIRN, DANDIE, SHETLAND, DOBERAMN, LAB };//犬種 class Mam
C++類中的一些細節(過載、重寫、覆蓋、隱藏,建構函式、解構函式、拷貝建構函式、賦值函式在繼承時的一些問題)
1 函式的過載、重寫(重定義)、函式覆蓋及隱藏 其實函式過載與函式重寫、函式覆蓋和函式隱藏不是一個層面上的概念。前者是同一個類內,或者同一個函式作用域內,同名不同引數列表的函式之間的關係。而後三者是基類和派生類函式不同情況下的關係。 1.1 函式過載
從零開始學C++之虛擬函式與多型(一):虛擬函式表指標、虛解構函式、object slicing與虛擬函式、C++物件模型圖
#include <iostream>using namespace std;class CObject {public: virtual void Serialize() { cout << "CObject::Serialize ..." <&
C++之繼承(多重繼承+多繼承+虛繼承+虛解構函式+重定義)
多重繼承和多繼承 這個我們來講講這兩個的概念問題,一字之差,千差萬別。 多重繼承,比如有三個類,人類-士兵類-步兵類,三個依次繼承,這樣的繼承稱為多重繼承。 class Person {}; class Soldier :public Person
C++中抽象類以及虛/純虛、解構函式的區別與介紹
一、虛擬函式 在某基類中宣告為 virtual 並在一個或多個派生類中被重新定義的成員函式,用法格式為:virtual+函式返回型別+ 函式名(引數表) {函式體};實現多型性,通過指向派生類的基類指標或引用,訪問派生類中同名覆蓋成員函式。 二、純虛擬函式 純虛擬函式是一種
Swift -繼承、屬性、重寫父類、懶載入、解構函式
1. 新建工程命名:zhoukaojineng,建立一個類Person,在類中定義方法eat,實現列印“吃飯” 2. 建立一個繼承自Person的Teacher類,在Teacher類中定義方法teach,實現列印“上課”,呼叫其父類的eat函式 3. 建立一個類Student並繼承與Pe
[收集]c++抽象類、純虛擬函式以及巧用純虛解構函式實現介面類
在Java、C#中有關鍵詞abstract指明抽象函式、抽象類,但是在C++中沒有這個關鍵詞,很顯然,在C++也會需要只需要在基類宣告某函式的情況,而不需要寫具體的實現,那C++中是如何實現這一功能的,答案是純虛擬函式。 含有純虛擬函式的類是抽象類,不能生成物件,只能派生。
【小家java】類中靜態程式碼塊、構造程式碼塊、靜態變數執行順序和繼承邏輯
相關閱讀 每篇一句 上帝給每個人都安排了幸福的一生,我們的任務就是把它走完 1、概述 誠如各位所知,java的三大特性:封裝、繼承、多型。其中繼承,是java中最有學問的一點也是最相對來說最難理解的一些東西,本文針對於此,做一些例項分析,希望能夠幫助大家
C++學習:虛擬函式,純虛擬函式(virtual),虛繼承,虛解構函式
C++學習:虛擬函式,虛繼承,純虛擬函式(virtual)虛解構函式 虛擬函式 純虛擬函式 虛解構函式 虛繼承 簡介 在java這種高階語言中,有abstract和interface這兩個關鍵字.代表的是抽象類和介面,但是在C++這門語言中
c++類的拷貝、賦值與銷毀(拷貝構造函數、拷貝賦值運算符析構函數)
錯誤 保存 編譯 oid 生成 標準庫 int 為什麽 explicit 拷貝構造函數 如果一個構造函數的第一個參數是自身類類型的引用,且任何額外參數都有默認值,則此構造函數是拷貝構造函數。 拷貝構造函數第一個參數必須是一個引用類型。此參數幾乎總是一個con
繼承裡既有虛繼承也有虛擬函式繼承(即既有虛基表,也有虛擬函式表)
對於單一的虛繼承可參考這篇部落格: https://blog.csdn.net/sophia__yu/article/details/82791522 對於有虛擬函式繼承可參考這篇部落格: https://blog.csdn.net/sophia__yu/article/details/82
【C++筆記】編寫類string的建構函式、解構函式和賦值函式
#include<iostream> using namespace std; class String { public: String(const char *str=NULL); //普通建構函式 String(const Stri
虛解構函式、虛擬函式考題
虛解構函式、虛擬函式結合考題變種 1.[Effective C++原則07]:為多型基類宣告virtual 解構函式。 [如果不]: 如果不宣告為解構函式,可能出現的結果如下:Derived物件的成分沒有被銷燬,形成資源洩露、在除錯上會浪費很長時間。 #incl
單繼承派生類建構函式與解構函式順序
派生類建構函式形式: 派生類建構函式 (引數表):基類建構函式(引數表) 類物件成員1(引數表)... 類物件成員n(引數表)//只能用表示式的方式對類物件成員進行初始化 {...派生類自定義的資料成員初始化} 在派生類中,首先呼叫基類的建構函式,其次呼叫
C++中class類 的 建構函式、解構函式
說明: demo.cpp:main.cpp所在之處 Line.h:線段類的.h檔案 Line.cpp:線段類的.cpp檔案 Coordinate.h:座標類的.h檔案 Coordinate.cpp:
c++單鏈表【建構函式、運算子過載、解構函式、增刪查改等】
c++中的單向連結串列寫法:實現增刪查改、建構函式、運算子過載、解構函式等。建立標頭檔案SList.h#pragma once typedef int DataType; //SList要訪問SListNode,可以通過友元函式實現,友元函式在被訪問的類中 class SL