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

c++面向?qū)ο笾庋b、繼承、和多態(tài)

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

一、封裝

把客觀事物封裝成類,而且可以把自己的數(shù)據(jù)和方法設(shè)置為只能讓可信的類或者對(duì)象操作,對(duì)不可信的信息進(jìn)行隱藏(利用public,private,protected,friend)實(shí)現(xiàn)

二、繼承

2.1類與類的關(guān)系

  • has-a :描述一個(gè)類由多個(gè)部件類構(gòu)成,一個(gè)類的成員屬性是另一個(gè)已經(jīng)定義好的類。
  • use-a:一個(gè)類使用另一個(gè)類,通過(guò)類之間的成員函數(shù)相互聯(lián)系,定義友元或者通過(guò)傳遞參數(shù)的方式來(lái)實(shí)現(xiàn)。
  • is-a:繼承,關(guān)系具有傳遞性

2.2繼承

讓某個(gè)類型對(duì)象獲得另一個(gè)類型對(duì)象的方法,可以使用現(xiàn)有類的所有功能。有三種繼承方法:

2.2.1 實(shí)現(xiàn)繼承

使用基類的屬性,不需要額外功能

2.2.2 接口繼承

僅使用屬性和方法的名稱,子類提供實(shí)現(xiàn)的能力

2.2.3 可視繼承

子類使用基類外觀和實(shí)現(xiàn)代碼的能力(C++不常用)

2.3 繼承機(jī)制中的對(duì)象之間如何轉(zhuǎn)換?指針和引用之間如何轉(zhuǎn)換?

  • 將派生類指針或引用轉(zhuǎn)換為基類的指針或者引用被稱為向上類型轉(zhuǎn)換,向上類型轉(zhuǎn)換會(huì)自動(dòng)進(jìn)行,而且是類型安全的。
  • 將基類指針或者引用轉(zhuǎn)換為派生類指針或者引用被稱為向下類型轉(zhuǎn)換,向下類型轉(zhuǎn)換不會(huì)自動(dòng)進(jìn)行,因?yàn)橐粋€(gè)基類對(duì)應(yīng)幾個(gè)派生類,向下不知道會(huì)對(duì)應(yīng)哪個(gè)派生類,所以在向下類型轉(zhuǎn)換時(shí)必須加動(dòng)態(tài)類型識(shí)別技術(shù),RTTI技術(shù),用dynamic_cast向下類型轉(zhuǎn)換。

2.4 基類

如果想要將某個(gè)類用作基類,為什么這個(gè)類必須定義而非聲明?

派生類中會(huì)包含并且使用從基類繼承而來(lái)的成員,為了使用這些成員,派生類必須知道他們是什么。

什么是虛擬繼承?

代碼來(lái)源阿秀的學(xué)習(xí)筆記

#include <iostream>
using namespace std;

class A{}
class B : virtual public A{};
class C : virtual public A{};
class D : public B, public C{};

int main()
{
    cout << "sizeof(A):" << sizeof A <<endl; // 1,空對(duì)象,只有一個(gè)占位
    cout << "sizeof(B):" << sizeof B <<endl; // 4,一個(gè)bptr指針,省去占位,不需要對(duì)齊
    cout << "sizeof(C):" << sizeof C <<endl; // 4,一個(gè)bptr指針,省去占位,不需要對(duì)齊
    cout << "sizeof(D):" << sizeof D <<endl; // 8,兩個(gè)bptr,省去占位,不需要對(duì)齊
}

B和C虛擬繼承A,D公有繼承B和C,這種方式是一種菱形繼承或者鉆石繼承。虛擬繼承情況下,無(wú)論基類被繼承多少次,只會(huì)存在一個(gè)實(shí)體。所以D類只會(huì)包含一個(gè)A類對(duì)象,避免重復(fù)成員出現(xiàn),減少了內(nèi)存使用。 虛擬繼承基類的子類中,子類會(huì)增加某種形式的指針,或者指向虛基類子對(duì)象,或者指向一個(gè)相關(guān)的表格;表格中存放的不是虛基類子對(duì)象的地址,就是其偏移量,此類指針被稱為bptr。如果既存在vptr又存在bptr,那么編譯器會(huì)將其優(yōu)化,合并為一個(gè)指針。

當(dāng)一個(gè)類使用虛擬繼承,它的構(gòu)造函數(shù)需要顯示調(diào)用虛基類的構(gòu)造函數(shù),同時(shí)虛基類的構(gòu)造函數(shù)會(huì)在最底層的派生類構(gòu)造函數(shù)中進(jìn)行調(diào)用,而不是在中間類的構(gòu)造函數(shù)中調(diào)用。

