国产 无码 综合区,色欲AV无码国产永久播放,无码天堂亚洲国产AV,国产日韩欧美女同一区二区

C++ 面向?qū)ο蠛诵?繼承、權(quán)限、多態(tài)、抽象類(lèi))

這篇具有很好參考價(jià)值的文章主要介紹了C++ 面向?qū)ο蠛诵?繼承、權(quán)限、多態(tài)、抽象類(lèi))。希望對(duì)大家有所幫助。如果存在錯(cuò)誤或未考慮完全的地方,請(qǐng)大家不吝賜教,您也可以點(diǎn)擊"舉報(bào)違法"按鈕提交疑問(wèn)。

1. 繼承

1.1概念

繼承(Inheritance)是面向?qū)ο缶幊讨械囊粋€(gè)重要概念,它允許一個(gè)類(lèi)(稱(chēng)為派生類(lèi)或子類(lèi))從另一個(gè)類(lèi)(稱(chēng)為基類(lèi)或父類(lèi))繼承屬性和方法。繼承是實(shí)現(xiàn)類(lèi)之間的關(guān)系,通過(guò)繼承,子類(lèi)可以重用父類(lèi)的代碼,并且可以在此基礎(chǔ)上添加新的功能或修改已有的功能。

在C++中,繼承可以通過(guò)關(guān)鍵字 classstruct 后面的冒號(hào)來(lái)聲明和定義。語(yǔ)法格式如下:

class DerivedClass : accessSpecifier BaseClass {
    // DerivedClass 的成員和方法
};

其中,DerivedClass 是派生類(lèi)的名稱(chēng),BaseClass 是基類(lèi)的名稱(chēng),accessSpecifier 是訪問(wèn)控制符,可以是 public、protectedprivate,用于控制派生類(lèi)對(duì)基類(lèi)成員的訪問(wèn)權(quán)限。

C++ 支持三種繼承方式:

  1. 公有繼承(Public Inheritance):通過(guò) public 關(guān)鍵字聲明,派生類(lèi)可以訪問(wèn)基類(lèi)的公有成員和受保護(hù)成員,基類(lèi)的私有成員對(duì)派生類(lèi)不可見(jiàn)。

  2. 保護(hù)繼承(Protected Inheritance):通過(guò) protected 關(guān)鍵字聲明,派生類(lèi)可以訪問(wèn)基類(lèi)的公有成員和受保護(hù)成員,基類(lèi)的私有成員對(duì)派生類(lèi)不可見(jiàn)。

  3. 私有繼承(Private Inheritance):通過(guò) private 關(guān)鍵字聲明,派生類(lèi)無(wú)法訪問(wèn)基類(lèi)的成員,除了通過(guò)友元關(guān)系。

派生類(lèi)可以繼承基類(lèi)的成員變量和成員函數(shù),包括公有成員、受保護(hù)成員和私有成員。繼承的成員可以通過(guò)派生類(lèi)對(duì)象進(jìn)行訪問(wèn)。

繼承還可以形成類(lèi)的層次結(jié)構(gòu),派生類(lèi)可以再次被其他類(lèi)繼承,形成多層次繼承關(guān)系。繼承鏈上的每個(gè)派生類(lèi)都可以添加新的成員和方法,形成更豐富的功能。

需要注意的是,C++ 支持多繼承,即一個(gè)派生類(lèi)可以同時(shí)繼承多個(gè)基類(lèi)。多繼承的語(yǔ)法類(lèi)似于單繼承,通過(guò)逗號(hào)分隔多個(gè)基類(lèi)。

繼承在面向?qū)ο缶幊讨惺且环N強(qiáng)大的機(jī)制,它提供了代碼重用和擴(kuò)展的能力。通過(guò)繼承,可以建立類(lèi)之間的關(guān)系,并且實(shí)現(xiàn)了多態(tài)性和抽象性等重要概念。

已有的類(lèi)稱(chēng)為父類(lèi)或者基類(lèi)
新的類(lèi)稱(chēng)為子類(lèi)或者派生類(lèi)

#include <iostream>
using namespace std;
class Father{
public:
    string first_name="王";
    void work(){
        cout<<"我是名廚師"<<endl;
    }
    void show(){
        cout<<"姓氏:"<<first_name<<endl;
    }
};
class Son:public Father{

};
int main()
{
    Son s;
    s.show();
    s.work();

}

在父類(lèi)基礎(chǔ)上做更改

#include <iostream>
using namespace std;
class Father{
public:
    string first_name="王";
    void work(){
        cout<<"我是名廚師"<<endl;
    }
    void show(){
        cout<<"姓氏:"<<first_name<<endl;
    }
};
class Son:public Father{
public:
    int age=20;
    void work(){
        cout<<"我是名貨車(chē)司機(jī)"<<endl;
    }
    void show(){
        //調(diào)用基類(lèi)的show()方法 
        Father::show();
        //cout<<"年齡"<<age<<"姓氏:"<<first_name<<endl;
        cout<<"年齡"<<age<<endl;
    }
};
int main()
{
    Son s;
    s.show();
    s.work();
}

1.2 構(gòu)造函數(shù)

構(gòu)造函數(shù)(Constructor)是一種特殊的成員函數(shù),用于在創(chuàng)建對(duì)象時(shí)初始化對(duì)象的狀態(tài)。構(gòu)造函數(shù)的名稱(chēng)與類(lèi)的名稱(chēng)相同,沒(méi)有返回類(lèi)型(包括 void),并且可以帶有參數(shù)。

在C++中,每個(gè)類(lèi)可以定義一個(gè)或多個(gè)構(gòu)造函數(shù)。當(dāng)創(chuàng)建類(lèi)的對(duì)象時(shí),會(huì)自動(dòng)調(diào)用相應(yīng)的構(gòu)造函數(shù)來(lái)初始化對(duì)象的數(shù)據(jù)成員。構(gòu)造函數(shù)可以執(zhí)行必要的初始化操作,如分配內(nèi)存、初始化成員變量、設(shè)置默認(rèn)值等。

構(gòu)造函數(shù)的特點(diǎn)如下:

  1. 與類(lèi)同名:構(gòu)造函數(shù)的名稱(chēng)必須與類(lèi)的名稱(chēng)完全相同。

  2. 沒(méi)有返回類(lèi)型:構(gòu)造函數(shù)沒(méi)有返回類(lèi)型,包括 void。在函數(shù)聲明和定義時(shí)不需要指定返回類(lèi)型。

  3. 可以帶有參數(shù):構(gòu)造函數(shù)可以帶有參數(shù),用于初始化對(duì)象的數(shù)據(jù)成員。參數(shù)可以是任意類(lèi)型,包括基本類(lèi)型、類(lèi)類(lèi)型和用戶(hù)自定義類(lèi)型。

  4. 可以重載:同一個(gè)類(lèi)可以定義多個(gè)構(gòu)造函數(shù),通過(guò)參數(shù)的類(lèi)型和數(shù)量的不同進(jìn)行重載。根據(jù)實(shí)際需要,可以使用不同的構(gòu)造函數(shù)來(lái)創(chuàng)建對(duì)象。

以下是一個(gè)示例,展示了如何定義和使用構(gòu)造函數(shù):

class MyClass {
public:
    // 默認(rèn)構(gòu)造函數(shù)
    MyClass() {
        // 初始化數(shù)據(jù)成員
        value = 0;
    }

    // 帶參數(shù)的構(gòu)造函數(shù)
    MyClass(int num) {
        // 使用參數(shù)初始化數(shù)據(jù)成員
        value = num;
    }

    // 成員函數(shù)
    void printValue() {
        std::cout << "Value: " << value << std::endl;
    }

private:
    int value;
};

int main() {
    // 使用默認(rèn)構(gòu)造函數(shù)創(chuàng)建對(duì)象
    MyClass obj1;
    obj1.printValue();  // Output: Value: 0

    // 使用帶參數(shù)的構(gòu)造函數(shù)創(chuàng)建對(duì)象
    MyClass obj2(42);
    obj2.printValue();  // Output: Value: 42

    return 0;
}

在上面的示例中,MyClass 類(lèi)定義了兩個(gè)構(gòu)造函數(shù):一個(gè)默認(rèn)構(gòu)造函數(shù)和一個(gè)帶參數(shù)的構(gòu)造函數(shù)。默認(rèn)構(gòu)造函數(shù)沒(méi)有參數(shù),用于初始化對(duì)象的數(shù)據(jù)成員為默認(rèn)值。帶參數(shù)的構(gòu)造函數(shù)接收一個(gè)整數(shù)參數(shù),并用該參數(shù)初始化對(duì)象的數(shù)據(jù)成員。

