日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程语言 > c/c++ >内容正文

c/c++

C++类实例以及子类在内存中的分配

發(fā)布時(shí)間:2023/12/18 c/c++ 24 豆豆
生活随笔 收集整理的這篇文章主要介紹了 C++类实例以及子类在内存中的分配 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

關(guān)于結(jié)構(gòu)體和C++類的內(nèi)存地址問題

今天終于有時(shí)間寫點(diǎn) 東西了~ 太爽了? *_* ?很多人都知道C++類是由結(jié)構(gòu)體發(fā)展得來的,所以他們的成員變量(C語言的結(jié)構(gòu)體只有成員變量)的內(nèi)存分配機(jī)制是一樣的。下面我們以類來說明問題,如果 類的問題通了,結(jié)構(gòu)體也也就沒問題啦。?類分為成員變量和成員函數(shù),我們先來討論成員變量。?一個(gè)類對象的地址就是類所包含的這一片內(nèi)存空間的首地址,這個(gè)首地址也就對應(yīng)具體某一個(gè)成員變量的地址。(在定義類對象的同時(shí)這些成員變量也就被定義了)我們來以一段代碼說明問題:?//類的定義

class K{
public:
?K(){k = 12;}
?~K(){}
?int k;
};?

//類的使用//...?K kTemp;
?printf("%d--%d\n",&kTemp,&kTemp.k);
?printf("%d--%d\n",sizeof(K),sizeof(kTemp.k));
?int *i = (int*)(&kTemp);
?int w = *i;
?printf("%d\n",w);?運(yùn)行上面的代碼,結(jié)果如下:1310588--1310588
4--4
12
很明顯,類的內(nèi)存大小和其唯一的成員變量的內(nèi)存大小是一致的。內(nèi)存地址也是一致的。他們甚至可以相互轉(zhuǎn)換。換成結(jié)構(gòu)體結(jié)果也是一樣。網(wǎng)友可以自己運(yùn)行上面代碼來進(jìn)行確認(rèn)。?這個(gè)時(shí)候,可能有人會提出疑問了。那么成員函數(shù)又如何?上面得代碼就好像類沒有任何成員函數(shù)一樣,根本說明不了問題。?呵呵,所有的函數(shù)都是存放在代碼區(qū)的, 不管是全局函數(shù),還是成員函數(shù)。要是成員函數(shù)占用類的對象空間,那么將是多么可怕的事情:定義一次類對象就有成員函數(shù)占用一段空間。?我們再來補(bǔ)充一下靜 態(tài)成員函數(shù)的存放問題吧:靜態(tài)成員函數(shù)與一般成員函數(shù)的唯一區(qū)別就是沒有this指針,因此不能訪問非靜態(tài)數(shù)據(jù)成員,就像我前面提到的,所有函數(shù)都存放在代碼區(qū),靜態(tài)函數(shù)也不例外。所有有人一看到 static 這個(gè)單詞就主觀的認(rèn)為是存放在全局?jǐn)?shù)據(jù)區(qū),那是不對的

?

c++是一種面向?qū)ο蟮木幊陶Z言,它向下保持了對c的兼容,同時(shí)也允許程序員能夠自由的操控內(nèi)存,雖然會帶來一些問題,但這不是我們要探討的問題,略過不 表。類是對某種對象的定義,包含變量和方法,也可以理解為現(xiàn)實(shí)生活中一類具有共同特征的事務(wù)的抽象,他是面向?qū)ο笳Z言的基礎(chǔ)。所以類是不占有內(nèi)存的,可是 如果類生成實(shí)例那么將會在內(nèi)存中分配一塊內(nèi)存來存儲這個(gè)類。

??? 類的實(shí)例在內(nèi)存中是如何分配內(nèi)存的,有什么需要我們注意的,下面將慢慢到來。

??? 比如下面一個(gè)類:

??? class A

??? {};

??? 從形式上看,它似乎什么有沒有,事實(shí)上它不止隱含了一個(gè)構(gòu)造函數(shù)和一個(gè)析構(gòu)函數(shù),還有一些操作符重載函數(shù),比如“=”。如果類A被實(shí)例話,如A a;在內(nèi)存會占據(jù)多大的空間呢?有人可能會說4,也有人會說0,還有人會說1,說1的就對了,為什么會是1呢?原因有很多,如果我們定義一個(gè)數(shù)組A b[10];如果上面是0,這樣的局面將會很尷尬,所以A這樣一個(gè)空類,編譯器會給它一個(gè)字節(jié)來填充。??

