GeekBand C++面向?qū)ο蟾呒壘幊蹋ㄏ拢㏒econd Week

GeekBand C++面向?qū)ο蟾呒壘幊蹋ㄏ? Second Week

本周主要是講述了C++的對象模型,通過對于對象模型的深入了解,我們可以知道虛函數(shù),多態(tài),this指針的了解。 以及部分 new,delete的部分詳細介紹。

Object Model

C++的對象模型是C++對象最重要的部分之一,且包含的內(nèi)容非常的豐富.C++中的class是由data member和function member組成的。對象模型的內(nèi)存布局和data member和function member緊密相關(guān)。這里主要總結(jié)下function member及相關(guān)繼承關(guān)系對與object model的影響。

C++支持三種類型的member function: static,nonstatic 和 virtual。

static member function

靜態(tài)成員函數(shù)有有以下的調(diào)用方式。

obj.func();
ptr->func();
Object::func(); // 這種member selection的寫法是一種寫法上的便利,它會被轉(zhuǎn)化成一個直接的函數(shù)調(diào)用

靜態(tài)成員函數(shù)本質(zhì)上和非成員函數(shù)是一樣的(編譯器會做一些 name mangled的處理)。如果取一個static member function的地址,獲得的將是其在內(nèi)存中的地址,其地址類型并不是一個“指向 class member function的指針”,而是一個“nonmember函數(shù)指針”。
靜態(tài)成員函數(shù)有以下特征:

1.它不能夠直接存取class中的nonstatic members
2.它不能夠聲明為const,volative,virtual
3.它不需要經(jīng)由class object才被調(diào)用

nonstatic member function

C++的設(shè)計準則之一就是:非靜態(tài)成員函數(shù)至少必須和一般的非成員函數(shù)有相同的效率。

編譯器在內(nèi)部會將非靜態(tài)成員函數(shù)通過 name mangling的處理成對等的非成員函數(shù)。通??赡苡幸韵聨讉€步驟:

1.改寫函數(shù)簽名, 添加一個額外的this指針的參數(shù)。
2.將每一個 “對nonstatic data member的存取操作” 改為竟有this指針來存取
3.將非靜態(tài)成員函數(shù)重新寫成一個外部函數(shù),該函數(shù)名經(jīng)過“name mangling” 處理,使它成為獨一無二的函數(shù)名。

virtual member function

C++中虛函數(shù)加上繼承是實現(xiàn)多態(tài)的手段。

一個類中如果有虛函數(shù),編譯器在類的構(gòu)造函數(shù)中會加入一個虛函數(shù)指針(vptr)的初始化。vptr指向一個虛函數(shù)表(vtbl). vtbl中存放著相應(yīng)虛函數(shù)的對應(yīng)的函數(shù)地址。

class Point {
public:
    virtual ~Point();
    virtual float x() const;
private:
    float x_;
};

class Point2d : public Point{
public:
    virtual float y() const;
    float length() const;
private:
    float x_, y_;
};

類的內(nèi)存布局如下:

Point* ptr = new Point();
ptr->x();

這個會被轉(zhuǎn)化為
(ptr->vptr[2])(ptr)

所以Point*類型的變量,會根據(jù)指向不同的類(基類或子類)的實例,根據(jù)vptr指向的對應(yīng)slots的函數(shù)地址,執(zhí)行不同的函數(shù),從而實現(xiàn)多態(tài)。

multi inheritance

多重繼承的情況要比單繼承要復雜。

class base1 {
public:
    virtual base1();
};

class base2 {
public:
    virtual ~base2();
};

class devired : public base1, public base2 {
};

相關(guān)類的內(nèi)存布局如下:

從內(nèi)存的布局可以看出,子類分別繼承了基類的虛函數(shù)表,但是也不是完全的獨立繼承。繼承的兩個虛函數(shù)表有不同的地位。一般按照繼承的順序,第一個(base1)基類中的虛函數(shù)和第二個基類(base2)中的相關(guān)虛函數(shù)。

virtual inheritance

虛繼承