main() 函數(shù)中,我們使用不同的構(gòu)造函數(shù)創(chuàng)建了兩個(gè) MyClass 對(duì)象 obj1obj2,并調(diào)用 printValue() 成員函數(shù)打印對(duì)象的值。

構(gòu)造函數(shù)在對(duì)象創(chuàng)建過(guò)程中自動(dòng)調(diào)用,可以確保對(duì)象在使用之前處于有效的狀態(tài)。構(gòu)造函數(shù)的重載和參數(shù)化使得可以根據(jù)需要進(jìn)行靈活的對(duì)象初始化。

1.2.1 透?jìng)鳂?gòu)造

透?jìng)鳂?gòu)造(Forwarding Constructors)是C++11引入的一種構(gòu)造函數(shù)技術(shù),它允許一個(gè)類(lèi)的構(gòu)造函數(shù)將參數(shù)透?jìng)鹘o另一個(gè)構(gòu)造函數(shù),從而避免了重復(fù)的代碼編寫(xiě)。

透?jìng)鳂?gòu)造的主要思想是,一個(gè)構(gòu)造函數(shù)可以接受相同的參數(shù)并將它們傳遞給另一個(gè)構(gòu)造函數(shù)來(lái)完成對(duì)象的構(gòu)造。這樣可以減少代碼冗余,提高代碼的可維護(hù)性和重用性。

在C++中,可以使用以下語(yǔ)法來(lái)實(shí)現(xiàn)透?jìng)鳂?gòu)造:

class MyClass {
public:
    // 主構(gòu)造函數(shù)
    MyClass(int num, double val, const std::string& str) {
        // 對(duì)參數(shù)進(jìn)行初始化
        // ...
    }

    // 透?jìng)鳂?gòu)造函數(shù)
    MyClass(int num) : MyClass(num, 0.0, "") {
        // 將參數(shù)透?jìng)鹘o主構(gòu)造函數(shù)
    }

    // 其他成員函數(shù)
    // ...
};

在上面的示例中,MyClass 類(lèi)定義了一個(gè)主構(gòu)造函數(shù)和一個(gè)透?jìng)鳂?gòu)造函數(shù)。主構(gòu)造函數(shù)接受三個(gè)參數(shù),并進(jìn)行相應(yīng)的初始化操作。透?jìng)鳂?gòu)造函數(shù)只接受一個(gè)整數(shù)參數(shù),并將該參數(shù)透?jìng)鹘o主構(gòu)造函數(shù),同時(shí)使用默認(rèn)值來(lái)初始化其他參數(shù)。

通過(guò)透?jìng)鳂?gòu)造函數(shù),我們可以使用不同的參數(shù)列表來(lái)創(chuàng)建對(duì)象,而實(shí)際的初始化工作由主構(gòu)造函數(shù)完成。這樣可以避免在透?jìng)鳂?gòu)造函數(shù)中重復(fù)編寫(xiě)相同的初始化代碼,提高了代碼的可讀性和維護(hù)性。

需要注意的是,透?jìng)鳂?gòu)造函數(shù)只能將參數(shù)透?jìng)鹘o同一個(gè)類(lèi)中的另一個(gè)構(gòu)造函數(shù),不能透?jìng)鹘o父類(lèi)的構(gòu)造函數(shù)或其他類(lèi)的構(gòu)造函數(shù)。

透?jìng)鳂?gòu)造在處理多個(gè)構(gòu)造函數(shù)的情況下非常有用,它簡(jiǎn)化了構(gòu)造函數(shù)的實(shí)現(xiàn),減少了代碼的冗余,并提供了更靈活的對(duì)象初始化方式。

子類(lèi)直接調(diào)用父類(lèi)的構(gòu)造方法

#include <iostream>
using namespace std;
class Father{
private:
    string first_name;
    string sex;
public:
    Father(string first_name){ //父類(lèi)中給出構(gòu)造函數(shù)  默認(rèn)無(wú)參的就不存在
        this->first_name=first_name;
    }
    Father(string first_name,string sex){
        this->first_name=first_name;
        this->sex=sex;
    }
     void show(){
        cout<<"姓氏:"<<first_name<<endl;
    }
};
class Son:public Father{
private:
    int age;
public:
    Son(string n,int a):Father(n),age(a){}
    Son(string first_name,string sex,int age):Father(first_name,sex),age(age){}
    void show(){
        Father::show();
        //cout<<"年齡"<<age<<"姓氏:"<<first_name<<endl;
        cout<<"年齡"<<age<<endl;
    }

};
int main()
{
    Son s("李",20);
    s.show();
    s.work();

    Son s2("王","男",30);
    s2.show();

}

1.2.2 委托構(gòu)造

委托構(gòu)造(Delegating Constructors)是C++11引入的一種構(gòu)造函數(shù)技術(shù),它允許一個(gè)構(gòu)造函數(shù)委托給同一個(gè)類(lèi)的另一個(gè)構(gòu)造函數(shù)完成對(duì)象的初始化。

委托構(gòu)造的主要思想是,一個(gè)構(gòu)造函數(shù)可以調(diào)用同一個(gè)類(lèi)中的其他構(gòu)造函數(shù)來(lái)完成對(duì)象的初始化。這樣可以避免在不同的構(gòu)造函數(shù)中重復(fù)編寫(xiě)相同的初始化代碼。

在C++中,可以使用以下語(yǔ)法來(lái)實(shí)現(xiàn)委托構(gòu)造:

class MyClass {
public:
    // 構(gòu)造函數(shù)1
    MyClass() : MyClass(0, 0.0, "") {
        // 委托給構(gòu)造函數(shù)2
    }

    // 構(gòu)造函數(shù)2
    MyClass(int num, double val, const std::string& str) {
        // 對(duì)參數(shù)進(jìn)行初始化
        // ...
    }

    // 其他成員函數(shù)
    // ...
};

在上面的示例中,MyClass 類(lèi)定義了兩個(gè)構(gòu)造函數(shù):構(gòu)造函數(shù)1和構(gòu)造函數(shù)2。構(gòu)造函數(shù)1通過(guò)使用冒號(hào)初始化列表調(diào)用構(gòu)造函數(shù)2來(lái)完成對(duì)象的初始化。這樣,構(gòu)造函數(shù)1不再需要顯式初始化對(duì)象的數(shù)據(jù)成員,而是將工作委托給構(gòu)造函數(shù)2。

通過(guò)委托構(gòu)造,我們可以避免在多個(gè)構(gòu)造函數(shù)中重復(fù)編寫(xiě)相同的初始化代碼,提高了代碼的可維護(hù)性和重用性。同時(shí),委托構(gòu)造還可以實(shí)現(xiàn)更清晰和簡(jiǎn)潔的構(gòu)造函數(shù)邏輯。

需要注意的是,C++11中引入的委托構(gòu)造要求委托的構(gòu)造函數(shù)在初始化列表中出現(xiàn),而不是在函數(shù)體內(nèi)部。

委托構(gòu)造在處理多個(gè)構(gòu)造函數(shù)的情況下非常有用,它簡(jiǎn)化了構(gòu)造函數(shù)的實(shí)現(xiàn),減少了代碼的冗余,并提供了更靈活的對(duì)象初始化方式。

一個(gè)類(lèi)中構(gòu)造函數(shù)訪問(wèn)自己另一個(gè)構(gòu)造函數(shù)。相當(dāng)于間接的訪問(wèn)父類(lèi)的構(gòu)造函數(shù)。
Son(int a):Son(“王”,a){} —>Son(string n,int a):Father(n),age(a){} -->Father(string first_name)

#include <iostream>
using namespace std;
class Father{
private:
    string first_name;
    string sex;
public:
    Father(string first_name){ //父類(lèi)中給出構(gòu)造函數(shù)  默認(rèn)無(wú)參的就不存在
        this->first_name=first_name;
    }
    Father(string first_name,string sex){
        this->first_name=first_name;
        this->sex=sex;
    }
    void show(){
        cout<<"姓氏:"<<first_name<<endl;
    }
};
class Son:public Father{
private:
    int age;
public:
    Son(int a):Son("王",a){}
    Son(string n,int a):Father(n),age(a){}
    Son(string first_name,string sex,int age):Father(first_name,sex),age(age){}
    void show(){
        Father::show();
        //cout<<"年齡"<<age<<"姓氏:"<<first_name<<endl;
        cout<<"年齡"<<age<<endl;
    }

};
int main()
{

    Son s(20);
    s.show();

}

1.2.3 繼承練習(xí)題

封裝Person 類(lèi)
name,age,sex
給出show方法要輸出上面三條信息

封裝Employee 職員 繼承自Person
salary, work_id
給出show方法要輸出除了Person類(lèi)中基本的情況之外 再輸出salary, work_id

封裝Manager 管理者 繼承自Employee
position 職位
給出show方法要輸出除了Employee類(lèi)中基本的情況之外 再輸出position

