C++筆記五(面向?qū)ο缶幊滔拢?/h2>

十六 對(duì)象模型:關(guān)于vptr和vtbl

55.png

如圖模型所示,B類繼承A類,C類繼承B類,子類有父類的成分,繼承的包括數(shù)據(jù)、非虛函數(shù)和虛函數(shù)。當(dāng)類里面有虛函數(shù),無(wú)論多少數(shù)量,會(huì)多一個(gè)指針,如圖vptr(虛指針),指向vtbl(虛函數(shù)表)。虛函數(shù)的調(diào)用是通過(guò)虛函數(shù)表實(shí)現(xiàn),無(wú)論子類是否要重寫函數(shù)。
有一個(gè)指針p指向c(new c),通過(guò)p調(diào)用vfunc1(),先調(diào)用虛函數(shù)表,在虛函數(shù)表上找到對(duì)應(yīng)編號(hào)的函數(shù),實(shí)現(xiàn)調(diào)用,這種方式叫做動(dòng)態(tài)綁定。解析成c語(yǔ)言的形式如下:

(*(p->vptr)[n])(p); //n是在虛函數(shù)表中函數(shù)的編號(hào),根據(jù)你寫的代碼的順序排
 //或者
(* p->vptr[n])(p);
56.png

(1)如圖左下角實(shí)現(xiàn)不同形狀的draw(),1.將指向A(shape類)的指針?lè)诺饺萜骼锩妫?.將draw()寫成虛函數(shù);3.具體形狀類(例如rect類)繼承抽象類shape。
注:為了讓容器容納各種各樣的形狀,容器里面放的是指針,因?yàn)槿萜骼锩鏀?shù)據(jù)類型必須一致,指針必須指向父類,這樣聲明的時(shí)候指向父類,用的時(shí)候就可以指向子類。
(2)對(duì)比c語(yǔ)言的實(shí)現(xiàn)方法,c語(yǔ)言是先判斷哪一種形狀,然后調(diào)用其draw()函數(shù),這樣寫要添加某個(gè)形狀,重新寫判斷類型代碼,不方便。

總結(jié):C++編譯器看到函數(shù),有兩個(gè)考量。
一是靜態(tài)綁定,編譯成Call、XXX(地址);
二是動(dòng)態(tài)綁定(虛機(jī)制:動(dòng)態(tài)綁定的形式),要符合三個(gè)條件:1.通過(guò)指針調(diào)用 2.這個(gè)指針向上轉(zhuǎn)型3.調(diào)用虛函數(shù)。調(diào)用哪個(gè)虛函數(shù),要看p指向什么,這就是理解動(dòng)態(tài)綁定。

結(jié)合第四周作業(yè)探究?jī)?nèi)存模型:
內(nèi)存模型測(cè)試.png

內(nèi)存模型.png

內(nèi)存模型2.png

所以根據(jù)測(cè)試代碼sizeof大小組成如圖所示
Fruit:8+(4+4)+8+(1+7)=32
Apple:8+(4+4)+8+(1+3+4)+(1+7)=40

測(cè)試代碼:
#include <iostream>
using namespace std;

class Fruit 
{
public:
    int no;
    double weight;
    char key;
    void print() { }
    virtual void process() { }
};
//-------------------------------------------------
class Apple : public Fruit 
{
public:
    int size;
    char type;
    void save() { }
    virtual void process() { }
};
//-------------------------------------------------
int main(int argc, char const *argv[]) 
{
    Fruit fruit;
    Apple apple;

    cout << "--------------Fruit----------------" << endl;
    cout << "sizeof(Fruit) = " << sizeof(Fruit) << endl;
    printf("Fruit.vptr         = %x\n", &fruit);
    printf("Fruit.vptr_process = %x\n", *(int*)&fruit);
    printf("Fruit              = %x\n", &fruit);
    printf("Fruit.no           = %x\n", &fruit.no);
    printf("Fruit.weight       = %x\n", &fruit.weight);
    printf("Fruit.key          = %x\n", &fruit.key);
    
    cout << "--------------Apple----------------" << endl;
    cout << "sizeof(Apple) = " << sizeof(Apple) << endl;
    printf("Apple.vptr         = %x\n", &apple);
    printf("Apple.vptr_process = %x\n", *(int*)&apple);
    printf("Apple              = %x\n", &apple);
    printf("Apple.no           = %x\n", &apple.no);
    printf("Apple.weight       = %x\n", &apple.weight);
    printf("Apple.key          = %x\n", &apple.key);
    printf("Apple.size         = %x\n", &apple.size);
    printf("Apple.type         = %x\n", &apple.type);

    system("pause");
    return 0;
}

十七 對(duì)象模型:關(guān)于this