2.5 抽象基類為什么不能創(chuàng)建對(duì)象?

因?yàn)槌橄箢惏藳](méi)有定義的純虛函數(shù),所以不能定義抽象類的對(duì)象。

2.6 多繼承

什么是多繼承

C++允許為一個(gè)派生類指定多個(gè)基類,這樣的繼承結(jié)構(gòu)被稱為多重繼承。

優(yōu)點(diǎn)

對(duì)象可以調(diào)用多個(gè)基類中的接口。

缺點(diǎn)

派生類所繼承的多個(gè)基類有相同的基類,派生類對(duì)象需要調(diào)用這個(gè)祖先類的接口方法,就會(huì)出現(xiàn)二義性。

解決方法

  • 加上全局符確定調(diào)用哪一份拷貝。比如pa.Author::eat()調(diào)用屬于Author的拷貝
  • 虛擬繼承

2.7 public,private,protected訪問(wèn)權(quán)限

圖片來(lái)源阿秀的學(xué)習(xí)筆記

c++面向?qū)ο笾庋b、繼承、和多態(tài)
c++面向?qū)ο笾庋b、繼承、和多態(tài)

2.8 組合與繼承的比較

繼承有以下缺點(diǎn):

  • 父類內(nèi)部對(duì)子類不可見(jiàn)
  • 子類從父類繼承的方法在編譯的時(shí)候就確定了,所以無(wú)法在運(yùn)行期間改變父類繼承的方法的行為
  • 如果父類的方法做了修改(比如增加一個(gè)參數(shù)),則子類的方法必須做出相應(yīng)的修改。父類與子類是一種高耦合,違背了面向?qū)ο蟮乃枷搿?/li>

組合

組合就是設(shè)計(jì)類的時(shí)候把要組合的類的對(duì)象加入到這個(gè)類中作為自己的成員變量。

組合的優(yōu)點(diǎn)

  • 當(dāng)前對(duì)象只能通過(guò)所包含的那個(gè)對(duì)象去調(diào)用其方法,所包含的對(duì)象的內(nèi)部細(xì)節(jié)對(duì)當(dāng)前對(duì)象是不可見(jiàn)的。
  • 當(dāng)前對(duì)象與包含的對(duì)象是一個(gè)低耦合的關(guān)系
  • 當(dāng)前對(duì)象可以在運(yùn)行的時(shí)候動(dòng)態(tài)綁定所包含的對(duì)象,通過(guò)set方法給所包含的對(duì)象賦值。
#include <iostream>

class Base {
public:
    virtual void display() = 0;
};

class SubClassA : public Base {
public:
    void display() override {
        std::cout << "This is SubClassA" << std::endl;
    }
};

class SubClassB : public Base {
public:
    void display() override {
        std::cout << "This is SubClassB" << std::endl;
    }
};

class Container {
private:
    Base* containedObject;

public:
    void setContainedObject(Base* object) {
        containedObject = object;
    }

    void displayContainedObject() {
        containedObject->display();
    }
};

int main() {
    SubClassA objA;
    SubClassB objB;

    Container container;

    // 綁定SubClassA對(duì)象到Container中
    container.setContainedObject(&objA);
    container.displayContainedObject();  // 輸出: This is SubClassA

    // 綁定SubClassB對(duì)象到Container中
    container.setContainedObject(&objB);
    container.displayContainedObject();  // 輸出: This is SubClassB

    return 0;
}

組合的缺點(diǎn)

  • 容易產(chǎn)生過(guò)多對(duì)象
  • 為了能組合多個(gè)對(duì)象,必須仔細(xì)對(duì)接口定義。

三、多態(tài)

同一事物表現(xiàn)不同事物的能力。允許將子類類型指針賦值給父類類型指針
多態(tài)有兩種-重載和虛函數(shù)。

3.1 實(shí)現(xiàn)多態(tài)的兩種方法

1、重載(編譯時(shí)多態(tài))

編譯時(shí)多態(tài),允許存在多個(gè)同名函數(shù),而這些函數(shù)的參數(shù)表不同,不能出現(xiàn)參數(shù)個(gè)數(shù)和類型均相同,僅僅依靠返回值不同來(lái)區(qū)分的函數(shù)(參數(shù)個(gè)數(shù)或者參數(shù)類型不同)

重載是在同一范圍定義中的同名成員函數(shù)才存在重載關(guān)系。重載和函數(shù)成員是否是虛函數(shù)沒(méi)有關(guān)系