#include <iostream>
using namespace std;
class Person{
private:
    string name;
    int age;
    string sex;
public:
    Person(string name,int age,string sex){
        this->name=name;
        this->age=age;
        this->sex=sex;
    }
    void show(){
        cout<<name<<" "<<age<<" "<<sex<<endl;
    }

};
class Employee:public Person{ //職員類(lèi)繼承自Person類(lèi)
private:
    double salary;
    string work_id;
public:
    Employee(string name,int age,string sex,double salary,string work_id):Person(name,age,sex){
        this->salary=salary;
        this->work_id=work_id;
    }
    void show(){
        Person::show();
        cout<<salary<<" "<<work_id<<endl;
    }

};
class Manager:public Employee{
private:
    string position;
public:
    //指明直接基類(lèi)的構(gòu)造函數(shù)函數(shù)如何調(diào)用
    Manager(string name,int age,string sex,double salary,string work_id,string position)
        :Employee(name,age,sex,salary,work_id)
    {
        this->position=position;

    }
    void show(){
        Employee::show();
        cout<<position<<endl;

    }

};
int main()
{
  Manager m("老吳",40,"男",8000,"007","經(jīng)理");
  m.show();
}

1.3 對(duì)象的創(chuàng)建與銷(xiāo)毀

在C++中,對(duì)象的創(chuàng)建和銷(xiāo)毀是通過(guò)構(gòu)造函數(shù)和析構(gòu)函數(shù)來(lái)完成的。

  1. 對(duì)象的創(chuàng)建:

    • 靜態(tài)對(duì)象的創(chuàng)建:靜態(tài)對(duì)象是在程序運(yùn)行之前就被創(chuàng)建的,它們存在于程序的整個(gè)生命周期中。靜態(tài)對(duì)象的創(chuàng)建由靜態(tài)存儲(chǔ)區(qū)域負(fù)責(zé)管理,例如全局變量或靜態(tài)成員變量。
    • 棧上對(duì)象的創(chuàng)建:棧上對(duì)象是在函數(shù)內(nèi)部通過(guò)聲明局部變量來(lái)創(chuàng)建的,它們的生命周期與所在的作用域相對(duì)應(yīng)。當(dāng)函數(shù)退出作用域時(shí),棧上對(duì)象會(huì)自動(dòng)被銷(xiāo)毀。
    • 堆上對(duì)象的創(chuàng)建:堆上對(duì)象通過(guò)使用new運(yùn)算符在堆上動(dòng)態(tài)分配內(nèi)存來(lái)創(chuàng)建。堆上對(duì)象的生命周期由程序員手動(dòng)管理,需要通過(guò)delete運(yùn)算符來(lái)顯式釋放對(duì)象所占用的內(nèi)存。
  2. 對(duì)象的銷(xiāo)毀:

    • 析構(gòu)函數(shù):析構(gòu)函數(shù)是在對(duì)象被銷(xiāo)毀時(shí)自動(dòng)調(diào)用的特殊成員函數(shù)。它的主要作用是清理對(duì)象所占用的資源,例如釋放動(dòng)態(tài)分配的內(nèi)存、關(guān)閉文件等。析構(gòu)函數(shù)的名稱(chēng)與類(lèi)的名稱(chēng)相同,以波浪號(hào)~作為前綴,沒(méi)有返回類(lèi)型和參數(shù)。
    • 對(duì)象銷(xiāo)毀的時(shí)機(jī):
      • 靜態(tài)對(duì)象:在程序結(jié)束時(shí)自動(dòng)銷(xiāo)毀。
      • 棧上對(duì)象:當(dāng)對(duì)象所在的作用域結(jié)束時(shí)自動(dòng)銷(xiāo)毀。
      • 堆上對(duì)象:需要程序員手動(dòng)調(diào)用delete運(yùn)算符來(lái)銷(xiāo)毀對(duì)象,并釋放內(nèi)存。

在對(duì)象銷(xiāo)毀的過(guò)程中,析構(gòu)函數(shù)會(huì)按照與構(gòu)造函數(shù)相反的順序被調(diào)用。這意味著先創(chuàng)建的對(duì)象的析構(gòu)函數(shù)會(huì)先被調(diào)用,后創(chuàng)建的對(duì)象的析構(gòu)函數(shù)會(huì)后被調(diào)用。

正確管理對(duì)象的創(chuàng)建和銷(xiāo)毀是編寫(xiě)高質(zhì)量C++代碼的重要部分。通過(guò)適當(dāng)?shù)厥褂脴?gòu)造函數(shù)和析構(gòu)函數(shù),可以確保對(duì)象的正確初始化和資源的及時(shí)釋放,避免內(nèi)存泄漏和資源泄漏的問(wèn)題。

#include <iostream>
using namespace std;
class Father{
private:
    string name;
public:
    Father(string n):name(n){
        cout<<"父類(lèi)的構(gòu)造函數(shù)調(diào)用"<<endl;
     }
    ~Father(){
         cout<<"父類(lèi)的析構(gòu)函數(shù)調(diào)用"<<endl;
    }
};
class Son:public Father{
public:
    Son(string name):Father(name){
        cout<<"子類(lèi)的構(gòu)造函數(shù)調(diào)用"<<endl;
    }
    ~Son(){
        cout<<"子類(lèi)的析構(gòu)函數(shù)調(diào)用"<<endl;
    }

};
int main()
{
    Son s("小明");
}
#include <iostream>
using namespace std;
class Value{
public:
    string str;
    Value(string str){
        this->str=str;
        cout<<this->str<<"創(chuàng)建了"<<endl;

    }
    ~Value(){
        cout<<str<<"銷(xiāo)毀了"<<endl;
    }
};
class Father{

public:
    string name;
    Value v= Value("Father中的對(duì)象成員");
    static Value s_v;  //static對(duì)象成員
    Father(string n):name(n){
        cout<<"父類(lèi)的構(gòu)造函數(shù)調(diào)用"<<endl;
     }
    ~Father(){
         cout<<"父類(lèi)的析構(gòu)函數(shù)調(diào)用"<<endl;
    }
};
Value Father::s_v=Value("Father中的static對(duì)象成員");

class Son:public Father{

public:
    Value v=Value("Son中的對(duì)象成員");
    static Value s_v;
    Son(string name):Father(name){
        cout<<"子類(lèi)的構(gòu)造函數(shù)調(diào)用"<<endl;
    }
    ~Son(){
        cout<<"子類(lèi)的析構(gòu)函數(shù)調(diào)用"<<endl;
    }

};
Value Son::s_v=Value("Son中的static對(duì)象成員");
int main()
{
    cout<<"——————————程序運(yùn)行之前————————"<<endl;
    {
        Son s("小明");
    }
    cout<<"------------"<<endl;
}

1.4 多繼承

1.4.1多繼承

多繼承(Multiple Inheritance)是面向?qū)ο缶幊讨械囊环N概念,指的是一個(gè)類(lèi)可以從多個(gè)父類(lèi)中繼承屬性和行為。

在C++中,多繼承允許一個(gè)類(lèi)派生自多個(gè)基類(lèi),通過(guò)使用逗號(hào)分隔的形式來(lái)指定多個(gè)基類(lèi)。例如:

class Derived : public Base1, public Base2 {
    // 類(lèi)成員和函數(shù)
};

在上面的示例中,Derived 類(lèi)通過(guò) public 訪問(wèn)修飾符從 Base1Base2 兩個(gè)類(lèi)中進(jìn)行多繼承。這意味著 Derived 類(lèi)將繼承 Base1Base2 類(lèi)中的所有公有成員和函數(shù)。

多繼承可以帶來(lái)一些優(yōu)勢(shì),如代碼重用、靈活性和多態(tài)性。通過(guò)從多個(gè)基類(lèi)中繼承,一個(gè)派生類(lèi)可以擁有多個(gè)父類(lèi)的特性,并具備更強(qiáng)大的功能。

然而,多繼承也帶來(lái)了一些挑戰(zhàn)和潛在的問(wèn)題。其中一個(gè)主要問(wèn)題是命名沖突,當(dāng)多個(gè)父類(lèi)具有同名的成員時(shí),派生類(lèi)需要明確指定使用哪個(gè)父類(lèi)的成員。此外,多繼承也增加了類(lèi)的復(fù)雜性和理解難度。

為了避免多繼承帶來(lái)的問(wèn)題,需要謹(jǐn)慎設(shè)計(jì)和使用多繼承,確保派生類(lèi)在繼承多個(gè)基類(lèi)時(shí)能夠保持清晰、可維護(hù)和易于理解的結(jié)構(gòu)。