通過(guò)一個(gè)對(duì)象來(lái)調(diào)用一個(gè)函數(shù),那個(gè)對(duì)象的地址就是this指針

57.png

第三周筆記有對(duì)這種模式具體的敘述及優(yōu)點(diǎn),這里不再贅述。
鏈接:http://www.itdecent.cn/p/ca6613dd4c8d

十八 對(duì)象模型:關(guān)于Dynamic Binding

58.png

a是A的對(duì)象,雖然初值是b經(jīng)過(guò)強(qiáng)制類型轉(zhuǎn)換的,但因?yàn)槭峭ㄟ^(guò)對(duì)象調(diào)用,不是指針,所以這是靜態(tài)綁定。編譯的匯編碼為箭頭所指。


59.png

pa為動(dòng)態(tài)綁定,滿足三個(gè)條件(new出來(lái)B,類型是A,是向上轉(zhuǎn)型),call不是一個(gè)固定的地址。
(*(P->vptr)[n])(p)的解釋:通過(guò)指針p找到虛指針vptr,再找到虛表,取出其中第n個(gè),把它當(dāng)成函數(shù)指針去調(diào)用,由于是通過(guò)p來(lái)調(diào)用,所以p就是一個(gè)this指針,(p)就是this指針。

十九 重談const

60.png

const第一周筆記有提過(guò),鏈接:http://www.itdecent.cn/p/2fafca2021d5
(1)這里需要新提出的是:basic_string有兩個(gè)成員函數(shù),一個(gè)operator[]函數(shù)后面有const,一個(gè)沒有const,這兩者可以存在,且有const的不用寫COW,沒有const的必須寫COW。
(2)常成員函數(shù)的const和non-const版本同時(shí)存在,const object只能調(diào)用const版本,non-const object只能調(diào)用non-const版本。

二十 關(guān)于New Delete

第二周有記載,這里不再贅述。
鏈接:http://www.itdecent.cn/p/12dcce512fd4

重載Operator new,Operator delete

重載operator new和operator delete用來(lái)設(shè)計(jì)自己的內(nèi)存池,也就是內(nèi)存管理。

全局寫法:
void* myAlloy(size_t size)
{return malloc(size);}

void myFree(void* ptr)
{return free(ptr);}

//他們不可以被聲明與一個(gè)namespace內(nèi)
inline void* operator new(size_t size)
{cout<<“jjhou global new()   \n";  return myAlloc(size);}
inline void* operator new[](size_t size)
{cout<<“jjhou global new[]()   \n";  return myAlloc(size);}
inline void* operator delete(void* ptr)
{cout<<“jjhou global delete()   \n";  myFree(ptr);}
inline void* operator delete[](void* ptr)
{cout<<“jjhou global delete[]()   \n";  myFree(ptr);}

new的時(shí)候三個(gè)動(dòng)作(其中一個(gè)調(diào)用new函數(shù),此new和前頭new不同),如果寫了自己的重載new函數(shù),則調(diào)用該函數(shù),否則調(diào)用全局的new函數(shù)。
重載全局的函數(shù),影響無(wú)遠(yuǎn)弗界。

成員函數(shù)寫法:
Foo* p=new Foo;
//try{
//     void* mem=operator new(sizeof(Foo));
//     p=static_cast<Foo*>(mem);
//     p->Foo::Foo();
//}
delete p
//p->~Foo();
//operator delete(p);
class Foo{
public:
    void* operator new(size_t);
    void  operator delete(void*,size_t); //size_t 可有可無(wú)
    //...
};  

示例

61.png

62.png

在G4.9中,數(shù)組大小是數(shù)組整包大小加上一個(gè)計(jì)數(shù)器。

Foo* p=::new Foo(7);
::delete p;

Foo* pArray=::new Foo[5];
::delete[] pArray;

這樣調(diào)用(也就是寫上global scope operator::),會(huì)繞過(guò)前述overloaded functions,強(qiáng)迫使用global version。

重載new(),delete()

我們可以重載class member operator new(),寫出多個(gè)版本,前提是每一版本的聲明都必須有獨(dú)特的參數(shù)列,其中第一個(gè)參數(shù)必須是size_t,其余參數(shù)以new所指定的placement arguments為初值。出現(xiàn)于new(......)小括號(hào)內(nèi)的便是所謂placement arguments。

Foo* pf=new(300,'c')Foo;

我們也可以重載class member operator delete(),寫出多個(gè)版本。但它們絕不會(huì)被delete調(diào)用。只有當(dāng)new所調(diào)用的ctor拋出exception,才會(huì)調(diào)用這些重載版的operator delete()。他只可能這樣被調(diào)用。主要用來(lái)歸還未能完全創(chuàng)建成功的object所占用的memory。

Basic_String 使用new(extra)擴(kuò)充申請(qǐng)量

63.png
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容