??? 增加一個(gè)變量,(字節(jié)對齊默認(rèn)都是4)

??? class? A

?? {

???? public:

????????? int i;

?? }

??

?? 類A的實(shí)例將占據(jù)4個(gè)字節(jié)的內(nèi)存,sizeof(A) = 4

?? 變量i 的初值被編譯器指定位0xcdcdcdcd。

??? 再增加一個(gè)變量,

?? class A

?? {

??????public:

????? int? i;

????? int? l;

?? }

?? 此時(shí)按照變量生命的先后順序,i被放在低地址上,l緊隨其后。

?? 實(shí)例占用8個(gè)字節(jié),sizeof(A) = 4*2 = 8

?? 如果累里面含有函數(shù):

? class A

?{

???? public:

????? int i;

????? int l;

??????int add(int x,int y){return (x+y);}

?};

?有些人可能會說類的大小是12,事實(shí)上sizeof(A) = 8;

?為什么會這樣,這是因?yàn)閟izeof訪問的程序的數(shù)據(jù)段,而函數(shù)地址則被保存在代碼段內(nèi),所以最后的結(jié)果是8.

?再看下面這個(gè)情況

?class A

?{

????? public:

???????? int i;

???????? int l;

???????? static int s;

?????? ? int add(int x,int y){return (x+y)};

?};

此時(shí)sizeof(A)大小仍為8,這里留給讀者去思考為什么?(^-^)。

當(dāng)類里面含有虛函數(shù)時(shí),情況會如何呢?

?class A

?{

????? public:

???????? int i;

???????? int l;

???????? static int s;

???????? virtual void Say(){};

??????? ?int add(int x,int y){return (x+y)};

?};

?因?yàn)楹刑摵瘮?shù),所以類里面將含有一個(gè)虛指針vptr,指向該類的虛表vtbl,一個(gè)指針占用四字節(jié)的地址,所以sizeof(A) = 12

?虛指針放在類實(shí)例地址的最低位置,

?比如 A *a = new A;

?我們可以這樣給變量i賦值

int *p = (int *)a;
?p++;
?*p = 1;//把i的值賦為1.

如果類作為派生類,內(nèi)存將如何分配呢?

這種情況雖然有些復(fù)雜,但并不是說不好理解。

他有多少個(gè)父類每個(gè)父類的大小加起來在加上自身就是sizeof的大小。

?

//-----C++類對象內(nèi)存結(jié)構(gòu)[講得很好] -------