需要注意的是,多繼承在實(shí)際開(kāi)發(fā)中并不常見(jiàn),更常見(jiàn)的是單繼承或使用接口(純虛函數(shù))實(shí)現(xiàn)的多態(tài)性。在設(shè)計(jì)類(lèi)的繼承關(guān)系時(shí),應(yīng)根據(jù)具體情況權(quán)衡利弊,并選擇合適的繼承方式。

一個(gè)類(lèi)有兩個(gè)個(gè)或者多個(gè)基類(lèi)叫多繼承,會(huì)獲得多個(gè)基類(lèi)的屬性和方法

#include <iostream>
using namespace std;
class Bed{
public:
    void sleep(){
        cout<<"可以躺著"<<endl;
    }
};
class Sofa{
public:
    void sit(){
        cout<<"可以坐著"<<endl;
    }

};
class SofaBed:public Bed,public Sofa{

};
int main()
{
    SofaBed sf;
    sf.sit();
    sf.sleep();

}

1.4.2 多繼承問(wèn)題

多繼承在面向?qū)ο缶幊讨惺且环N強(qiáng)大的工具,但也帶來(lái)了一些潛在的問(wèn)題和挑戰(zhàn)。以下是一些常見(jiàn)的多繼承問(wèn)題:

  1. 命名沖突:當(dāng)多個(gè)父類(lèi)擁有同名的成員(變量、函數(shù)等)時(shí),派生類(lèi)在訪問(wèn)這些成員時(shí)會(huì)出現(xiàn)沖突。需要通過(guò)作用域解析符(::)明確指定使用哪個(gè)父類(lèi)的成員,或者使用虛擬繼承來(lái)解決沖突。

  2. 菱形繼承(Diamond Inheritance):當(dāng)一個(gè)派生類(lèi)繼承自?xún)蓚€(gè)間接基類(lèi),而這兩個(gè)間接基類(lèi)又共同繼承自同一個(gè)基類(lèi)時(shí),就形成了菱形繼承結(jié)構(gòu)。這種結(jié)構(gòu)可能導(dǎo)致二義性問(wèn)題和內(nèi)存資源的浪費(fèi)。C++通過(guò)虛擬繼承(virtual inheritance)來(lái)解決菱形繼承問(wèn)題,確保只有一份共同基類(lèi)的實(shí)例。

  3. 難以理解和維護(hù):多繼承會(huì)增加類(lèi)的復(fù)雜性,使類(lèi)的層次結(jié)構(gòu)變得更加龐大和復(fù)雜。這可能使代碼更難理解、調(diào)試和維護(hù)。需要謹(jǐn)慎設(shè)計(jì)多繼承關(guān)系,使類(lèi)的層次結(jié)構(gòu)保持簡(jiǎn)潔、清晰和易于理解。

  4. 耦合性增加:多繼承可能導(dǎo)致類(lèi)之間的耦合性增加,因?yàn)榕缮?lèi)同時(shí)繼承了多個(gè)父類(lèi)的特性。修改一個(gè)父類(lèi)可能會(huì)對(duì)多個(gè)派生類(lèi)產(chǎn)生影響,增加了代碼的脆弱性和維護(hù)的困難度。

為了解決多繼承問(wèn)題,可以采取以下幾種策略:

  • 使用虛擬繼承(virtual inheritance)解決菱形繼承問(wèn)題,確保只有一份共同基類(lèi)的實(shí)例。
  • 使用命名空間(namespace)來(lái)解決命名沖突問(wèn)題,將同名的成員放置在不同的命名空間中。
  • 通過(guò)合理的設(shè)計(jì),避免過(guò)度使用多繼承,盡量保持類(lèi)的層次結(jié)構(gòu)的簡(jiǎn)潔和清晰。
  • 使用接口(純虛函數(shù))實(shí)現(xiàn)的多態(tài)性,而不是通過(guò)多繼承來(lái)實(shí)現(xiàn)。

總而言之,多繼承是一項(xiàng)強(qiáng)大的特性,但在使用時(shí)需要謹(jǐn)慎考慮,權(quán)衡利弊。合理設(shè)計(jì)和使用多繼承可以充分發(fā)揮其優(yōu)勢(shì),同時(shí)避免潛在的問(wèn)題和復(fù)雜性。

如果多繼承的兩個(gè)基類(lèi)中有重名的成員時(shí),會(huì)產(chǎn)生二義性.
解決方式:使用基類(lèi)::同名成員 進(jìn)行區(qū)分,明確成員來(lái)源

#include <iostream>
using namespace std;
class Bed{
public:
    void sleep(){
        cout<<"可以躺著"<<endl;
    }
    void positon(){
        cout<<"放在臥室"<<endl;
    }
};
class Sofa{
public:
    void sit(){
        cout<<"可以坐著"<<endl;
    }
    void positon(){
        cout<<"放在客廳"<<endl;
    }
};
class SofaBed:public Bed,public Sofa{

};
int main()
{
    SofaBed sf;
    sf.sit();
    sf.sleep();
    //sf.positon(); //二義性問(wèn)題
    sf.Bed::positon();
    sf.Sofa::positon();
}

1.4.3 菱形繼承

菱形繼承(Diamond Inheritance)是指在多繼承關(guān)系中存在一個(gè)菱形形狀的繼承結(jié)構(gòu)。這種結(jié)構(gòu)通常發(fā)生在一個(gè)派生類(lèi)繼承自?xún)蓚€(gè)不同的父類(lèi),而這兩個(gè)父類(lèi)又共同繼承自同一個(gè)基類(lèi)的情況。

下面是一個(gè)簡(jiǎn)單的示例來(lái)說(shuō)明菱形繼承的概念:

class Base {
public:
    int value;
};

class Derived1 : public Base {
};

class Derived2 : public Base {
};

class Derived3 : public Derived1, public Derived2 {
};

在上面的示例中,Derived1Derived2 類(lèi)分別直接繼承自 Base 類(lèi)。然后,Derived3 類(lèi)通過(guò)多繼承同時(shí)繼承自 Derived1Derived2 類(lèi)。這樣就形成了一個(gè)菱形繼承結(jié)構(gòu)。

菱形繼承可能導(dǎo)致以下問(wèn)題:

  1. 冗余數(shù)據(jù):Derived3 類(lèi)繼承了兩次 Base 類(lèi),因此在 Derived3 對(duì)象中會(huì)有兩份 value 成員變量。這樣會(huì)導(dǎo)致冗余的數(shù)據(jù)存儲(chǔ)和內(nèi)存占用。

  2. 二義性:由于 Derived3 類(lèi)繼承自?xún)蓚€(gè)共同的基類(lèi),當(dāng)訪問(wèn)基類(lèi)的成員時(shí)可能發(fā)生二義性。例如,如果在 Derived3 類(lèi)中訪問(wèn) value 成員變量,編譯器無(wú)法確定是從 Derived1 還是 Derived2 繼承的 value

為了解決菱形繼承帶來(lái)的問(wèn)題,C++提供了虛擬繼承(virtual inheritance)機(jī)制。通過(guò)在派生類(lèi)對(duì)共同基類(lèi)的繼承關(guān)系前加上 virtual 關(guān)鍵字,可以確保在派生類(lèi)中只有一份共同基類(lèi)的實(shí)例。

修改示例代碼如下:

class Base {
public:
    int value;
};

class Derived1 : virtual public Base {
};

class Derived2 : virtual public Base {
};

class Derived3 : public Derived1, public Derived2 {
};

通過(guò)在 Derived1Derived2 類(lèi)的繼承聲明中加上 virtual 關(guān)鍵字,可以解決菱形繼承帶來(lái)的冗余數(shù)據(jù)和二義性問(wèn)題?,F(xiàn)在,在 Derived3 類(lèi)中只會(huì)有一份 value 成員變量,并且通過(guò) Derived3 類(lèi)可以訪問(wèn)到該成員變量。

虛擬繼承機(jī)制是通過(guò)在共同基類(lèi)的子對(duì)象中添加一個(gè)虛擬指針(vptr)來(lái)實(shí)現(xiàn)的。這個(gè)虛擬指針指向一個(gè)虛擬函數(shù)表(vtable),用于解決函數(shù)調(diào)用的二義性問(wèn)題。

例1

一個(gè)類(lèi)的兩個(gè)直接基類(lèi),也擁有一個(gè)共同基類(lèi),叫菱形繼承。這時(shí)也會(huì)產(chǎn)生二義性問(wèn)題
菱形解決二義性可以使用作用域限定符的方式區(qū)分

