继承和多态 2.0 -- 继承的六个默认成员函数
本文重要介紹普通繼承中如何寫派生類的六個默認成員函數,主要是針對在派生類中,如何調用基類的六個默認成員函數
需要說明的一點就是,如果子類中沒有調用父類的函數時,系統會自動生成一個。
構造函數
子類中有父類的成員,子類首先需要調用父類的構造函數,然后調用自己的構造函數。如果沒有調用父類的構造函數,系統會自動生成一個。
在構造函數中,我們使用父類的匿名對象完成初始化。看下面的代碼
class A
{
public:A(int a = 0):_a(a){}
private:int _a;
};class B : public A
{
public:B(int a = 0,int b = 1):A(a),_b(b){}
private:int _b;
};
上面的例子我們可以看出來,當我們寫子類B的構造函數的時候,首先需要先調用 父類的匿名構造函數,完成對子類中包含的父類成員的初始化的工作。
拷貝構造函數
拷貝構造函數需要和構造函數區分開,因為我們的構造函數的參數是可變的,我們可以根據需要,為子類中包含的父類成員傳遞參數,但是在拷貝構造函數中,我們只能夠傳遞的是子類的對象的引用,但是這個時候我們實際上還是可以調用父類的拷貝構造函數的,根據我們上一篇文章中學習過的賦值兼容規則,子類的對象是可以賦值給父類的對象的,所以這個時候我們可以拿子類的對象去實例化子類中包含的父類成員。此時實際上采用 的還是父類匿名對象。
拷貝構造函數應該寫成是下面的形式
B(const B& b):A(b),_b(b._b){}
這里語法規定子類中初始化父類的時候,必須在初始化列表中初始化父類,包括我們上面的構造函數也是這個樣子,必須在初始化列表中初始化父類
賦值運算符的重載
拷貝構造函數應該如何使用呢,還是上面的思想,我們需要那子類的對象去為父類賦值,所以這個時候,我們可以在子類的賦值運算符重載的函數內部,調用父類的賦值運算符重載的函數,對父類的對象進行賦值。
B& operator=(const B& b){if (this != &b){operator=(b);_b = b._b;}return *this;}
但是上面的問題,會出現一個問題就是,棧溢出,為什么會出現這樣的問題呢,主要是在調用父類的賦值運算符重載的 時候,實際上是并沒有調用父類的賦值運算符重載,這里調用的是子類的,這樣子就形成了遞歸了,然后會一直調用下去。
解決辦法是這樣的,我們需要在調用父類的函數前面加上一個作用域限制符。
A::operator(b);
這里為什么會調用子類的賦值運算符的重載呢,實際上是子類的函數名和父類的函數名構成了重定義,這個時候如果不加上作用域限制符就會出現問題了
析構函數
這里在寫子類的析構函數的時候,是不允許子類調用父類的析構函數的,有兩個原因
- 因為棧幀的關系,后開辟的空間先釋放掉,意思就是說,父類是先開辟的空間,應該后釋放,為了防止程序員寫錯代碼,先釋放了父類的,這個時候就會出現問題,所以要求自動的調用父類的析構函數
- 如果我們在實現析構函數的時候,忘記釋放了,這個時候就會造成內存泄漏了,造成資源的浪費
基于上面的一些原因,要求就是只需要由系統自動的調用父類的析構函數即可。
總結
以上是生活随笔為你收集整理的继承和多态 2.0 -- 继承的六个默认成员函数的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: STL模拟实现1.0 -- list和i
- 下一篇: 继承和多态 3.0 -- 菱形继承