在程式設計時,時常會說我們是面向過程程式設計,還是物件導向程式設計。其它這是把底層記憶體處理過程,通過編譯器來抽象實現。不管是面向什麼程式設計,其它底下都是面向記憶體程式設計。
在程式設計過程中,如c++等高階語言都是物件導向程式設計的方式實現。都要涉及到物件的記憶體模型問題。如果我例項化乙個物件,那這個物件在記憶體中是如何存放的,如果這個物件還存在繼承關係,那繼承的基類與派生類在記憶體中又是怎麼樣存放,存在什麼樣的關係呢。
本篇只說幾個比較簡單的問
1) 繼承中基類的物件與派生類物件在記憶體中存放。
2)繼承關係中虛函式在記憶體如何存放,基類與派生類虛函式是什麼關係。
3)多型的實現原理
**:
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
using
namespace std;
class
base
;base
(int age,
const
char
* name)
;void
showbasename()
virtual
void
virtualshowbasename()
virtual
void
virtualshowbaseage()
virtual
void
virtualshowbaseinfo()
~base()
protected
:int
showbaseage()
private
:int mage;
const
char
* mname;};
class
inherbase
:public base
;inherbase
(const
char
* myname)
:base
(112
, myname)
void
showinherbasebname()
void
showbasename()
virtual
void
virtualshowbasename()
virtual
void
virtualshowmybaseinfo()
~inherbase()
private
:int mage;
const
char
* mname;};
base::
base
(int age,
const
char
* name)
intmain
(int argc,
char
** ar**)
this is base name
this is mybase name:
virtual show mybase name:zhangsan
virtual base info age:112
virtual show mybase name:zhangsan
以簡單的單繼承,沒有虛函式時,基類被派生類繼承後,所有的資料及方法都是被派生類繼承了過來,相當於在派生類的記憶體一塊區域存放了乙個以基類名為命名空間的物件;如果派生類中對基類的函式有重寫過,那麼並不會影響基類的函式,因為基類函式名的空間作用域是基類,而派生類重寫的函式作用域是派生類中。彼此其它是在不同的作用域中,所以派生類函式的重寫不會覆蓋基類函式。這就可以理解為什麼從派生類強制轉換成基類時,此時呼叫的函式是基類中的函式,而不是派生類中重寫的函式。
的以有如下的**執行及結果
inherbase* ibs =
newinherbase
("zhangsan");
base* ms =
static_cast
>
(ibs)
;//物件中的型別強制轉換,相當於指標加上作用域,再作記憶體訪問。
ms->
showbasename()
; ibs-
>
showbasename()
;
this is base name
this is mybase name:
通過記憶體杳看工具也是可看到更清析的記憶體模型排列情況。
單繼承有虛函式情況下,因為虛函式的存在,所以需要在物件記憶體中建乙個表,稱為虛函式表。這個虛函式表是通過指標來訪問ptrtable,表存放的內容就是虛函式指標。這個虛函式指標是基類與派生類共享的,其它也是相當於在基類、派生類中使用指標,指向一片記憶體區域,這個記憶體區域存放的是虛函式指標(這個共享指標存放在物件記憶體的首位址處)。是基類,派生類使用虛指標來實現記憶體共享的來實現的。所以不管如何的進行指標型別的強制轉換,基類,派生類都可以訪問到虛函式表。
注意:因為這個虛函式表記憶體區域是共享的,那麼如果是同名的虛函式,那麼會虛函式指標只能存在乙個,那麼基類的虛函式就會被派生類覆蓋掉,所以多型時的虛函式只能呼叫到基類虛函式。如果虛函式不重寫,而是增加,那麼基類、派生類的虛函式相互不影響。
派生類指標可以訪問基類虛函式指標,也可以訪問派生虛函式。但基類型別指標是無法訪問到派生類虛函式。
base* bs =
newbase(11
,"tudou");
inherbase* ibs =
newinherbase
("zhangsan");
base* ms =
static_cast
>
(ibs)
;//物件中的型別強制轉換,相當於指標加上作用域,再作記憶體訪問。
ms->
virtualshowbasename()
; ibs-
>
virtualshowbasename()
;//基類虛函式被覆蓋,只執行派生類虛函式。
ms->
virtualshowbaseinfo()
;//ms->virtualshowmybaseinfo(); //error!!! 這個編譯錯誤,因為基類的作用域無法訪問到派生類的虛函式。
ibs-
>
virtualshowbaseinfo()
; ibs-
>
virtualshowmybaseinfo()
;//ibs是派生類指標,可以訪問到基類的虛函式,也可以訪問到派生類虛函式。
關於多型的原理,是通過物件指標型別的強制轉換來實現的,從上面的分析可以看出,強制轉換後可派生類虛函式指標可訪問基類的虛函式指標,也可以訪問到派生類的虛函式指標的。通過強制轉換成基類指標來訪問到派生類中的成員方法(虛函式)叫多型。說白了就是基類與派生類共享記憶體,通過指標強制轉換來實現對共享記憶體的訪問。但因為基類與派生類又有不同的記憶體作用域,所以基類與派生類能訪問到記憶體作用域不同。從而實現了狀態的轉換,操作了不同作用域的記憶體區域。
C 物件模型 記憶體布局
聯絡人 石虎暱稱 嗡嘛呢叭咪哄 一 概念 1 沒有繼承情況,vptr存放在物件的開始位置,以下是base1的記憶體布局 m idata 100 2.單繼承的情況下,物件只有乙個vptr,它存放在物件的開始位置,派生類子物件在父類子物件的最後面,以下是d1的記憶體布局 b1 m idata 100 b...
C 物件記憶體模型探索
實驗環境 ubuntu 18.04 64 bit gcc 7.3.0 g 7.3.0,編譯使用 m32選項啟用32位環境 實驗步驟 1 不含有虛函式的基類,如下 include class base private int i int main 執行結果 kevin kvm study temp g...
C 物件記憶體模型初探
週末看資料的時候,看到虛繼承和虛函式 兩者完全不是一碼事,正在寫乙個簡單的總結 進一步看到c 的類物件的記憶體模型,網上已有很多文章,自己也記錄一下。研究問題從簡單入手,一步步深入。我們先來看乙個最簡單的模式 class a 用測試程式可以發現,a的物件的大小是4位元組,也就是a int型別 的大小...