#include <iostream>
using namespace std;
class Furniture{
public:
    int a=5;
    void show(){
        cout<<"家具類(lèi)的show方法"<<endl;
    }

};
class Bed:public Furniture{
public:
  //繼承一次Furniture成員
};
class Sofa:public Furniture{
public:
  //繼承一次Furniture成員


};
class SofaBed:public Bed,public Sofa{
//這時(shí)SofaBed會(huì)繼承兩次Furniture成員

};
int main()
{
  SofaBed sf;
  sf.Bed::show();
  sf.Sofa::show();
  cout<< &(sf.Bed::a)<<endl; //0x61fe88
  cout<<&(sf.Sofa::a)<<endl; //0x61fe8c

}
例2

菱形繼承也可以通過(guò)虛繼承的方式,解決二義性。虛繼承之后,最上層的基類(lèi)成員相當(dāng)于變成共享的。所有的派生類(lèi)都只獲得一份最上層基類(lèi)(Furniture)的成員。

#include <iostream>
using namespace std;
class Furniture{
public:
    int a=5;
    void show(){
        cout<<"家具類(lèi)的show方法"<<endl;
    }

};
class Bed:virtual public Furniture{
public:
 
 };
class Sofa:virtual public Furniture{
public:
  
};
class SofaBed:public Bed,public Sofa{

};
int main()
{
  SofaBed sf;
//sf.Bed::show();
//sf.Sofa::show();
  sf.show(); //只有一份沒(méi)有二義性

  cout<< &(sf.Bed::a)<<endl; //0x61fe8c
  cout<<&(sf.Sofa::a)<<endl; //0x61fe8c

}

2. 權(quán)限

2.1 不同權(quán)限修飾的成員的訪問(wèn)情況

public 本類(lèi)中可以訪問(wèn) 子類(lèi)中可以訪問(wèn) 全局中可以訪問(wèn)
protected 本類(lèi)中可以訪問(wèn) 子類(lèi)中可以訪問(wèn) 全局不可以訪問(wèn)
private 本類(lèi)中可以訪問(wèn) 子類(lèi)不可以訪問(wèn) 全局不可以訪問(wèn)

public 本類(lèi)中可以訪問(wèn) 子類(lèi)中可以訪問(wèn) 全局中可以訪問(wèn)
protected 本類(lèi)中可以訪問(wèn) 子類(lèi)中可以訪問(wèn) 全局不可以訪問(wèn)
private 本類(lèi)中可以訪問(wèn) 子類(lèi)不可以訪問(wèn) 全局不可以訪問(wèn)
#include <iostream>
using namespace std;
class Father{
public:
    string first_name="王";
protected:
    string car="勞斯萊斯";
private:
    string password="123789";

public:
    void show(){
        cout<<first_name<<" "<<car<<" "<<password<<endl;
    }

};
class Son:public Father{
public:
    void show(){
        cout<<first_name<<endl;
        cout<<car<<endl;
        //cout<<password<<endl; //父類(lèi)中私有的不能訪問(wèn)到
    }

};
int main()
{
    Father f;
    f.show();

    Son s;
    s.show();

    cout<<"-------------"<<endl;
    cout<<f.first_name<<endl;//全局中可以訪問(wèn)類(lèi)中public權(quán)限成員
    //cout<<f.car<<endl;  //全局中不可以訪問(wèn)類(lèi)中protected權(quán)限成員
    //cout<<f.password<<endl; //全局中不可以訪問(wèn)類(lèi)中private權(quán)限成員

}

2.2 不同權(quán)限的繼承

繼承中的權(quán)限有三種

  1. public權(quán)限繼承
  2. protected權(quán)限繼承
  3. private權(quán)限繼承

需要注意:
私有成員能夠繼承,但是不能直接訪問(wèn),需要通過(guò)public接口才可以訪問(wèn)。
各種權(quán)限的繼承都不會(huì)改變基類(lèi)中的私有內(nèi)容的權(quán)限類(lèi)型
繼承并不會(huì)改變基類(lèi)中的權(quán)限,只是繼承過(guò)來(lái)的內(nèi)容的權(quán)限在派生類(lèi)中發(fā)生了變化

2.2.1 public繼承

公有繼承情況下,繼承過(guò)來(lái)的內(nèi)容權(quán)限在子類(lèi)是不變的

#include <iostream>
using namespace std;
class Father{
public:
    string first_name="王";
protected:
    string car="勞斯萊斯";
private:
    string password="123789";

public:
    void show(){
        cout<<first_name<<" "<<car<<" "<<password<<endl;
    }

};
class Son:public Father{
public:
    void show(){
        cout<<first_name<<endl;
        cout<<car<<endl;
        //cout<<password<<endl; //父類(lèi)中私有的不能訪問(wèn)到
    }

};
class GrandSon:public Son{
public:
    void show(){
        cout<<first_name<<endl;
        cout<<car<<endl;
    }
};

int main()
{
  Son s;
  s.show();

  GrandSon gs;
  gs.show();

}

2.2.2 protected 繼承

保護(hù)繼承之后,繼承過(guò)來(lái)的內(nèi)容在子類(lèi)中的權(quán)限全變成protected

#include <iostream>
using namespace std;
class Father{
public:
    string first_name="王";
protected:
    string car="勞斯萊斯";
private:
    string password="123789";

public:
    void show(){
        cout<<first_name<<" "<<car<<" "<<password<<endl;
    }

};
class Son:protected Father{
public:
    void show(){
        cout<<first_name<<endl;
        cout<<car<<endl;
        //cout<<password<<endl; //父類(lèi)中私有的不能訪問(wèn)到
    }

};
class GrandSon:public Son{
public:
    void show(){
        cout<<first_name<<endl;
        cout<<car<<endl;
    }
};

int main()
{
  Son s;
  s.show();
  //cout<<s.first_name<<endl; //父類(lèi)中的public在子類(lèi)中變成protected

  GrandSon gs;
  gs.show();

}

2.2.3 private繼承

私有繼承會(huì)把繼承過(guò)來(lái)的內(nèi)容,在子類(lèi)中都變成私有

#include <iostream>
using namespace std;
class Father{
public:
    string first_name="王";
protected:
    string car="勞斯萊斯";
private:
    string password="123789";

public:
    void show(){
        cout<<first_name<<" "<<car<<" "<<password<<endl;
    }

};
class Son:private Father{ 
public:
    void show(){
        cout<<first_name<<endl;
        cout<<car<<endl;
        //cout<<password<<endl; //父類(lèi)中私有的不能訪問(wèn)到
    }

};
class GrandSon:public Son{ 
public:
    void show(){
        //cout<<first_name<<endl;  Son中first_name變成私有 訪問(wèn)不到
        //cout<<car<<endl;         Son中first_name變成私有 訪問(wèn)不到
    }
};

int main()
{
  Son s;
  s.show();

  GrandSon gs;
  gs.show();

}

3.多態(tài)

多態(tài)(Polymorphism)是面向?qū)ο缶幊痰囊粋€(gè)重要概念,指的是同一類(lèi)型的對(duì)象在不同的情況下表現(xiàn)出不同的行為。

在多態(tài)中,父類(lèi)的指針或引用可以指向子類(lèi)的對(duì)象,通過(guò)調(diào)用相同的方法,可以實(shí)現(xiàn)不同子類(lèi)對(duì)象的不同行為。這樣的特性可以使代碼更加靈活、可擴(kuò)展和可維護(hù)。

多態(tài)有兩種形式:靜態(tài)多態(tài)和動(dòng)態(tài)多態(tài)。

  1. 靜態(tài)多態(tài)(靜態(tài)多態(tài)性,Static Polymorphism)也稱(chēng)為編譯時(shí)多態(tài)或早期綁定(Early Binding),是通過(guò)函數(shù)重載和模板實(shí)現(xiàn)的。在編譯時(shí),根據(jù)函數(shù)的參數(shù)類(lèi)型或模板的實(shí)例化類(lèi)型,確定要調(diào)用的函數(shù)或方法。

  2. 動(dòng)態(tài)多態(tài)(動(dòng)態(tài)多態(tài)性,Dynamic Polymorphism)也稱(chēng)為運(yùn)行時(shí)多態(tài)或晚期綁定(Late Binding),是通過(guò)虛函數(shù)(virtual function)和繼承關(guān)系實(shí)現(xiàn)的。在運(yùn)行時(shí),根據(jù)對(duì)象的實(shí)際類(lèi)型來(lái)決定要調(diào)用的函數(shù)或方法。

動(dòng)態(tài)多態(tài)通過(guò)虛函數(shù)和繼承關(guān)系實(shí)現(xiàn),允許子類(lèi)重寫(xiě)父類(lèi)的虛函數(shù),并在運(yùn)行時(shí)根據(jù)對(duì)象的實(shí)際類(lèi)型調(diào)用相應(yīng)的函數(shù)。這使得可以通過(guò)父類(lèi)的指針或引用調(diào)用子類(lèi)特定的實(shí)現(xiàn),實(shí)現(xiàn)了多態(tài)的特性。