為了解決從不同途徑繼承來的同名的數(shù)據(jù)成員在內(nèi)存中有不同的拷貝造成數(shù)據(jù)不一致問題,將共同基類設(shè)置為虛基類。這時從不同的路徑繼承過來的同名數(shù)據(jù)成員在內(nèi)存中就只有一個拷貝,同一個函數(shù)名也只有一個映射。這樣不僅就解決了二義性問題,也節(jié)省了內(nèi)存,避免了數(shù)據(jù)不一致的問題。
class 派生類名:virtual 繼承方式 基類名
virtual是關(guān)鍵字,聲明該基類為派生類的虛基類。
在多繼承情況下,虛基類關(guān)鍵字的作用范圍和繼承方式關(guān)鍵字相同,只對緊跟其后的基類起作用。
聲明了虛基類之后,虛基類在進一步派生過程中始終和派生類一起,維護同一個基類子對象的拷貝

1.在多繼承情況下,虛基類關(guān)鍵字的作用范圍和繼承方式關(guān)鍵字相同,只對緊跟其后的基類起作用。

2.聲明了虛基類之后,虛基類在進一步派生過程中始終和派生類一起,維護同一個基類子對象的拷貝。

3.觀察類構(gòu)造函數(shù)的構(gòu)造順序,拷貝也只有一份。

new 與 delete

對于class,new 是先分配內(nèi)存,再調(diào)用ctor。delete是先調(diào)用dtor,然后再釋放內(nèi)存。

String* ps = new String("hello");

// 上面這句話轉(zhuǎn)化為下面的三句話
void* mem = operator new(sizeof(String));
ps = static_cast<String*>(mem);
ps->String::String("hello);

其中operator new 分為全局的和屬于某個類的區(qū)分??梢酝ㄟ^重載operator new(operator new[]) 或 operator delete(operator delete[]) 函數(shù)來自己控制內(nèi)存的管理。

重載 ::operator new 等

重載全局的 operator new 等函數(shù)是一件非常危險的事情,它會影響到所有的 new 等的內(nèi)存分配。

void* myAlloc(size_t size)
{ return malloc(size); }

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

// 全局的函數(shù)不可以聲明于任何一個 namespace 內(nèi)
inline void* operator new(size_t size)
{ cout << "my global new" << endl; return myAlloc(size); }

inline void* operator new[](size_t size)
{ cout << "my global new[]" << endl; return myALloc(size); }

// delete 同理

重載 member operator new/delete

對于某個類重載其operator new/delete的函數(shù),可以只針對這個類使用自定義的內(nèi)存管理。

class Foo {
public:
    void* operator new(size_t);
    void operator delete(void*, size_t);
    // ...
};

在使用new或delete時可以指定使用全局的 operator new 或operator delete 函數(shù)

// 如果沒有定義member的operator new的方法,就調(diào)用global的方法,有就調(diào)用自定義的member的版本
Foo* pf = new Foo;

// 強制使用 global的版本
Foo* pf = ::new Foo;

重載 new(), delete()

我們可以重載 class member operator(),寫出多個版本,前提是每一個版本都必須有特殊的參數(shù)列,其中第一個參數(shù)必須是size_t,其余參數(shù)以 new 所制定的placement argument為初值。出現(xiàn)于 new(...)小括號內(nèi)的便是所謂的 placement arguments。這種寫法被稱為placement new。

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

const

const是一個非常好的關(guān)鍵字,如果可以用,就需要加上。

const可以用來修飾函數(shù)(只限成員函數(shù))和 函數(shù)的參數(shù)。修飾成員函數(shù)表示此函數(shù)保證不更改data members。

關(guān)于const object 和 non-const object 調(diào)用 const 和 non-const 函數(shù)的關(guān)系可以用以下的表格來表示。

const object non-const object
const member function ? ?
non-const member function ? ?

同時const也是屬于成員函數(shù)的簽名的一部分,一個類中可以同時出現(xiàn)nonconst 和const兩種版本。

當成員函數(shù)的const和non-const版本同時存在時,const object只能調(diào)用 const的版本,non-const object 只能調(diào)用non-const 的版本。

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

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

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