程序中有函數(shù)重載的時(shí)候函數(shù)的匹配原則和順序是什么?
  • 名字查找
  • 確定候選函數(shù)
  • 尋找最佳匹配
重載運(yùn)算符

只能重載已經(jīng)有的運(yùn)算符。有兩種重載方式:成員運(yùn)算符和非成員運(yùn)算符,成員運(yùn)算符比非成員運(yùn)算符少一個(gè)參數(shù),下標(biāo)運(yùn)算符(通常以所訪問(wèn)元素的引用作為返回值,最好定義下標(biāo)運(yùn)算符的常量版本和非常量版本)、箭頭運(yùn)算符(解引用通常也是類的成員,重載的箭頭運(yùn)算符必須返回類的指針)必須是成員運(yùn)算符。引入運(yùn)算符重載是為了實(shí)現(xiàn)類的多態(tài)性。

  • 示例一
#include <iostream>
using namespace std;

class Complex {
public:
    int real, imag;
    Complex(int r = 0, int i =0) {real = r; imag = i;}
    Complex operator + (Complex const &obj) {
         Complex res;
         res.real = real + obj.real;
         res.imag = imag + obj.imag;
         return res;
    }
    void print() { cout << real << " + i" << imag << endl; }
};

Complex operator - (Complex const &obj1, Complex const &obj2) {
     Complex res;
     res.real = obj1.real - obj2.real;
     res.imag = obj1.imag - obj2.imag;
     return res;
}

int main()
{
    Complex c1(10, 5), c2(2, 4);
    Complex c3 = c1 + c2; 
    c3.print();//12+i9
    Complex c4 = c1 - c2; 
    c4.print();//8+i1
}

  • 示例二
#include <iostream>
using namespace std;

class MyClass {
    int arr[5];
public:
    MyClass() {
        for (int i = 0; i < 5; i++)
            arr[i] = i;
    }
    int operator[](int i) {
        return arr[i];
    }
};

class PtrClass {
    MyClass *ptr;
public:
    PtrClass(MyClass *p = NULL) {
        ptr = p;
    }
    MyClass* operator->() {
        return ptr;
    }
};

int main() {
    MyClass obj;
    PtrClass ptr(&obj);
    cout << obj[2] << endl;//2
    cout << ptr->operator[](2) << endl;//2
}

  • 解引用
    在 C++ 中,解引用運(yùn)算符 * 用于訪問(wèn)指針指向的變量的值。例如,如果您有一個(gè)指向整型變量的指針 p,則可以使用表達(dá)式 *p 來(lái)訪問(wèn)該變量的值。
#include <iostream>
using namespace std;

int main() {
    int x = 5;
    int *p = &x;
    cout << *p << endl;//5
    *p = 10;
    cout << x << endl;//10
}

2、虛函數(shù)(覆蓋)(重寫(xiě))(運(yùn)行時(shí)多態(tài))

運(yùn)行時(shí)多態(tài)。子類重新定義父類的虛函數(shù)。在基類的函數(shù)前加上virtual關(guān)鍵字,在派生類中重寫(xiě)該函數(shù),運(yùn)行的時(shí)候根據(jù)所指對(duì)象是基類還是基類還是派生類來(lái)調(diào)用里面的函數(shù)。

注意
  • 與基類的虛函數(shù)有相同的參數(shù)個(gè)數(shù)
  • 與基類的虛函數(shù)有相同的參數(shù)類型
  • 與基類的虛函數(shù)有相同的返回值類型
final和override關(guān)鍵字
  • override
    這個(gè)關(guān)鍵字的意思是,這個(gè)子類的這個(gè)函數(shù)是重寫(xiě)父類的,如果不小心打錯(cuò)了名字,編譯是不會(huì)通過(guò)的
  • final
    不希望某個(gè)類被繼承或者不希望某個(gè)虛函數(shù)被重寫(xiě),可以在類名和虛函數(shù)后面添加final關(guān)鍵字。添加final關(guān)鍵字之后如果被繼承或者重寫(xiě),編譯器會(huì)報(bào)錯(cuò)
    代碼來(lái)源阿秀的學(xué)習(xí)筆記
class Base
{
    virtual void foo();
};
 
class A : public Base
{
    void foo() final; // foo 被override并且是最后一個(gè)override,在其子類中不可以重寫(xiě)
};

class B final : A // 指明B是不可以被繼承的
{
    void foo() override; // Error: 在A中已經(jīng)被final了
};
 
class C : B // Error: B is final
{
};