首先介紹一下C++中有繼承關(guān)系的類對象內(nèi)存的布局:?
在C++中,如果類中有虛函數(shù),那么它就會有一個(gè)虛函數(shù)表的指針__vfptr,在類對象最開始的內(nèi)存數(shù)據(jù)中。之后是類中的成員變量的內(nèi)存數(shù)據(jù)。?
對于子類,最開始的內(nèi)存數(shù)據(jù)記錄著父類對象的拷貝(包括父類虛函數(shù)表指針和成員變量)。 之后是子類自己的成員變量數(shù)據(jù)。?
對于子類的子類,也是同樣的原理。但是無論繼承了多少個(gè)子類,對象中始終只有一個(gè)虛函數(shù)表指針。?
?
?
?
為了探討C++類對象的內(nèi)存布局,先來寫幾個(gè)類和函數(shù)?
首先寫一個(gè)基類:?
class Base?
{?
public:?
virtual void f() { cout << "Base::f" << endl; }?
virtual void g() { cout << "Base::g" << endl; }?
virtual void h() { cout << "Base::h" << endl; }?
int base;?
protected:?
private:?
};?
然后,我們多種不同的繼承情況來研究子類的內(nèi)存對象結(jié)構(gòu)。?
1. 無虛函數(shù)集繼承?
?
//子類1,無虛函數(shù)重載?
class Child1 : public Base?
{?
public:?
virtual void f1() { cout << "Child1::f1" << endl; }?
virtual void g1() { cout << "Child1::g1" << endl; }?
virtual void h1() { cout << "Child1::h1" << endl; }?
int child1;?
protected:?
private:?
};?
這個(gè)子類Child1沒有繼承任何一個(gè)基類的虛函數(shù),因此它的虛函數(shù)表如下圖:?
?
?
我們可以看出,子類的虛函數(shù)表中,先存放基類的虛函數(shù),在存放子類自己的虛函數(shù)。?
?
2. 有一個(gè)虛函數(shù)繼承?
//子類2,有1個(gè)虛函數(shù)重載?
class Child2 : public Base?
{?
public:?
virtual void f() { cout << "Child2::f" << endl; }?
virtual void g2() { cout << "Child2::g2" << endl; }?
virtual void h2() { cout << "Child2::h2" << endl; }?
int child2;?
protected:?
private:?
};?
?
當(dāng)子類重載了父類的虛函數(shù),則編譯器會將子類虛函數(shù)表中對應(yīng)的父類的虛函數(shù)替換成子類的函數(shù)。?
3. 全部虛函數(shù)都繼承?
//子類3,全部虛函數(shù)重載?
class Child3 : public Base?
{?
public:?
virtual void f() { cout << "Child3::f" << endl; }?
virtual void g() { cout << "Child3::g" << endl; }?
virtual void h() { cout << "Child3::h" << endl; }?
protected:?
int x;?
private:?
};?
?
?
?
4. 多重繼承?
多重繼承,即類有多個(gè)父類,這種情況下的子類的內(nèi)存結(jié)構(gòu)和單一繼承有所不同。?
?
我們可以看到,當(dāng)子類繼承了多個(gè)父類,那么子類的內(nèi)存結(jié)構(gòu)是這樣的:?
子類的內(nèi)存中,順序?
?
5. 菱形繼承?
?
?
6. 單一虛擬繼承?
?
?
虛 擬繼承的子類的內(nèi)存結(jié)構(gòu),和普通繼承完全不同。虛擬繼承的子類,有單獨(dú)的虛函數(shù)表, 另外也單獨(dú)保存一份父類的虛函數(shù)表,兩部分之間用一個(gè)四個(gè)字節(jié)的0x00000000來作為分界。子類的內(nèi)存中,首先是自己的虛函數(shù)表,然后是子類的數(shù)據(jù) 成員,然后是0x0,之后就是父類的虛函數(shù)表,之后是父類的數(shù)據(jù)成員。?
如果子類沒有自己的虛函數(shù),那么子類就不會有虛函數(shù)表,但是子類數(shù)據(jù)和父類數(shù)據(jù)之間,還是需要0x0來間隔。?
因此,在虛擬繼承中,子類和父類的數(shù)據(jù),是完全間隔的,先存放子類自己的虛函數(shù)表和數(shù)據(jù),中間以0x分界,最后保存父類的虛函數(shù)和數(shù)據(jù)。如果子類重載了父類的虛函數(shù),那么則將子類內(nèi)存中父類虛函數(shù)表的相應(yīng)函數(shù)替換。?
?
7. 菱形虛擬繼承?
?
結(jié)論:?
(1) 對于基類,如果有虛函數(shù),那么先存放虛函數(shù)表指針,然后存放自己的數(shù)據(jù)成員;如果沒有虛函數(shù),那么直接存放數(shù)據(jù)成員。?
(2) 對于單一繼承的類對象,先存放父類的數(shù)據(jù)拷貝(包括虛函數(shù)表指針),然后是本類的數(shù)據(jù)。?
(3) 虛函數(shù)表中,先存放父類的虛函數(shù),再存放子類的虛函數(shù)?
(4) 如果重載了父類的某些虛函數(shù),那么新的虛函數(shù)將虛函數(shù)表中父類的這些虛函數(shù)覆蓋。?
(5) 對于多重繼承,先存放第一個(gè)父類的數(shù)據(jù)拷貝,在存放第二個(gè)父類的數(shù)據(jù)拷貝,一次類推,最后存放自己的數(shù)據(jù)成員。其中每一個(gè)父類拷貝都包含一個(gè)虛函數(shù)表指 針。如果子類重載了某個(gè)父類的某個(gè)虛函數(shù),那么該將該父類虛函數(shù)表的函數(shù)覆蓋。另外,子類自己的虛函數(shù),存儲于第一個(gè)父類的虛函數(shù)表后邊部分。?
(6) 當(dāng)對象的虛函數(shù)被調(diào)用是,編譯器去查詢對象的虛函數(shù)表,找到該函數(shù),然后調(diào)用。

來源:http://blog.csdn.net/jimmy54/archive/2010/03/26/5418766.aspx

//-------------------------------------更新中的自己的了解...----------------------------------------------

....20/6/2011....

在C++中,如果類中有虛函數(shù),那么它就會有一個(gè)虛函數(shù)表的指針__vfptr,在類對象最開始的內(nèi)存數(shù)據(jù)中。之后是類中的成員變量的內(nèi)存數(shù)據(jù)。


總結(jié)

以上是生活随笔為你收集整理的C++类实例以及子类在内存中的分配的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。