国产日韩精品视频_2020久久国产最新免费观看_国内久久久久影院精品_日本一区二区视频在线

我愛學習網-上傳
當前位置: 主頁 > 文庫 > C++ >

c++ 內存模型和指針

時間:2020-09-20 16:32來源:我愛學習網 作者:apple 點擊:

對象的內存模型(無繼承)

類是創建對象的模板,不占用內存空間;而對象是實實在在的數據,需要內存來儲存。對象在創建時會在堆或棧上分配內存。

不同對象的成員變量的值可能不同,需要單獨分配內存來存儲,但是不同對象的成員函數的代碼是一樣的。所以編譯器會將成員變量和成員函數分開存儲:分別為每個對象的成員變量分配內存,但是所有對象都共享同一段函數代碼。


類是一中復雜的數據類型,在計算類的大小的時候只計算成員變量的大小,并不包含成員函數。例如:

class Student{
private:
    char *m_name;
    int m_age;
    float m_score;
public:
    void setname(char *name);
    void setage(int age);
    void setscore(float score);
    void show();
};

int main(){
    //在棧上創建對象
    Student stu;
    cout<

運行結果:

12

12

12

假設 stu 的起始地址為 0X1000,那么該對象的內存分布如下圖所示:

m_name、m_age、m_score 按照聲明的順序依次排列,和結構體非常類似,也會有內存對齊的問題。

內存對齊

這里順便提一下內存對齊。

計算機內存是以字節(Byte)為單位劃分的,理論上CPU可以訪問任意編號的字節,但實際情況并非如此。CPU 通過地址總線來訪問內存,一次能處理幾個字節的數據,就命令地址總線讀取幾個字節的數據。32 位的 CPU 一次可以處理4個字節(尋址步長)的數據,那么每次就從內存讀取4個字節的數據;少了浪費主頻,多了沒有用。64位的處理器也是這個道理,每次讀取8個字節。

將一個數據盡量放在一個步長之內,避免跨步長存儲,這稱為內存對齊。在32位編譯模式下,默認以4字節對齊;在64位編譯模式下,默認以8字節對齊。

 

  • 結構體中的內存對齊

在系統默認的情況下,結構體會進行對齊,對齊的原則如下:

原則A:結構體的成員,第一個成員在偏移0的位置,之后的每個成員的起始位置必須是當前成員大小的整數倍;

原則B:如果結構體A含有結構體成員B,那么B的起始位置必須是B中最大元素大小整數倍地址;

原則C:結構體的總大小,必須是內部最大成員的整數倍;

 

typedef struct?

{?

? int a;?

? double b;?

? char c?

}?

首先偏移量0是int大小(4)的倍數,因此直接分配,因為偏移量4不是double大?。?)的倍數,所以先擴充再分配,接著因為偏移量16是char大?。?)的倍數,所以直接分配。最后因為總大小17不是8的倍數,所以再擴充到24。

 

  • 聯合體中的內存對齊

聯合體的內存不會為了所有成員安排,而是只取最大的成員的所需內存大小,每次只能使用其中一個成員。但是有一個問題:

typedef union?

{?

? char a;?

? int[5] b;?

? double c;?

}?

當然只取最大的int數組的大小20沒錯,但是double是8字節的,而此時聯合體已經按int的4字節對齊了,所以還要額外多加4字節的內存來保證8的倍數。所以最后結果是24。

所以聯合體的內存除了取最大成員內存外,還要保證是所有成員類型size的最小公倍數。

 

對象的內存模型(有繼承,無名字遮蔽)

class A{
...
private:
    int m_a;
    int m_b;
...
};

class B:public A{
...
private:
    int m_c;
...
};

int main(){
    A obj_a(99, 10);
    B obj_b(84, 23, 95);
}

obj_a 是基類對象,obj_b 是派生類對象。假設 obj_a 的起始地址為 0X1000,那么它的內存分布如下圖所示:

假設 obj_b 的起始地址為 0X1100,那么它的內存分布如下圖所示:


可以發現,基類的成員變量排在前面,派生類的排在后面。

 

對象的內存模型(有名字遮蔽)

在上述代碼基礎上添加如下代碼:

class C:public B{
...
private:
    int m_b;
    int m_c;
    int m_d;
}

int main(){
...
    C obj_c(12,2,32,45);
}

假設 obj_c 的起始地址為 0X1300,那么它的內存分布如下圖所示:


多繼承下的內存模型

//基類A
class A{
public:
    A(int a, int b);
protected:
    int m_a;
    int m_b;
};

//基類B
class B{
public:
    B(int b, int c);
protected:
    int m_b;
    int m_c;
};

class C: public A, public B{
public:
    C(int a, int b, int c, int d);
public:
    void display();
private:
    int m_a;
    int m_c;
    int m_d;
};

int main(){
    C obj_c(10, 20, 30, 40);
}

A、B 是基類,C 是派生類,假設 obj_c 的起始地址是 0X1000,那么 obj_c 的內存分布如下圖所示:


基類對象的排列順序和繼承時聲明的順序相同。

虛繼承下的內存模型

如今虛繼承下的解決方案:VC解決方案,有點類似于虛函數。

VC 引入了虛基類表,如果某個派生類有一個或多個虛基類,編譯器就會在派生類對象中安插一個指針,指向虛基類表。虛基類表其實就是一個數組,數組中的元素存放的是各個虛基類的偏移。

 

假設 A 是 B 的虛基類,那么各對象的內存模型如下圖所示:


假設 A 是 B 的虛基類,同時 B 又是 C 的虛基類,那么各對象的內存模型如下圖所示:


虛繼承表中保存的是所有虛基類(包括直接繼承和間接繼承到的)相對于當前對象的偏移,這樣通過派生類指針訪問虛基類的成員變量時,不管繼承層次都多深,只需要一次間接轉換就可以。

 

另外,這種方案還可以避免有多個虛基類時讓派生類對象額外背負過多的指針。例如,假設 A、B、C、D 類的繼承關系為:


那么 obj_d 的內存模型如下圖所示:


 

存在虛函數時的內存模型

詳情見虛函數

 

玩轉指針之突破訪問權限

在弄清楚了C++中的內存模型,我們便可以借助指針突破訪問權限的限制,訪問private、protected屬性的成員變量。

class A{
...
private:
    int m_a;
    int m_b;
    int m_c;
};

int main(){
    A obj(10, 20, 30);
    int a = obj.m_a;  //Compile Error
    A *p = new A(40, 50, 60);
    int b = p->m_b;  //Compile Error
    return 0;
}

這段代碼說明了,無論通過對象變量還是對象指針,都不能訪問 private 屬性的成員變量。

 

使用偏移:

在對象的內存模型中,成員變量和對象的開頭位置會有一定的距離。以上面的 obj 為例,它的內存模型為:


圖中假設 obj 對象的起始地址為 0X1000,m_a、m_b、m_c 與對象開頭分別相距 0、4、8 個字節,我們將這段距離稱為偏移(Offset)。一旦知道了對象的起始地址,再加上偏移就能夠求得成員變量的地址,知道了成員變量的地址和類型,也就能夠輕而易舉地知道它的值。

 

當通過對象指針訪問成員變量時,編譯器實際上也是使用這種方式來取得它的值。為了說明問題,我們不妨將上例中成員變量的訪問權限改為 public,再來執行第 13 行的語句:

int b = p->m_b;

此時編譯器內部會發生類似下面的轉換:

int b = *(int*)( (int)p + sizeof(int) );

若在上述代碼中直接用這個替換即可突破訪問權限的限制。

 

向上轉型(將派生類賦值給基類)

類其實也是一種數據類型,也可以發生數據類型轉換,不過這種轉換只有在基類和派生類之間才有意義,并且只能將派生類賦值給基類,包括將派生類對象賦值給基類對象、將派生類指針賦值給基類指針、將派生類引用賦值給基類引用,這在 C++ 中稱為向上轉型(Upcasting)。相應地,將基類賦值給派生類稱為向下轉型(Downcasting)。