虛函數(shù)底層實(shí)現(xiàn)原理

虛表:類中含有virtual關(guān)鍵詞時(shí),系統(tǒng)自動(dòng)生成虛表
虛表指針:含有虛函數(shù)的類實(shí)例化對(duì)象時(shí),對(duì)象地址前四個(gè)字節(jié)存儲(chǔ)指向虛表的指針

所以構(gòu)造函數(shù)不能定義為虛函數(shù),因?yàn)樘摵瘮?shù)對(duì)應(yīng)一個(gè)虛函數(shù)表,類中存儲(chǔ)一個(gè)虛表指針,而此時(shí)還沒(méi)有初始化,所以沒(méi)有虛表指針,無(wú)法找到虛表。

虛表的特征
  • 全局共享,也就是全局只有一個(gè),在編譯的時(shí)候就構(gòu)造完成。
  • 虛表類似于一個(gè)數(shù)組,類對(duì)象中存儲(chǔ)vptr指針,指向虛函數(shù)表。
  • 虛表存儲(chǔ)的是虛函數(shù)的地址即虛函數(shù)表的元素是指向類成員函數(shù)的指針,而類中虛函數(shù)的個(gè)數(shù)在編譯的時(shí)候就可以確定,虛函數(shù)表的大小是在編譯的時(shí)候確定的,不用動(dòng)態(tài)分配內(nèi)存空間。
虛函數(shù)表存放位置

虛函數(shù)表類似于類中的靜態(tài)成員的變量,靜態(tài)成員變量也是全局共享。
測(cè)試結(jié)果顯示:虛函數(shù)表vtable在Linux中存放在可執(zhí)行文件的只讀數(shù)據(jù)段(也就是常量區(qū))中;微軟編譯器將虛函數(shù)表存放在常量段。虛函數(shù)位于代碼區(qū)

虛函數(shù)實(shí)現(xiàn)多態(tài)的過(guò)程
  1. 編譯器發(fā)現(xiàn)基類中有虛函數(shù),自動(dòng)生成一個(gè)虛函數(shù)表,這個(gè)是一個(gè)一維數(shù)組,虛表保存虛函數(shù)入口地址
  2. 編譯器會(huì)在每個(gè)對(duì)象前四個(gè)字節(jié)中保存一個(gè)虛表指針,構(gòu)造時(shí),根據(jù)對(duì)象類型初始化虛表指針。
  3. 派生類定義對(duì)象的時(shí)候,程序運(yùn)行會(huì)自動(dòng)調(diào)用構(gòu)造函數(shù),構(gòu)造子類對(duì)象的時(shí)候,先調(diào)用父類構(gòu)造函數(shù),然后才調(diào)用子類的構(gòu)造函數(shù),為子類對(duì)象初始化虛表指針,令他指向子類虛表
  4. 派生類對(duì)基類沒(méi)有重寫(xiě)的時(shí)候,派生類虛表指針指向基類的虛表,重寫(xiě)的時(shí)候,指向自身的虛表
構(gòu)造函數(shù)或者析構(gòu)函數(shù)能否調(diào)用虛函數(shù)?

派生類對(duì)象構(gòu)造期間進(jìn)入基類的構(gòu)造函數(shù)時(shí),對(duì)象類型變成了基類類型,而不是派生類型,同樣進(jìn)入基類析構(gòu)函數(shù)時(shí),對(duì)象也是基類類型。所以,虛函數(shù)始終僅僅調(diào)用基類的虛函數(shù),不能達(dá)到多態(tài)的效果。

虛表指針和this指針關(guān)系

this指針表示對(duì)象的地址起始內(nèi)存地址。即this指針的值,指向了對(duì)象起始內(nèi)存。this指針的值和類第一個(gè)成員變量的地址一樣。當(dāng)類有虛函數(shù)時(shí)候,類的第一個(gè)成員變量是一個(gè)虛函數(shù)指針(_vfpr),根據(jù)this指針的值和第一個(gè)成員變量地址相同(this指針指向第一個(gè)成員變量)。因此當(dāng)有虛函數(shù)時(shí),this指針的值等于虛函數(shù)指針的地址(this指針指向虛函數(shù)指針)。

虛函數(shù)調(diào)用過(guò)程
  • 將this指針轉(zhuǎn)換為int*類型,并解引用得到虛表指針
  • 將虛表指針加上offset得到虛函數(shù)指針
  • 將虛函數(shù)指針轉(zhuǎn)換為對(duì)應(yīng)類型,并傳入this指針作為參數(shù),調(diào)用虛函數(shù)