使用多態(tài)的好處包括:

  • 代碼重用:可以通過(guò)父類(lèi)的指針或引用調(diào)用子類(lèi)的方法,實(shí)現(xiàn)代碼的重用,減少重復(fù)的代碼。
  • 靈活性:可以在運(yùn)行時(shí)根據(jù)對(duì)象的實(shí)際類(lèi)型來(lái)決定調(diào)用哪個(gè)函數(shù),實(shí)現(xiàn)不同對(duì)象的不同行為。
  • 擴(kuò)展性:通過(guò)添加新的子類(lèi)來(lái)擴(kuò)展功能,而無(wú)需修改現(xiàn)有的代碼。

要使用多態(tài),需要滿(mǎn)足以下條件:

  1. 使用繼承關(guān)系:子類(lèi)繼承自父類(lèi),子類(lèi)可以重寫(xiě)父類(lèi)的虛函數(shù)。
  2. 聲明虛函數(shù):在父類(lèi)中聲明虛函數(shù),使用 virtual 關(guān)鍵字進(jìn)行修飾。
  3. 使用父類(lèi)的指針或引用:通過(guò)父類(lèi)的指針或引用指向子類(lèi)的對(duì)象,來(lái)實(shí)現(xiàn)多態(tài)的調(diào)用。

總而言之,多態(tài)是面向?qū)ο缶幊讨兄匾母拍?,通過(guò)靜態(tài)多態(tài)和動(dòng)態(tài)多態(tài)可以實(shí)現(xiàn)代碼的靈活性和可擴(kuò)展性。動(dòng)態(tài)多態(tài)通過(guò)虛函數(shù)和繼承關(guān)系實(shí)現(xiàn),在運(yùn)行時(shí)根據(jù)對(duì)象的實(shí)際類(lèi)型來(lái)決定調(diào)用哪個(gè)函數(shù)。

總結(jié)如下
一個(gè)接口,多種狀態(tài)。程序運(yùn)行的時(shí)候選擇決定調(diào)用相應(yīng)的代碼
靜態(tài)多態(tài):運(yùn)算符重載,函數(shù)重載 在編譯的時(shí)候就已經(jīng)確定。
動(dòng)態(tài)多態(tài)的條件:
1.公有繼承
2.基類(lèi)指針或者基類(lèi)引用指向派生類(lèi)對(duì)象
3.派生類(lèi)覆蓋基類(lèi)中的虛函數(shù)

3.1 沒(méi)有使用多態(tài)的情況

在編譯的時(shí)候就已經(jīng)確定參數(shù)類(lèi)型,運(yùn)行的時(shí)候直接執(zhí)行參數(shù)類(lèi)型中的方法

#include <iostream>
using namespace std;
class Animla{
public:
   void eat(){
        cout<<"吃東西"<<endl;
   }

};
class Cat:public Animla{
public:
     void eat(){
        cout<<"吃魚(yú)"<<endl;
    }
};
class Dog:public Animla{
public:
     void eat(){
        cout<<"吃肉"<<endl;
    }
};


void test(Animla& a){
    a.eat();
}
int main()
{
    Dog d;
    test(d); //吃東西;
    Cat c;
    test(c); //吃東西;
}

3.2 使用多態(tài)的情況

函數(shù)覆蓋需要用到虛函數(shù)。虛函數(shù)就是virtual關(guān)鍵字修飾的函數(shù)
函數(shù)覆蓋和虛函數(shù)的一些特點(diǎn):
1.虛函數(shù)具有傳遞性,如果重寫(xiě)父類(lèi)中的虛函數(shù),子類(lèi)加不加virtual關(guān)鍵字都是虛函數(shù)。
2.構(gòu)造函數(shù)和靜態(tài)函數(shù)不能定義成虛函數(shù)。
3.虛函數(shù)聲明和定義分離時(shí),關(guān)鍵字只需要加在聲明處

 重寫(xiě):函數(shù)的名稱(chēng) 和參數(shù)列表要完全一致。并且返回值一致或者能自動(dòng)轉(zhuǎn)換

運(yùn)行時(shí)根據(jù)傳入對(duì)象的類(lèi)型,來(lái)判斷應(yīng)該執(zhí)行哪個(gè)對(duì)象里的方法

#include <iostream>
using namespace std;
class Animla{
public:
  virtual void eat(){
        cout<<"吃東西"<<endl;
   }

};
class Cat:public Animla{
public:
     void eat(){
        cout<<"吃魚(yú)"<<endl;
    }
};
class Dog:public Animla{
public:
    virtual void eat(){
        cout<<"吃肉"<<endl;
    }
};
//void test(Cat& c){
//    c.eat();
//}
//void test(Dog& d){
//    d.eat();
//}

void test(Animla& a){
    a.eat();
}
void test2(Animla * a){
    a->eat();
}
int main()
{
    Dog d;
    test(d); //吃肉

    Cat c;
    test(c); //吃魚(yú)
    
    test2(&c);//吃魚(yú)
}

3.3 多態(tài)原理

多態(tài)的原理是基于虛函數(shù)(virtual function)和動(dòng)態(tài)綁定(dynamic binding)的機(jī)制實(shí)現(xiàn)的。

在多態(tài)中,當(dāng)一個(gè)父類(lèi)的指針或引用指向一個(gè)子類(lèi)的對(duì)象時(shí),通過(guò)調(diào)用相同的函數(shù)名,可以實(shí)現(xiàn)對(duì)不同子類(lèi)對(duì)象的不同行為。這是因?yàn)楦割?lèi)中的函數(shù)被聲明為虛函數(shù),子類(lèi)可以重寫(xiě)(覆蓋)這些虛函數(shù),以提供自己的實(shí)現(xiàn)。

在C++中,當(dāng)使用虛函數(shù)時(shí),編譯器為每個(gè)含有虛函數(shù)的類(lèi)生成一個(gè)虛函數(shù)表(vtable),這個(gè)表存儲(chǔ)了每個(gè)虛函數(shù)的地址。同時(shí),每個(gè)對(duì)象也會(huì)存儲(chǔ)一個(gè)指向其所屬類(lèi)的虛函數(shù)表的指針,這個(gè)指針?lè)Q為虛函數(shù)指針(vptr)。

當(dāng)通過(guò)父類(lèi)的指針或引用調(diào)用虛函數(shù)時(shí),實(shí)際調(diào)用的是虛函數(shù)表中對(duì)應(yīng)的函數(shù)。編譯器在編譯時(shí)并不確定具體調(diào)用哪個(gè)函數(shù),而是在運(yùn)行時(shí)根據(jù)對(duì)象的實(shí)際類(lèi)型來(lái)決定調(diào)用哪個(gè)函數(shù)。這個(gè)過(guò)程稱(chēng)為動(dòng)態(tài)綁定(dynamic binding)或后期綁定(late binding)。

動(dòng)態(tài)綁定的過(guò)程如下:

  1. 當(dāng)使用父類(lèi)的指針或引用調(diào)用虛函數(shù)時(shí),先根據(jù)指針或引用的靜態(tài)類(lèi)型(父類(lèi)類(lèi)型)找到對(duì)應(yīng)的虛函數(shù)表。
  2. 然后根據(jù)指針或引用的動(dòng)態(tài)類(lèi)型(實(shí)際指向的子類(lèi)對(duì)象類(lèi)型)在虛函數(shù)表中查找對(duì)應(yīng)的虛函數(shù)。
  3. 最后調(diào)用找到的虛函數(shù)。

通過(guò)動(dòng)態(tài)綁定,即使使用父類(lèi)的指針或引用,也能夠在運(yùn)行時(shí)調(diào)用到子類(lèi)的實(shí)現(xiàn),實(shí)現(xiàn)了多態(tài)的特性。

需要注意的是,只有通過(guò)指針或引用調(diào)用虛函數(shù)才會(huì)觸發(fā)動(dòng)態(tài)綁定,直接通過(guò)對(duì)象調(diào)用虛函數(shù)會(huì)導(dǎo)致靜態(tài)綁定,即根據(jù)對(duì)象的靜態(tài)類(lèi)型確定調(diào)用的函數(shù)。

總結(jié)起來(lái),多態(tài)的原理是通過(guò)虛函數(shù)和動(dòng)態(tài)綁定機(jī)制實(shí)現(xiàn)的。虛函數(shù)表存儲(chǔ)了每個(gè)虛函數(shù)的地址,通過(guò)指針或引用的動(dòng)態(tài)類(lèi)型在虛函數(shù)表中查找對(duì)應(yīng)的虛函數(shù),并在運(yùn)行時(shí)調(diào)用正確的函數(shù)實(shí)現(xiàn)。這使得通過(guò)父類(lèi)的指針或引用可以實(shí)現(xiàn)對(duì)不同子類(lèi)對(duì)象的不同行為。