將派生類對象賦值給基類對象

#include 
using namespace std;
//基類
class A{
public:
    A(int a);
public:
    void display();
public:
    int m_a;
};
A::A(int a): m_a(a){ }
void A::display(){
    cout<<"Class A: m_a="<

運行結果:

Class A: m_a=10

Class B: m_a=66, m_b=99

----------------------------

Class A: m_a=66

Class B: m_a=66, m_b=99

 

賦值的本質是將現有的數據寫入已分配好的內存中,對象的內存只包含了成員變量,所以對象之間的賦值是成員變量的賦值,成員函數不存在賦值問題。運行結果也有力地證明了這一點,雖然有a=b;這樣的賦值過程,但是 a.display() 始終調用的都是 A 類的 display() 函數。換句話說,對象之間的賦值不會影響成員函數,也不會影響 this 指針。

 

將派生類對象賦值給基類對象時,會舍棄派生類新增的成員,也就是“大材小用”,如下圖所示:


這種轉換關系是不可逆的,只能用派生類對象給基類對象賦值,而不能用基類對象給派生類對象賦值。理由很簡單,基類不包含派生類的成員變量,無法對派生類的成員變量賦值。同理,同一基類的不同派生類對象之間也不能賦值。

 

將派生類指針賦值給基類指針

除了可以將派生類對象賦值給基類對象(對象變量之間的賦值),還可以將派生類指針賦值給基類指針(對象指針之間的賦值)。我們先來看一個多繼承的例子,繼承關系為:


#include 
using namespace std;

//基類A
class A{
public:
    A(int a);
public:
    void display();
protected:
    int m_a;
};
A::A(int a): m_a(a){ }
void A::display(){
    cout<<"Class A: m_a="< display();
    pb = pd;
    pb -> display();
    pc = pd;
    pc -> display();
    cout

運行結果:

 

Class A: m_a=4

Class B: m_a=4, m_b=40

Class C: m_c=400

-----------------------

pa=0x9b17f8

pb=0x9b17f8

pc=0x9b1800

pd=0x9b17f8

 

本例中定義了多個對象指針,并嘗試將派生類指針賦值給基類指針。與對象變量之間的賦值不同的是,對象指針之間的賦值并沒有拷貝對象的成員,也沒有修改對象本身的數據,僅僅是改變了指針的指向。

1) 通過基類指針訪問派生類的成員

請讀者先關注第 68 行代碼,我們將派生類指針 pd 賦值給了基類指針 pa,從運行結果可以看出,調用 display() 函數時雖然使用了派生類的成員變量,但是 display() 函數本身卻是基類的。也就是說,將派生類指針賦值給基類指針時,通過基類指針只能使用派生類的成員變量,但不能使用派生類的成員函數,這看起來有點不倫不類,究竟是為什么呢?第 71、74 行代碼也是類似的情況。

 

pa 本來是基類 A 的指針,現在指向了派生類 D 的對象,這使得隱式指針 this 發生了變化,也指向了 D 類的對象,所以最終在 display() 內部使用的是 D 類對象的成員變量,相信這一點不難理解。

 

編譯器雖然通過指針的指向來訪問成員變量,但是卻不通過指針的指向來訪問成員函數:編譯器通過指針的類型來訪問成員函數。對于 pa,它的類型是 A,不管它指向哪個對象,使用的都是 A 類的成員函數,因為函數調用是根據類名、參數名等重新命名來調用的。

 

總結:

  • 編譯器通過指針來訪問成員變量,指針指向哪個對象就使用哪個對象的數據;
  • 編譯器通過指針的類型來訪問成員函數,指針屬于哪個類的類型就使用哪個類的函數。

 

2) 賦值后值不一致的情況

本例中我們將最終派生類的指針 pd 分別賦值給了基類指針 pa、pb、pc,按理說它們的值應該相等,都指向同一塊內存,但是運行結果卻有力地反駁了這種推論,只有 pa、pb、pd 三個指針的值相等,pc 的值比它們都大。也就是說,執行pc = pd;語句后,pc 和 pd 的值并不相等。

 