3、重載和重寫(xiě)的區(qū)別

  • 重寫(xiě)是父類和子類的垂直關(guān)系,重載是不同函數(shù)之間的水平關(guān)系。
  • 重寫(xiě)要求參數(shù)列表相同,重載則要求參數(shù)列表不同,返回值不要求。
  • 重寫(xiě)關(guān)系中,調(diào)用方法根據(jù)對(duì)象類型決定,重載根絕調(diào)用的時(shí)候?qū)崊⒈砼c形參表對(duì)應(yīng)關(guān)系來(lái)選擇函數(shù)體

4、隱藏

隱藏指的是子類隱藏父類的函數(shù)(還存在),具有以下特征

  • 子類函數(shù)與父類名稱相同,但是參數(shù)不相同,父類函數(shù)被隱藏
  • 子類函數(shù)與父類函數(shù)名稱相同,參數(shù)也相同,但是父類函數(shù)沒(méi)有virtual,父類函數(shù)被隱藏
  • 兩個(gè)函數(shù)參數(shù)相同,但是基類函數(shù)不是虛函數(shù),和重寫(xiě)的區(qū)別在于基類函數(shù)是否是虛函數(shù)
  • 兩個(gè)函數(shù)參數(shù)不同,無(wú)論基類函數(shù)是不是虛函數(shù),都會(huì)被隱藏。和重載的區(qū)別在于兩個(gè)函數(shù)不在同一個(gè)類中
舉例一

代碼來(lái)源C++中函數(shù)重載、隱藏、覆蓋和重寫(xiě)的區(qū)別

#include <iostream>
using namespace std;

void func(char* s){
    cout<<"global function with name:"<<s<<endl;
}

class A{
    void func(){
        cout<<"member function of A"<<endl;
    }
public:
    void useFunc(){
        //func("lvlv");//A::func()將外部函數(shù)func(char*)隱藏
        func();
        ::func("lvlv");
    }
    virtual void print(){
        cout<<"A's print"<<endl;
    }
};

class B:public A{
public:
    void useFunc(){          //隱藏A::vodi useFunc()
        cout<<"B's useFunc"<<endl;
    }
    int useFunc(int i){      //隱藏A::vodi useFunc()
        cout<<"In B's useFunc(),i="<<i<<endl;
        return 0;
    }
    virtual int print(char* a){
        cout<<"B's print:"<<a<<endl;
        return 1;
    }

    //下面編譯不通過(guò),因?yàn)閷?duì)父類虛函數(shù)重寫(xiě)時(shí),需要函數(shù)返回值類型,函數(shù)名稱和參數(shù)類型全部相同才行
    // virtual int print(){
        // cout<<"B's print:"<<a<<endl;
    // }
};

int main(){
    A a;
    a.useFunc();
    B b;
    b.useFunc();//A::useFunc()被B::useFunc()隱藏
    b.A::useFunc();
    b.useFunc(2);
    //b.print();//編譯出錯(cuò),A::print()被B::print(char* a)隱藏
    b.A::print();
    b.print("jf");
}

程序運(yùn)行結(jié)果

程序執(zhí)行結(jié)果: 
member function of A
global function with name:lvlv
B's useFunc
member function of A
global function with name:lvlv
In B's useFunc(),i=2
A's print
B's print:jf
舉例二

代碼來(lái)源C++中的覆蓋與隱藏(詳細(xì)講解)

class father
{
public:
    void show1()
    {
        cout << "father::show1" << endl<< endl;
    }

    virtual void show2()
    {
        cout << "father::show2" << endl << endl;
    }
};

class son:public father
{
public:
    void show1()
    {
        cout << "son::show1" << endl<< endl;
    }

    virtual void show2()
    {
        cout << "son::show2" << endl << endl;
    }
};
int main(){  
//基類指針指向派生類對(duì)象的時(shí)候,基類指針可以直接調(diào)用派生類的覆蓋函數(shù),也可以通過(guò)::調(diào)用基類被覆蓋的虛函數(shù)
//而基類指針只能調(diào)用基類的被隱藏函數(shù),無(wú)法識(shí)別派生類中的隱藏函數(shù)
	father f;
	son s;  
	father *pf=&s;  
	son *ps=&s;  
	pf->show1();  //father::show1
	pf->show2();   //son::show2
	return 0;
}
  • 因?yàn)閟how1是非virtual函數(shù),調(diào)用它的對(duì)象類型為靜態(tài)類型即父類(靜態(tài)聯(lián)編),所以調(diào)用的是父類的對(duì)象
  • 但是show2為virtual函數(shù),調(diào)用它的對(duì)象類型為動(dòng)態(tài)類型即指針指向的類型(動(dòng)態(tài)聯(lián)編),所以調(diào)用的是子類的類型