3.4 多態(tài)的問(wèn)題

形成多態(tài)可能造成內(nèi)部泄漏

#include <iostream>
using namespace std;
class Animla{
public:
   virtual void eat(){
        cout<<"吃東西"<<endl;
   }
   ~Animla(){
        cout<<"Animal的析構(gòu)函數(shù)"<<endl;
    }

};
class Cat:public Animla{
public:
     void eat(){
        cout<<"吃魚(yú)"<<endl;
    }
     ~Cat(){
         cout<<"Cat的析構(gòu)函數(shù)"<<endl;
     }
};
class Dog:public Animla{
public:
     void eat(){
        cout<<"吃肉"<<endl;
    }
    ~Dog(){
        cout<<"Dog的析構(gòu)函數(shù)"<<endl;
    }
};
int main()
{
   Dog * d=new Dog;
   delete d;  //先調(diào)用dog類(lèi)的析構(gòu) 再Anima的析構(gòu)函數(shù)

   Animla * a=new Cat;
   delete a;    //只會(huì)Animal的析構(gòu)函數(shù)

}

解決方式 :需要在基類(lèi)的析構(gòu)函數(shù)前加virtual關(guān)鍵字

#include <iostream>
using namespace std;
class Animla{
public:
   virtual void eat(){
        cout<<"吃東西"<<endl;
   }
   virtual ~Animla(){
        cout<<"Animal的析構(gòu)函數(shù)"<<endl;
    }

};
class Cat:public Animla{
public:
     void eat(){
        cout<<"吃魚(yú)"<<endl;
    }
     ~Cat(){
         cout<<"Cat的析構(gòu)函數(shù)"<<endl;
     }
};
class Dog:public Animla{
public:
     void eat(){
        cout<<"吃肉"<<endl;
    }
    ~Dog(){
        cout<<"Dog的析構(gòu)函數(shù)"<<endl;
    }
};
int main()
{
   Dog * d=new Dog;
   delete d;

   Animla * a=new Cat;
   delete a;
}

4. 抽象類(lèi)

抽象類(lèi)(Abstract Class)是一種特殊的類(lèi),它不能被實(shí)例化,只能作為其他類(lèi)的基類(lèi)來(lái)使用。抽象類(lèi)用于定義一組相關(guān)的接口和行為,但無(wú)法直接創(chuàng)建對(duì)象。

在C++中,通過(guò)在類(lèi)中聲明純虛函數(shù)(Pure Virtual Function)來(lái)定義抽象類(lèi)。純虛函數(shù)是一種在基類(lèi)中聲明但沒(méi)有具體實(shí)現(xiàn)的虛函數(shù),通過(guò)在函數(shù)聲明的末尾使用 “= 0” 來(lái)表示。

抽象類(lèi)的主要目的是為了提供一個(gè)通用的接口,而具體的實(shí)現(xiàn)則由派生類(lèi)來(lái)完成。派生類(lèi)必須實(shí)現(xiàn)基類(lèi)中的純虛函數(shù),否則它也會(huì)成為一個(gè)抽象類(lèi)。通過(guò)這種方式,抽象類(lèi)可以起到一種約束和規(guī)范的作用。

下面是一個(gè)示例代碼來(lái)說(shuō)明抽象類(lèi)的定義和使用:

class Animal {
public:
    virtual void sound() = 0; // 純虛函數(shù),定義了抽象類(lèi)Animal的接口
};

class Dog : public Animal {
public:
    void sound() override {
        cout << "Woof!" << endl; // 實(shí)現(xiàn)了抽象類(lèi)Animal的純虛函數(shù)
    }
};

class Cat : public Animal {
public:
    void sound() override {
        cout << "Meow!" << endl; // 實(shí)現(xiàn)了抽象類(lèi)Animal的純虛函數(shù)
    }
};

int main() {
    Animal* animal1 = new Dog();
    Animal* animal2 = new Cat();

    animal1->sound(); // 輸出: Woof!
    animal2->sound(); // 輸出: Meow!

    delete animal1;
    delete animal2;

    return 0;
}

在上面的示例中,Animal 是一個(gè)抽象類(lèi),它聲明了純虛函數(shù) sound(),用于定義動(dòng)物的聲音。DogCat 類(lèi)是 Animal 類(lèi)的派生類(lèi),它們必須實(shí)現(xiàn) sound() 函數(shù),否則也會(huì)成為抽象類(lèi)。

main() 函數(shù)中,通過(guò) Animal 類(lèi)的指針創(chuàng)建了 DogCat 對(duì)象,并調(diào)用了 sound() 函數(shù)。由于 sound() 函數(shù)是虛函數(shù),并且在派生類(lèi)中進(jìn)行了實(shí)現(xiàn),所以在運(yùn)行時(shí)會(huì)根據(jù)對(duì)象的實(shí)際類(lèi)型調(diào)用相應(yīng)的函數(shù)實(shí)現(xiàn)。

需要注意的是,抽象類(lèi)不能被實(shí)例化,因此不能創(chuàng)建抽象類(lèi)的對(duì)象。但可以通過(guò)抽象類(lèi)的指針或引用來(lái)調(diào)用派生類(lèi)的實(shí)現(xiàn)。抽象類(lèi)提供了一種統(tǒng)一的接口,可以方便地?cái)U(kuò)展和實(shí)現(xiàn)多態(tài)的特性。

只是一個(gè)抽象的概念,目的是給派生類(lèi)寫(xiě)一個(gè)框架。抽象類(lèi)不能實(shí)例化對(duì)象,抽象類(lèi)不能做函數(shù)參數(shù)和函數(shù)返回值。
一個(gè)類(lèi)中有純虛函數(shù)是就是抽象類(lèi)
抽象類(lèi)中一定有純虛函數(shù)函數(shù)

#include <iostream>
using namespace std;
class Shape{
public:
    virtual void area()=0;
    virtual void perimeter()=0;
};
int main()
{
    Shape s; //錯(cuò)誤 抽象類(lèi)不能實(shí)例化對(duì)象

}

如果繼承抽象類(lèi),需要把抽象類(lèi)中純虛方法全部實(shí)現(xiàn),如果沒(méi)有全部實(shí)現(xiàn),該派生類(lèi)還是抽象類(lèi),不能實(shí)例化對(duì)象文章來(lái)源地址http://www.zghlxwxcb.cn/news/detail-481702.html

#include <iostream>
using namespace std;
class Shape{
public:
    virtual void area()=0;
    virtual void perimeter()=0;
};
class Rectangle:public Shape{
public:
    void area(){
        cout<<"長(zhǎng)方形的面積"<<endl;
    }
    void perimeter(){
        cout<<"長(zhǎng)方形的周長(zhǎng)"<<endl;
    }
};
int main()
{
    Rectangle s;
    s.area();
    s.perimeter();

}

#include <iostream>
using namespace std;
//Role 派生出soldier  ADC  AP
//寫(xiě)一個(gè)公共接口test() 不同的的對(duì)象可以作為參數(shù)傳到test()接口
//不同類(lèi)型對(duì)象執(zhí)行attack()攻擊方法的實(shí)現(xiàn)是不同的
class Role{
public:
    virtual void attack(){
        cout<<"角色進(jìn)行攻擊"<<endl;
    }
};
class soldier:public Role{
    void attack(){
        cout<<"戰(zhàn)士進(jìn)行攻擊"<<endl;
    }
};
class ADC:public Role{
    void attack(){
        cout<<"ADC進(jìn)行攻擊"<<endl;
    }

};
class AP:public Role{
    void attack(){
        cout<<"AP進(jìn)行攻擊"<<endl;
    }

};
void test(Role & r){
    r.attack();
}
void test2(Role * r){
    r->attack();
}
int main()
{
    ADC adc;
    soldier s;
    AP ap;
    test(adc);
    test(s);
    test(ap);

    ADC * adc2=new ADC; //基類(lèi)的指針指向派生類(lèi)對(duì)象
    test2(adc2);
}

到了這里,關(guān)于C++ 面向?qū)ο蠛诵?繼承、權(quán)限、多態(tài)、抽象類(lèi))的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!

本文來(lái)自互聯(lián)網(wǎng)用戶(hù)投稿,該文觀點(diǎn)僅代表作者本人,不代表本站立場(chǎng)。本站僅提供信息存儲(chǔ)空間服務(wù),不擁有所有權(quán),不承擔(dān)相關(guān)法律責(zé)任。如若轉(zhuǎn)載,請(qǐng)注明出處: 如若內(nèi)容造成侵權(quán)/違法違規(guī)/事實(shí)不符,請(qǐng)點(diǎn)擊違法舉報(bào)進(jìn)行投訴反饋,一經(jīng)查實(shí),立即刪除!

