C 物件模型的演變及驗證 2

2021-06-10 21:01:01 字數 3679 閱讀 4234

2012-11-11 wcdj

瑪雅人**的2023年12月21日世界末日前的最後乙個光棍節,果真是乙個人的節日,祝小時爸爸手術順利,早日**!

本文在前文的基礎上,考慮繼承關係(單繼承),繼續驗證繼承關係下的c++物件模型。

先看下什麼叫作覆蓋(override)

覆蓋

override,是指派生類中存在重新定義的函式,其函式名、引數列、返回值型別必須同父類中的相對應被覆蓋的函式嚴格一致,覆蓋函式和被覆蓋函式只有函式體(花括號中的部分)不同,當派生類物件呼叫子類中該同名函式時會自動呼叫子類中的覆蓋版本,而不是父類中的被覆蓋函式版本,這種機制就叫做覆蓋,特徵是:      

1、不同的範圍(分別位於派生類與基類);      

2、函式名字相同;      

3、引數相同;      

4、基類函式必須有

virtual

關鍵字。  

以上說明摘自: 區分過載(overload),覆蓋(override)和隱藏(hide)

在前文的基礎上,進行驗證。

/* 2012-11-11 wcdj

* c++虛函式表的例項解析

*/#include // 指定按1位元組對齊

#pragma pack(1)

// 定義普通函式指標

typedef void(*fun)(void);

// 定義類成員函式指標

class base;

typedef void(base::*cfun)(void);

#define callmemfun(obj, pcfun) ( (obj).*(pcfun) )

#define pcallmemfun(pobj, pcfun) ( (pobj)->*(pcfun) )

class base

base(int a, char b): m_ia(a), m_cb(b) {}

virtual ~base() {}

// virtual functions

virtual void f()

virtual void g()

virtual void h()

// non-virtual functions

void test()

void test2()

private:

int m_ia;

char m_cb;

};class derive1 : public base

derive1(int a, char b, int c) : base(a, b), m_ic(c) {}

virtual ~derive1()

virtual void x()

virtual void y()

virtual void z()

void test()

private:

int m_ic;

};class derive2 : public base

derive2(int a, char b, int c) : base(a, b), m_ic(c) {}

virtual ~derive2()

void f()

void g()

void test()

private:

int m_ic;

};int main()

/*output:

the size of derive1: 13

invork f()

invork derive1::x()

invork f()

invork ~derive1

the size of derive2: 13

invork derive2::f()

invork ~derive2

invork derive2::f()

invork f()

invork ~derive2

invork ~derive1

*/

在這個繼承關係中,子類沒有覆蓋任何父類的函式。物件模型參考derive1類的驗證。

注意:(1) 虛函式按照其宣告順序放於表中,其中,派生類的析構函式會替換基類的虛構函式;

(2) 父類的虛函式在子類的虛函式前面,虛函式表中沒有x(), y(), z()虛函式,pd1只能訪問基類的成員;

(3) 注意隱藏的概念,例如:

d1.test();

d1.base::test();

所謂的隱藏,指的是派生類型別的物件、指標、引用訪問基類和派生類都有的同名函式時,訪問的是派生類的函式,即隱藏了基類的同名函式。隱藏規則的底層原因其實是c++的名字解析過程。在繼承機制下,派生類的類域被巢狀在基類的類域中。派生類的名字解析過程如下:

1、首先在派生類類域中查詢該名字。

2、如果第一步中沒有成功查詢到該名字,即在派生類的類域中無法對該名字進行解析,則編譯器在外圍基類類域對查詢該名字的定義。

總結一下隱藏的特徵:

1、如果派生類的函式與基類的函式同名,但是引數不同。此時,不論有無

virtual

關鍵字,基類的函式將被隱藏(注意別與過載混淆)。      

2、如果派生類的函式與基類的函式同名,並且引數也相同,但是基類函式沒有

virtual

關鍵字。此時,基類的函式被隱藏(注意別與覆蓋混淆)。

派生類覆蓋父類的虛函式是很顯然的事情,不然虛函式就變得毫無意義了。物件模型參考derive2類的驗證。

注意:(1) 派生類覆蓋的 f() 函式被放到了虛表中原來父類虛函式的位置;即,

base *pd2 =new

derive2(7,'z', 8);

pd2->f();

由於基類指標pd2所指的記憶體中的虛函式表的 f() 的位置已經被 派生類derive2::f() 函式位址所取代,於是在實際呼叫發生時,是 derive2::f() 被呼叫了,即,實現了多型。

(2) 基類中沒有被派生類覆蓋的虛函式,在虛函式表中保持不變;

(3) 使用基類指標可以訪問派生類中的函式,前提是派生類覆蓋了基類的虛函式;

任何妄圖使用父類指標想呼叫子類中的未覆蓋父類的成員函式的行為都會被編譯器視為非法,所以,這樣的程式根本無法編譯通過。但在執行時,我們可以通過指標的方式訪問虛函式表來達到違反c++語義的行為。即,訪問non-public的虛函式:如果父類的虛函式是private或是protected的,但這些非public的虛函式同樣會存在於虛函式表中,所以,我們同樣可以使用訪問虛函式表的方式來訪問這些non-public的虛函式,這是很容易做到的。

對於派生類的普通例項物件 d1 和 d2,通過vs2008觀察其物件模型如下:

問題是,d1物件的虛函式表中沒有找到x(), y(), z()虛函式的存放位置。

多重繼承的情況後續分析。

C 物件模型的演變及驗證 1

2012 11 10 wcdj 關鍵字 c 物件模型,訪問私有成員,虛函式,虛函式表 vtbl 虛函式表指標 vptr 類成員函式指標 在c 中 有兩種類資料成員 1 static 2 nonstatic 有三種類成員函式 1 static 2 nonstatic 3 virtual c 物件模型的...

C 物件模型 2

史列因 我剛看了你寫的 深度探索c 物件模型 1 感覺很不錯。不過我有乙個建議 你說 誰知第一章便如此的難以消化,已經反覆讀了3遍,還是有些夾生 是很自然的。第一章是乙個總覽,如果你能全看懂,後面的就沒什麼看的必要了。第一章的內容後面都有詳細介紹,開始只要有個大概印象就可以了。這本書中很多內容都是前...

C 學習 物件模型之類及物件記憶體模型

使用c 開發一年多,從今天開始系統性的總結下c 一些知識點,畢竟c 語言特性複雜而龐大,很難短時間全部掌握。首先類可以模擬c語言的struct結構體,單純的宣告乙個類a,如果a沒有被例項化,那麼a是不會占用記憶體空間。這裡的類可以理解為一種特殊資料結構型別。再來談談物件,乙個類例項化後就變成乙個物件...