3.2 虛函數(shù)和純虛函數(shù)

虛函數(shù)和純虛函數(shù)都是用于實(shí)現(xiàn)多態(tài)的機(jī)制,它們都支持動(dòng)態(tài)綁定的特性,但二者有著一些區(qū)別:

  • 實(shí)現(xiàn)方式不同。虛函數(shù)需要在基類中有一個(gè)默認(rèn)的實(shí)現(xiàn),派生類可以選擇重載該函數(shù)或者使用默認(rèn)的實(shí)現(xiàn),而純虛函數(shù)沒(méi)有默認(rèn)的實(shí)現(xiàn),必須在派生類中進(jìn)行實(shí)現(xiàn)才能使用。
  • 使用場(chǎng)景不同。虛函數(shù)應(yīng)被定義為默認(rèn)的實(shí)現(xiàn)和在派生類中重新實(shí)現(xiàn)的繼承函數(shù),它們讓繼承樹(shù)中不同的實(shí)現(xiàn)起到相同的作用,提高代碼重用性;而純虛函數(shù)則常被用作通用接口或抽象類,提供一種規(guī)范的實(shí)現(xiàn)方式,以確保子類中的實(shí)現(xiàn)得到規(guī)范化。
  • 不同的初始化方式。派生類中的虛函數(shù)在默認(rèn)沒(méi)有被重載時(shí)和基類中的虛函數(shù)都指向同一個(gè)實(shí)現(xiàn),這個(gè)實(shí)現(xiàn)允許在對(duì)象構(gòu)造時(shí)調(diào)用;而純虛函數(shù)不能在基類中被調(diào)用,因?yàn)槠錄](méi)有實(shí)現(xiàn),只作為規(guī)范的存在。如果執(zhí)行的代碼需要實(shí)現(xiàn)純虛函數(shù),這時(shí)只能從派生類構(gòu)造函數(shù)中調(diào)用。
  • 對(duì)于純虛函數(shù),它們?cè)诨愔谐洚?dāng)抽象角色。在實(shí)際中,純虛函數(shù)常常適用于基類,因?yàn)榛惼鋵?shí)是一個(gè)不能被實(shí)例化的抽象類,它只包含一些接口,讓繼承它的派生類去實(shí)現(xiàn)這些接口;而虛函數(shù)則適用于一個(gè)可以被實(shí)例化的類,它通過(guò)繼承實(shí)現(xiàn)了多態(tài)性的特性。
  • 純虛函數(shù)沒(méi)有具體的函數(shù)體,在虛表中的值為0,而具有函數(shù)體的虛函數(shù)則是函數(shù)的具體地址

3.3 虛函數(shù)的代價(jià)

  • 每一個(gè)帶有虛函數(shù)的類會(huì)產(chǎn)生一個(gè)虛函數(shù)表,用來(lái)存儲(chǔ)指向虛成員函數(shù)的指針
  • 帶有虛函數(shù)的類的每一個(gè)對(duì)象會(huì)有一個(gè)指向虛表的指針,增加對(duì)象的空間大小
  • 虛函數(shù)不能在世內(nèi)聯(lián)函數(shù)。因?yàn)閮?nèi)聯(lián)函數(shù)在編譯階段進(jìn)行替代,而虛函數(shù)在運(yùn)行階段才能確定用哪種函數(shù),虛函數(shù)不能是內(nèi)聯(lián)函數(shù)。

3.4 哪些函數(shù)不能是虛函數(shù)?

  • 構(gòu)造函數(shù):派生類必須知道基類干了什么,才能進(jìn)行構(gòu)造;當(dāng)有虛函數(shù)的時(shí)候,每一個(gè)類有一個(gè)虛表,對(duì)象有虛表指針,虛表指針在構(gòu)造函數(shù)中初始化
  • 內(nèi)聯(lián)函數(shù)
  • 靜態(tài)函數(shù):靜態(tài)函數(shù)不屬于對(duì)象屬于類,沒(méi)有this指針,設(shè)置為虛函數(shù)沒(méi)有意義
  • 友元函數(shù)、普通函數(shù):不屬于成員函數(shù),不能被繼承

