c ++繼承

目錄

繼承的含義:(常規->具體)

 派生類:

重定義、重載與重寫

重定義:(redefining)

重載:(overload)

重寫:(override)

 派生類的對象具有多個類型

不繼承的函數:

構造函數和析構函數


繼承的含義:(常規->具體)

繼承意味着可以定義並編譯常規形式的類,再定義這個類的更加具體的形式並繼承這個類的所有屬性,即一個基類創建派生類的過程,在這個過程中,我們可以根據需要添加更多的成員屬性和成員函數

 派生類:

class HourlyEmployee : public Employee
{

}
  •  HourlyEmployee:派生類
  • Employee:基類
  • 繼承關係:符號(

派生類的定義列出新增的成員變量,基類定義的成員變量不必提及,它們會自動提供給派生類。

重定義、重載與重寫

重定義:(redefining)

繼承基類的成員函數的定義需要在派生類的定義修改,使其在派生類中的定義有別於在基類中的定義。爲了重定義成員函數,只需在類定義中列出它,並提供新的定義,類似於在派生中新增成員函數。

  • 子類重新定義父類中有相同名稱的非虛函數 (函數名相同,返回的類型相同, 參數列表可以相同,可以不同 ) 。

重載:(overload)

  • 重載是函數名相同,參數列表不同
  • 重載只是在類的內部存在。但是不能靠返回類型來判斷。

重寫:(override)

重寫也叫做覆蓋。子類重新定義父類中有相同名稱和參數的虛函數函數特徵相同。但是具體實現不同,主要是在繼承關係中出現的 。

重寫需要注意:

1 被重寫的函數不能是static的。必須是virtual的

2 重寫函數必須有相同的類型,名稱和參數列表

3 重寫函數的訪問修飾符可以不同。儘管virtual是private的,派生類中重寫改寫爲public,protected也是可以的

#include <iostream>
using namespace std;

class A
{
public:
    A()
    {
        cout<<"this is A'contrutor."<<endl;
    }
    ~A()
    {
        cout<<"this is A'destrutor."<<endl;
    }
    //overload,兩個f1函數在Base類的內部被重載
    void f1() { cout<<"A f1(null)"<<endl; }
    void f1(int a) { cout<<"A f1(int)"<<endl; }

    virtual void display() { cout<<"A display()"<<endl; } 
private:   
    void say(){ cout<<"A say()"<<endl; }
};
class B:public A
{
public:
    B():A()
    {
        cout<<"this is B'contrutor."<<endl;
    }
    ~B()
    {
        cout<<"this is B'destrutor."<<endl;
    }
    //重定義
    void f1(int a) { cout<<"B f1(int)"<<endl; }
    //重寫
    void display() { cout<<"B display()"<<endl; }
};
int main()
{
    int a=1;
    A a1;
    cout<<"********"<<endl;
    B b1;

    a1.f1();
    a1.f1(a);

    b1.f1(a);

    a1.display();
    b1.display();
    return 0;
}

綜上所述,總結如下:

  •  成員函數重載特徵:

相同的範圍(在同一個類中

函數名字相同

參數不同

virtual關鍵字可有可無

  • 重寫(覆蓋)是指派生類函數覆蓋基類函數,特徵是

不同的範圍,分別位於基類和派生類中

函數的名字相同

參數相同

基類函數必須有virtual關鍵字

  • 重定義(隱藏)是指派生類的函數屏蔽了與其同名的基類函數,規則如下:

   a 如果派生類的函數和基類的函數同名,但是參數不同,此時,不管有無virtual,基類的函數被隱藏。

   b 如果派生類的函數與基類的函數同名,並且參數也相同,但是基類函數沒有vitual關鍵字,此時,基類的函數被隱藏。

派生類的對象具有多個類型

  • 在允許使用基類對象的任何地方,都能使用派生類的對象。

例如:

在函數中要求使用Employee類型的參數,就可以爲該參數傳遞HourlyEmployee類型的值,另外,可以將HourlyEmployee類的對象賦給Employee類型的變量。

  • 注意:不可以將Employee對象賦給HourlyEmployee類型的變量,畢竟Employee不一定是HourlyEmployee.

不繼承的函數:

  • 構造函數不繼承
  • 私有成員函數和成員變量不繼承
  • 析構函數不繼承
  • 拷貝構造函數不繼承
  • 賦值操作符不繼承

構造函數和析構函數

  • 基類的構造函數不被派生類繼承,構造函數不能被繼承是有道理的,因爲即使繼承了,它的名字和派生類的名字也不一樣,不能成爲派生類的構造函數,當然更不能成爲普通的成員函數,可在派生類構造函數的定義中調用基類的構造函數
  • 析構函數也不能被繼承。與構造函數不同的是,在派生類的析構函數中不用顯式地調用基類的析構函數,因爲每個類只有一個析構函數,編譯器知道如何選擇,無需程序員干涉。
#include <iostream>
using namespace std;

class A
{
public:
    A()
    {
        cout<<"this is a'contrutor."<<endl;
    }
    ~A()
    {
        cout<<"this is a'destrutor."<<endl;
    }
};
class B:public A
{
public:
    B():A()
    {
        cout<<"this is b'contrutor."<<endl;
    }
    ~B()
    {
        cout<<"this is B'destrutor."<<endl;
    }
};
int main()
{
    A a1;
    cout<<"********"<<endl;
    B b1;
    return 0;
}

  • 執行順序:析構函數的執行順序和構造函數的執行順序相反

創建派生類對象時,構造函數的執行順序和繼承順序相同,即先執行基類構造函數,再執行派生類構造函數

而銷燬派生類對象時,析構函數的執行順序和繼承順序相反,即先執行派生類析構函數,再執行基類析構函數

私有成員函數和成員變量

  • 假如類的私有成員變量可在派生類的成員函數定義中訪問,這違背了其保密性,因此,除了通過基類的接口函數進行訪問,否則不能直接訪問基類的成員函數和成員變量
  • 私有成員函數只應作爲輔助函數使用,所以只應在定義他們的類中使用。

賦值操作符和拷貝構造函數

  • 重載的賦值操作符和拷貝構造函數不會被繼承,然而,他們可以在派生的重載賦值操作符以及拷貝構造函數的定義中使用。
  • 重載賦值操作符必須定義爲類的成員函數。
  • 爲什麼賦值運算符重載函數不能被繼承呢?

     因爲相較於基類,派生類往往要添加一些自己的數據成員和成員函數,如果允許派生類繼承基類的賦值運算符重載函數,那麼,在派生類不提供自己的賦值運算符重載函數時,就只能調用基類的,但基類版本只能處理基類的數據成員,在這種情況下,派生類自己的數據成員怎麼辦?

參考:

https://blog.csdn.net/u010177010/article/details/51014207

https://www.cnblogs.com/zpcdbky/p/5027481.html

protected限定符

在類的成員變量或成員函數之前,如果添加了限定符protected,那麼除了派生類之外的其他任何類或函數,它的作用等同於private,但在派生類中,可以通過名稱直接訪問這些成員變量

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章