這非常出乎我們的意料,按照我們通常的理解,賦值就是將一個變量的值交給另外一個變量,不會出現不相等的情況,究竟是什么導致了 pc 和 pd 不相等呢?下一節會解釋。

 

將派生類引用賦值給基類引用

引用在本質上是通過指針的方式實現的,所以基類的引用也可以指向派生類的對象,并且它的表現和指針是類似的。

將派生類指針賦給基類指針

將派生類的指針賦值給基類的指針時也是類似的道理,編譯器也可能會在賦值前進行處理。

 

首先要明確的一點是,對象的指針必須要指向對象的起始位置。對于 A 類和 B 類來說,它們的子對象的起始地址和 D 類對象一樣,所以將 pd 賦值給 pa、pb 時不需要做任何調整,直接傳遞現有的值即可;而 C 類子對象距離 D 類對象的開頭有一定的偏移,將 pd 賦值給 pc 時要加上這個偏移,這樣 pc 才能指向 C 類子對象的起始位置。也就是說,執行pc = pd;語句時編譯器對 pd 的值進行了調整,才導致 pc、pd 的值不同。

 

 

------分隔線----------------------------
    ?分享到??
看看啦
国产日韩精品视频_2020久久国产最新免费观看_国内久久久久影院精品_日本一区二区视频在线