3.5 靜態(tài)綁定和動(dòng)態(tài)綁定

  • 靜態(tài)綁定:綁定的是靜態(tài)類型(對(duì)象在聲明時(shí)采用的類型,在編譯器確定),發(fā)生在編譯器
  • 動(dòng)態(tài)綁定:綁定動(dòng)態(tài)類型(通常是指一個(gè)指針或引用目前所指對(duì)象的類型,是在運(yùn)行期決定的),所對(duì)應(yīng)的函數(shù)或?qū)傩砸蕾囉趯?duì)象的動(dòng)態(tài)類型,發(fā)生在運(yùn)行期。

建議:絕對(duì)不要重新定義繼承而來(lái)的非虛函數(shù),因?yàn)檫@樣重寫(xiě)的時(shí)候是沒(méi)有多態(tài)的,這樣會(huì)給程序留下不可預(yù)知的隱患和莫名其妙的bug,而且動(dòng)態(tài)綁定時(shí),要注意默認(rèn)參數(shù)的使用,當(dāng)缺省參數(shù)和virtual函數(shù)一起使用的時(shí)候要謹(jǐn)慎。

引用是否可以實(shí)現(xiàn)動(dòng)態(tài)綁定

引用在創(chuàng)建的時(shí)候必須初始化,在訪問(wèn)虛函數(shù)的時(shí)候,編譯器會(huì)根據(jù)綁定的對(duì)象類型決定要調(diào)用哪個(gè)函數(shù)(只能調(diào)用虛函數(shù))。文章來(lái)源地址http://www.zghlxwxcb.cn/news/detail-434551.html

3.6 如何防止一個(gè)類被實(shí)例化?

  • 將類定義為抽象基類或者將構(gòu)造函數(shù)聲明為private
  • 不允許外部創(chuàng)建類對(duì)象,只能在類內(nèi)部創(chuàng)建對(duì)象(static)

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