領(lǐng)支付寶紅包贊助服務(wù)器費(fèi)用

相關(guān)文章

  • C++核心編程—類(lèi)和對(duì)象,類(lèi)的三大特性——封裝、繼承、多態(tài)

    C++核心編程—類(lèi)和對(duì)象,類(lèi)的三大特性——封裝、繼承、多態(tài)

    縱有疾風(fēng)起,人生不言棄。本文篇幅較長(zhǎng),如有錯(cuò)誤請(qǐng)不吝賜教,感謝支持。 ①什么是對(duì)象? 生活中有各種各樣的 事物 ,如人、動(dòng)物、植物等在C++中將這些稱(chēng)為對(duì)象。 對(duì)象多種多樣, 各種對(duì)象的屬性也不相同 。 例如狗的品種,毛色,年齡等 各個(gè)對(duì)象都有自己的行為 ,例

    2024年02月07日
    瀏覽(29)
  • c、c++、java、python、js對(duì)比【面向?qū)ο?、過(guò)程;解釋、編譯語(yǔ)言;封裝、繼承、多態(tài)】

    c、c++、java、python、js對(duì)比【面向?qū)ο?、過(guò)程;解釋、編譯語(yǔ)言;封裝、繼承、多態(tài)】

    目錄 內(nèi)存管理、適用 區(qū)別 C 手動(dòng)內(nèi)存管理:C語(yǔ)言沒(méi)有內(nèi)置的安全檢查機(jī)制,容易出現(xiàn)內(nèi)存泄漏、緩沖區(qū)溢出等安全問(wèn)題。 適用于系統(tǒng)級(jí)編程 C++ 手動(dòng)內(nèi)存管理:C++需要程序員手動(dòng)管理內(nèi)存,包括分配和釋放內(nèi)存,這可能導(dǎo)致內(nèi)存泄漏和指針錯(cuò)誤。 適用于游戲引擎和系統(tǒng)級(jí)編

    2024年02月08日
    瀏覽(51)
  • 面向?qū)ο蟆鄳B(tài)、抽象類(lèi)、接口

    面向?qū)ο蟆鄳B(tài)、抽象類(lèi)、接口

    學(xué)習(xí)資料來(lái)自:黑馬程序員,內(nèi)容僅為學(xué)習(xí)記錄,侵刪 多態(tài) 多態(tài):事務(wù)存在的多種形態(tài) 多態(tài)的前提:1、有繼承關(guān)系;2、重寫(xiě)父類(lèi)方法;3、父類(lèi)引用指向子類(lèi)對(duì)象 面向?qū)ο?面向?qū)ο蠖鄳B(tài)中成員訪問(wèn)特點(diǎn) 1.成員方法(動(dòng)態(tài)綁定):編譯看左邊(父類(lèi)),運(yùn)行看右邊(子類(lèi))

    2024年02月08日
    瀏覽(28)
  • 面向?qū)ο笤斀?,面向?qū)ο蟮娜筇卣鳎悍庋b、繼承、多態(tài)

    面向?qū)ο笤斀?,面向?qū)ο蟮娜筇卣鳎悍庋b、繼承、多態(tài)

    一、面向?qū)ο笈c面向過(guò)程 面向?qū)ο缶幊?(Object-Oriented Programming,簡(jiǎn)稱(chēng)OOP)和 面向過(guò)程編程 (Procedural Programming,簡(jiǎn)稱(chēng)PP)是兩種不同的 編程范式 。 面向?qū)ο缶幊虖?qiáng)調(diào)把問(wèn)題分解成對(duì)象,通過(guò)封裝、繼承和多態(tài)等機(jī)制,來(lái)處理對(duì)象之間的關(guān)系 。每個(gè)對(duì)象都可以獨(dú)立地處理自

    2024年02月21日
    瀏覽(23)
  • 02 面向?qū)ο? 繼承,抽象類(lèi))

    強(qiáng)調(diào):一定用自己的話(huà)總結(jié),避免抄文檔,否則視為作業(yè)未完成。 this的作用 為了解決成員變量和局部變量所存在的二義性,適用于有參構(gòu)造時(shí)使用 示例 為成員變量設(shè)置值, 構(gòu)造器和setter方法的選擇,為什么? 構(gòu)造器:在知道具體的參數(shù)的時(shí)候可以使用,可以創(chuàng)建對(duì)象并做

    2024年02月06日
    瀏覽(29)
  • 面向?qū)ο?類(lèi)/繼承/封裝/多態(tài))詳解

    面向?qū)ο?類(lèi)/繼承/封裝/多態(tài))詳解

    面向?qū)ο缶幊蹋∣bject-Oriented Programming,OOP)是一種廣泛應(yīng)用于軟件開(kāi)發(fā)的編程范式。它基于一系列核心概念,包括類(lèi)、繼承、封裝和多態(tài)。在這篇詳細(xì)的解釋中,我們將探討這些概念,并說(shuō)明它們?nèi)绾卧赑HP等編程語(yǔ)言中實(shí)現(xiàn)。 類(lèi)是OOP的基礎(chǔ)。它是一種用于創(chuàng)建對(duì)象的藍(lán)圖或模

    2024年02月08日
    瀏覽(24)
  • Java面向?qū)ο?- 封裝、繼承和多態(tài)

    目錄 第1關(guān):什么是封裝,如何使用封裝 第2關(guān):什么是繼承,怎樣使用繼承 第3關(guān):super的使用 第4關(guān):方法的重寫(xiě)與重載 第5關(guān):抽象類(lèi) 第6關(guān):final的理解與使用 第7關(guān):接口 第8關(guān):什么是多態(tài),怎么使用多態(tài) Java_Educoder

    2024年02月07日
    瀏覽(22)
  • 第十一單元 面向?qū)ο笕豪^承與多態(tài)

    假設(shè)老師類(lèi)設(shè)計(jì)如下: 學(xué)生類(lèi)設(shè)計(jì)如下: 我們秉承著,讓最簡(jiǎn)潔的代碼,實(shí)現(xiàn)最最強(qiáng)大的功能原則,能否讓上述案例中的重復(fù)代碼進(jìn)行優(yōu)化呢?我們能否將學(xué)生類(lèi)與老師類(lèi)再進(jìn)行抽象,得到一個(gè)人類(lèi)?這章節(jié)學(xué)習(xí)繼承與多態(tài)。 繼承是面向?qū)ο蟪绦蛟O(shè)計(jì)中最重要的概念之一。

    2024年02月06日
    瀏覽(20)
  • 什么是面向?qū)ο螅娜齻€(gè)基本特征:封裝、繼承、多態(tài)

    什么是面向?qū)ο?,它的三個(gè)基本特征:封裝、繼承、多態(tài)

    什么是面向?qū)ο笏枷耄恳呀?jīng)學(xué)完了java確不知道如何跟別人解釋面向?qū)ο笫鞘裁匆馑歼@很常見(jiàn)。讓我們一起來(lái)回顧下這個(gè)奇思妙想~ 現(xiàn)在越來(lái)越多的高級(jí)語(yǔ)言流行起來(lái)了,如大家耳熟能詳?shù)腸++,python,java等,這些都是基于 面向?qū)ο?的語(yǔ)言 而最最基礎(chǔ)的,學(xué)校必學(xué)的語(yǔ)言----c語(yǔ)

    2024年02月02日
    瀏覽(26)
  • Python面向?qū)ο缶幊蹋ㄒ唬╊?lèi)的基礎(chǔ),關(guān)系,繼承,封裝,多態(tài)

    Python面向?qū)ο缶幊蹋ㄒ唬╊?lèi)的基礎(chǔ),關(guān)系,繼承,封裝,多態(tài)

    類(lèi)的一些理論概念及其應(yīng)用場(chǎng)景等基礎(chǔ)內(nèi)容此處不贅述 目錄 python中一切皆對(duì)象 類(lèi)的定義及基礎(chǔ) 屬性 方法 初始化方法 ?普通方法 類(lèi)之間的關(guān)系 相互調(diào)用 依賴(lài)關(guān)系 關(guān)聯(lián)關(guān)系 組合關(guān)系 三大特征----類(lèi)的繼承 重寫(xiě)父類(lèi)方法 多繼承 混合繼承? 三大特征----封裝 三大特征----多態(tài)

    2024年02月10日
    瀏覽(30)

覺(jué)得文章有用就打賞一下文章作者

支付寶掃一掃打賞

博客贊助

微信掃一掃打賞

請(qǐng)作者喝杯咖啡吧~博客贊助

支付寶掃一掃領(lǐng)取紅包,優(yōu)惠每天領(lǐng)

二維碼1

領(lǐng)取紅包

二維碼2

領(lǐng)紅包