91久久在线| 久久人人看视频| 狠狠色综合色综合网络| 麻豆成人综合网| 亚洲一区二区视频在线| 好吊色欧美一区二区三区视频| 欧美激情一区二区三区在线视频观看 | 国产精品视频| 欧美人交a欧美精品| 久久久久国产精品人| 99热这里只有精品8| 亚洲福利在线视频| 国产精品女人久久久久久| 欧美激情一区二区三区蜜桃视频| 久久成年人视频| 欧美一区二区三区在线看| 洋洋av久久久久久久一区| 亚洲高清不卡| 狠狠色狠狠色综合日日91app| 国产精品久久二区二区| 欧美久久婷婷综合色| 欧美黑人国产人伦爽爽爽| 久久久亚洲国产天美传媒修理工 | 精品成人一区二区| 欧美午夜精品理论片a级按摩| 免费影视亚洲| 乱码第一页成人| 久久人人爽国产| 久久综合国产精品| 快播亚洲色图| 欧美国产91| 欧美激情成人在线| 欧美—级a级欧美特级ar全黄| 老巨人导航500精品| 久久综合免费视频影院| 久久先锋资源| 欧美激情国产日韩精品一区18| 毛片一区二区| 欧美区亚洲区| 国产精品久久久久久av下载红粉 | 国产精品wwwwww| 国产精品网站一区| 国产在线播放一区二区三区| 国户精品久久久久久久久久久不卡| 国产情人节一区| 亚洲丰满少妇videoshd| 亚洲三级网站| 亚洲欧美春色| 久久久久久久性| 欧美精品色网| 国产精品免费视频xxxx| 韩国自拍一区| 99精品欧美一区二区三区综合在线| 一区二区成人精品| 性色av一区二区三区在线观看| 久久激情五月激情| 欧美成人激情在线| 国产精品国产a级| 在线成人av.com| 一本色道久久精品| 久久久久88色偷偷免费| 欧美精品久久久久久| 国产精品亚洲网站| 91久久综合| 欧美在线网站| 欧美小视频在线| 樱桃视频在线观看一区| 亚洲一级电影| 欧美激情 亚洲a∨综合| 国产一区二区三区的电影| 亚洲美女中出| 久久久久久久久伊人| 国产精品热久久久久夜色精品三区| 激情综合在线| 欧美自拍丝袜亚洲| 国产精品视频不卡| 夜夜嗨一区二区| 欧美激情第一页xxx| 狠狠色丁香久久婷婷综合丁香| 亚洲一区在线观看免费观看电影高清| 欧美不卡视频一区| 一区二区三区在线免费播放| 亚欧成人在线| 国产伦精品一区二区三区免费 | 欧美久色视频| 亚洲国产成人91精品| 久久精品毛片| 国产精品成人在线| 一区二区三区四区精品| 欧美激情自拍| 亚洲欧洲视频在线| 欧美电影免费| 狠狠综合久久av一区二区小说| 亚洲午夜视频| 欧美视频在线观看免费网址| 亚洲精华国产欧美| 欧美岛国在线观看| 91久久视频| 欧美激情麻豆| 亚洲黄色在线视频| 欧美va亚洲va香蕉在线| 亚洲电影免费观看高清完整版在线观看| 亚洲一区二区三区四区五区黄| 欧美日韩小视频| 亚洲性线免费观看视频成熟| 欧美视频三区在线播放| 日韩天堂在线观看| 欧美激情一区二区三区在线视频观看| 国内揄拍国内精品少妇国语| 久久久久88色偷偷免费| 在线成人小视频| 另类国产ts人妖高潮视频| 亚洲激情第一页| 国产精品爱久久久久久久| 亚洲欧美日韩一区二区三区在线观看 | 久久久久88色偷偷免费| 亚洲第一免费播放区| 欧美激情中文不卡| 亚洲在线日韩| 国产日韩亚洲欧美精品| 久久人人九九| 亚洲美女诱惑| 国产欧美一区在线| 女女同性精品视频| 亚洲伊人网站| 一区二区三区在线观看视频| 久久永久免费| 日韩午夜电影在线观看| 国产精品乱码人人做人人爱| 久久精品五月婷婷| 一本大道久久a久久综合婷婷| 国产精品嫩草久久久久| 久久综合亚洲社区| 亚洲视频福利| 亚洲第一福利在线观看| 国产精品国产精品国产专区不蜜| 久久精品亚洲| 亚洲一区二区高清| 亚洲欧洲在线视频| 国产区二精品视| 欧美深夜影院| 欧美福利视频一区| 久久精品国产99国产精品澳门| 99精品欧美一区| 亚洲国产精品黑人久久久| 国产麻豆精品theporn| 欧美激情中文字幕一区二区| 久久国产精品电影| 亚洲欧美日韩视频二区| av成人激情| 国内精品久久久久久 | 性欧美办公室18xxxxhd| 亚洲国产精品一区制服丝袜| 国产日韩欧美视频在线| 欧美日在线观看| 欧美精品一区二区三区四区| 噜噜噜在线观看免费视频日韩| 久久99在线观看| 欧美一区二区三区久久精品茉莉花| 亚洲精选一区| 亚洲精品午夜精品| 在线不卡视频| 一区二区三区在线视频观看| 国产美女高潮久久白浆| 国产精品久久久久久久久久尿| 欧美精品在线一区二区三区| 久久影院午夜论| 欧美第一黄网免费网站| 狂野欧美一区| 欧美精品v日韩精品v韩国精品v | 欧美日韩国产bt| 欧美日韩和欧美的一区二区| 欧美国产视频日韩| 欧美成人午夜激情| 免费亚洲电影在线| 免费欧美日韩国产三级电影| 蜜臀va亚洲va欧美va天堂| 免费成人性网站| 欧美激情视频在线播放| 欧美日韩国产一区| 欧美日韩综合视频| 国产欧美视频一区二区三区| 国产丝袜美腿一区二区三区| 国产一区二区三区高清播放| 亚洲大片一区二区三区| 日韩亚洲精品电影| 亚洲欧美激情视频| 欧美主播一区二区三区| 久久先锋影音| 欧美日本三区| 国产精品久久久久av| 国产日韩欧美综合精品| 经典三级久久| 一区二区三区欧美成人| 欧美一级精品大片| 久久综合狠狠综合久久综合88| 欧美精品亚洲精品| 国产精品亚洲综合天堂夜夜| 在线观看国产欧美| 亚洲一区二区四区|