本文來(lái)自互聯(lián)網(wǎng)用戶投稿,該文觀點(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)文章

  • 什么是面向?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ū)ο缶幊蹋ㄒ唬╊惖幕A(chǔ),關(guān)系,繼承,封裝,多態(tài)

    Python面向?qū)ο缶幊蹋ㄒ唬╊惖幕A(chǔ),關(guān)系,繼承,封裝,多態(tài)

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

    2024年02月10日
    瀏覽(29)
  • 【Java不看后悔系列】|面向?qū)ο缶幊蘾[繼承、封裝、多態(tài)全覆蓋]

    【Java不看后悔系列】|面向?qū)ο缶幊蘾[繼承、封裝、多態(tài)全覆蓋]

    ??個(gè)人主頁(yè):? Aileen_0v0 ??系列專欄: Java學(xué)習(xí)系列專欄 ??個(gè)人格言:\\\"沒(méi)有羅馬,那就自己創(chuàng)造羅馬~\\\" (inheritance) 1.A?child class inherits everything from its parent class. 2.A?child class cannot inherit the constructors from its parent class . 子類 繼承 的內(nèi)容: 字段(成員變量): 子類會(huì)繼承父類的字段

    2024年02月05日
    瀏覽(22)
  • Python-面向?qū)ο螅好嫦驅(qū)ο蟆⒊蓡T方法 、類和對(duì)象、構(gòu)造方法、魔術(shù)方法、封裝、繼承、類型注解、多態(tài)(抽象類(接口))

    Python-面向?qū)ο螅好嫦驅(qū)ο蟆⒊蓡T方法 、類和對(duì)象、構(gòu)造方法、魔術(shù)方法、封裝、繼承、類型注解、多態(tài)(抽象類(接口))

    當(dāng)前版本號(hào)[20230806]。 版本 修改說(shuō)明 20230806 初版 生活中數(shù)據(jù)的組織 學(xué)校開(kāi)學(xué),要求學(xué)生填寫(xiě)自己的基礎(chǔ)信息,一人發(fā)一張白紙,讓學(xué)生自己填, 易出現(xiàn)內(nèi)容混亂 但當(dāng)改為登記表,打印出來(lái)讓學(xué)生自行填寫(xiě), 就會(huì)整潔明了 程序中數(shù)據(jù)的組織 在程序中簡(jiǎn)單使用變量來(lái)記錄學(xué)

    2024年02月14日
    瀏覽(22)
  • 【Java SE語(yǔ)法篇】8.面向?qū)ο笕筇卣鳌庋b、繼承和多態(tài)

    【Java SE語(yǔ)法篇】8.面向?qū)ο笕筇卣鳌庋b、繼承和多態(tài)

    ??博客主頁(yè):愛(ài)敲代碼的小楊. ?專欄:《Java SE語(yǔ)法》 ??感謝大家點(diǎn)贊????收藏?評(píng)論???,您的三連就是我持續(xù)更新的動(dòng)力?? 面向?qū)ο笕筇匦裕悍庋b、繼承和多態(tài)。 在面向?qū)ο蟪淌皆O(shè)計(jì)方法中,封裝(英語(yǔ):Encapsulation)是指一種將抽象性函式接口的實(shí)現(xiàn)細(xì)節(jié)部

    2024年02月01日
    瀏覽(34)
  • 【深入淺出C#】章節(jié) 4: 面向?qū)ο缶幊袒A(chǔ):封裝、繼承和多態(tài)

    封裝、繼承和多態(tài)是面向?qū)ο缶幊讨械暮诵母拍睿鼈儗?duì)于構(gòu)建靈活、可擴(kuò)展和可維護(hù)的軟件系統(tǒng)至關(guān)重要。 封裝(Encapsulation)通過(guò)將數(shù)據(jù)和相關(guān)操作封裝在一個(gè)類中,隱藏內(nèi)部實(shí)現(xiàn)細(xì)節(jié),并提供公共接口來(lái)與外部進(jìn)行交互。封裝有助于保護(hù)數(shù)據(jù)的完整性和安全性,同時(shí)提

    2024年02月10日
    瀏覽(27)
  • Educoder/頭歌JAVA——JAVA面向?qū)ο螅悍庋b、繼承和多態(tài)的綜合練習(xí)

    Educoder/頭歌JAVA——JAVA面向?qū)ο螅悍庋b、繼承和多態(tài)的綜合練習(xí)

    目錄 第1關(guān):封裝、繼承和多態(tài)進(jìn)階(一) 相關(guān)知識(shí) 面向?qū)ο笏枷?封裝 繼承 組合和繼承 構(gòu)造函數(shù) super()和this() 編程要求 第2關(guān):封裝、繼承和多態(tài)進(jìn)階(二) 相關(guān)知識(shí) 重寫(xiě)和重載 abstract(抽象類)和interface(接口) final static static的作用 多態(tài) 編程要求 第

    2024年02月04日
    瀏覽(65)
  • 軟考:軟件工程:面向?qū)ο蠹夹g(shù)與UML,時(shí)序圖,用例圖,類對(duì)象,封裝,繼承,多態(tài)

    軟考:軟件工程:面向?qū)ο蠹夹g(shù)與UML,時(shí)序圖,用例圖,類對(duì)象,封裝,繼承,多態(tài)

    提示:系列被面試官問(wèn)的問(wèn)題,我自己當(dāng)時(shí)不會(huì),所以下來(lái)自己復(fù)盤(pán)一下,認(rèn)真學(xué)習(xí)和總結(jié),以應(yīng)對(duì)未來(lái)更多的可能性 關(guān)于互聯(lián)網(wǎng)大廠的筆試面試,都是需要細(xì)心準(zhǔn)備的 (1)自己的科研經(jīng)歷, 科研內(nèi)容 ,學(xué)習(xí)的相關(guān)領(lǐng)域知識(shí),要熟悉熟透了 (2)自己的實(shí)習(xí)經(jīng)歷,做了 什

    2024年02月11日
    瀏覽(24)
  • 設(shè)計(jì)模式學(xué)習(xí)筆記 - 面向?qū)ο?- 2.封裝、抽象、繼承、多態(tài)分別用來(lái)解決哪些問(wèn)題?

    封裝 也叫作信息隱藏或者數(shù)據(jù)訪問(wèn)保護(hù)。類通過(guò)暴露有限的訪問(wèn)接口,授權(quán)外部?jī)H能通過(guò)類提供的方法(或者叫作函數(shù))來(lái)訪問(wèn)內(nèi)部信息或數(shù)據(jù)。 下面這段代碼是一個(gè)簡(jiǎn)化版的虛擬錢(qián)包的代碼實(shí)現(xiàn)。在金融系統(tǒng)中,我們會(huì)給每個(gè)用戶創(chuàng)建一個(gè)虛擬錢(qián)包,用來(lái)記錄用戶在我們

    2024年02月21日
    瀏覽(27)
  • C++ 面向?qū)ο蠛诵?繼承、權(quán)限、多態(tài)、抽象類)

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

    2024年02月08日
    瀏覽(27)

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

支付寶掃一掃打賞

博客贊助

微信掃一掃打賞

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

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

二維碼1

領(lǐng)取紅包

二維碼2

領(lǐng)紅包