亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

由static_cast和dynamic_cast到C++對象占用內存的全面分析

 更新時間:2017年01月05日 09:29:55   投稿:jingxian  
下面小編就為大家?guī)硪黄蓅tatic_cast和dynamic_cast到C++對象占用內存的全面分析。小編覺得挺不錯的,現(xiàn)在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧

static_cast和dynamic_cast是C++的類型轉換操作符。編譯器隱式執(zhí)行的任何類型轉換都可以由static_cast顯式完成,即父類和子類之間也可以利用static_cast進行轉換。而dynamic_cast只能用于類之間的轉換。那么dynamic_cast的存在還有什么意義呢?因為dynamic_cast提供了一個重要的特性:運行時類型檢查來保證轉換的安全性。

用static_cast轉換存在的危險

我們知道,一個基類指針不需要進行明確的轉換操作,就可以指向基類對象或者派生類對象。比如:

class Base{
  //…
};
class Derived{
  //…
};
int main{
  Base *p = new Base();//OK
  Base *p = new Derived();//OK
}

上面的兩種定義都是正確的,那么如果想反過來,讓一個子類指針指向父類對象呢?如下代碼:

class Base{
  //…
};
class Derived{
  //…
};
int main{
  Derived *p = new Base();//error
  Derived *p = static_cast<Derived*>(new Base());//OK
}

如果直接把Base類型的指針轉換為Derived類型的指針,那么編譯時會報錯。如果在轉換時加上static操作符則可以順利通過編譯。但是這種做法是十分危險的,在運行期時可能會出現(xiàn)一些難以預測和查找的錯誤。如下面代碼:

class Base{
  public:
    Base():m_b(4){};
    int m_b;
void m_funcB(){cout << "base" << endl;};
};
class Derived:public Base{
  public:
    Derived():m_d(3){};
    int m_d;
    void m_funcD(){cout << "derived" << endl;};
};
int main(){
  Derived* p = static_cast<Derived*>(new Base());
  cout << p->m_d << endl;
  p->m_funcD();
}


雖然p是Derived類型的指針,但是實際卻指向了Base對象,而Base對象不存在m_d這個數(shù)據成員,因此輸出的結果不可預測(在我的機子上一直輸出0)。正是這種不可預測才導致難以追蹤的錯誤,試想,如果執(zhí)行這段代碼會崩潰,那么還是比較還排查的,但是現(xiàn)在并不一定崩潰,只是執(zhí)行的結果和我們的預測不一致,可能將導致連環(huán)的邏輯錯誤,這就像給自己挖了一個坑或者定時炸彈。

但是很奇怪的一點是,執(zhí)行p->m_funcD()這一句后,居然可以打印出”derived”。這是怎么回事?m_funcD明明是類Derived的函數(shù),而且類Base里并不存在這個函數(shù),這個我們留在后面說明。

利用dynamic_cast保證轉換的安全

原則上,我們不應該讓子類指針指向父類的對象。但是如果寫下了上面這樣的代碼,我們希望可以有一種檢查機制可以幫助我們發(fā)現(xiàn)這個問題,這樣就可以避免對轉換后的指針進行操作,造成不可預料的后果。

C++是支持運行期類型識別的(RTTI),這種機制除了幫助我們實現(xiàn)多態(tài),還能在類型轉換時進行安全檢查?;氐缴厦娴拇a,我們稍作修改:

class Base{
  public:
    Base():m_b(4){};
    int m_b;
    virtual void m_funcB(){cout << "base" << endl;};
};

class Derived:public Base{
  public:
    Derived():m_d(3){};
    int m_d;
    void m_funcD(){cout << "derived" << endl;};
};
int main(){
  Derived* p = dynamic_cast<Derived*>(new Base());
  cout << p->m_d << endl;
  p->m_funcD();
}

運行結果會是什么?程序崩潰了。原因就是我們執(zhí)行了p->m_d,而p這個時候是一個空指針。原因在于利用dynamic_cast進行類型轉換時會進行安全檢查,在這里我們將一個父類指針轉換為子類指針,這被認為是一個無效操作,因此返回NULL,因此p成了空指針。所以當我們利用dynamic_cast進行了轉換后,只要對得到的指針進行檢查,就可以知道轉換是否成功。static_cast則沒有提供這種檢查,這就是dynamic_cast比static_cast安全的原因。

現(xiàn)在稍微離開一下正題,如果把打印m_d這句注釋掉,執(zhí)行p->m_funcD()這一句后,發(fā)現(xiàn)還是能夠打印出”derived”。等我們總結dynamic_cast和static_cast的區(qū)別后就對這個現(xiàn)象進行討論。

dynamic_cast和static_cast的區(qū)別:

dynamic_cast可以實現(xiàn)運行期類型安全檢查,是一種更加安全的方法,但是僅僅對多態(tài)類型有效,而且只能用于指針或者引用類型的轉換上。static_cast則可應用與任何類型,而且不需要類型實現(xiàn)了多態(tài)。static_cast的應用更加廣泛,但是dynamic_cast更加強大和安全。

對象占用內存分析:

下面看一下我們兩次提到的現(xiàn)象:為什么通過一個實際指向了基類對象的子類指針調用子類的方法,既然沒有出現(xiàn)錯誤并且可以順利調用?

一個類無非就是包含兩種成員:數(shù)據和方法。那么當我們實例化出一個對象的時候,這個對象包含了哪些東西,實際占用的內存大小是多少?寫一段代碼試一試:

class Base{
  public:
    Base():m_b(4){};
    int m_b;
    virtual void m_funcB(){cout << "base" << endl;};
};
class Derived:public Base{
  public:
    Derived():m_d(3){};
    int m_d;
    void m_funcD(){cout << "derived" << endl;};
};
int main(){
  cout << sizeof(Base) << endl;  
  cout << sizeof(Derived) << endl;
}

打印出的結果分別是8和12。

那么一個類或者說對象占用的內存到底怎么計算呢?以Base為例,首先成員變量m_b占用了4個字節(jié),其次,由于m_funcB是虛函數(shù),因此要有一張?zhí)摵瘮?shù)表,其實就是一個指向表的指針,無論是什么類型的指針,占用的大小總是4字節(jié),因此base占用了8個字節(jié)的大小。而Derived除了繼承了Base的成員m_b之外,也保存了虛函數(shù)表的地址,還有自己的成員變量m_d,所以占用了12個字節(jié)。

或者有人會問:構造函數(shù)呢?還有虛函數(shù)本身不是還有函數(shù)體嗎?難道不用計算進去?確實,類的函數(shù)是不會存儲在實例化出來的對象里的,試想,對于每個對象,函數(shù)實現(xiàn)都是一樣的,如果每實例化一個對象就存儲一次函數(shù)體,不是毫無必要并且對內存使用而言是極大的浪費?

函數(shù)編譯出來后是作為代碼的一部分放在代碼段中的,因此只要我們定義了Derived指針,無論這個實際指針指向什么對象,由于程序“事先”已經知道了這個方法屬于哪個類,只要指針的類型正確,都可以正確找到調用函數(shù)的入口。所以即使我們的代碼這么寫,也是可以正確運行的:

void * p2 = (int*)0;
Derived* p3= (Derived*)p2;
cout << p3->m_funcD() << endl;

不管把什么地址賦給p2,都能正確地執(zhí)行m_funcD函數(shù)。當然如果p3定義成其他類型,那么編譯就會出錯。

如果執(zhí)行以下代碼:

void * p2 = (int*)0;
Derived* p3= (Derived*)p2;
cout << p3->m_d << endl;

那么程序就會出現(xiàn)錯誤了,因為和成員函數(shù)不同,成員變量是每個對象都會在內存中用實際的內存地址存儲,所以說成員函數(shù)屬于類,成員變量屬于各自的對象。

以上就是小編為大家?guī)淼挠蓅tatic_cast和dynamic_cast到C++對象占用內存的全面分析全部內容了,希望大家多多支持腳本之家~

相關文章

  • C語言實現(xiàn)乒乓球比賽

    C語言實現(xiàn)乒乓球比賽

    這篇文章主要為大家詳細介紹了C語言實現(xiàn)乒乓球比賽,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2020-05-05
  • C++中的const的使用詳解

    C++中的const的使用詳解

    這篇文章主要介紹了 C++中的const的使用詳解的相關資料,需要的朋友可以參考下
    2017-05-05
  • c++冒泡排序示例分享

    c++冒泡排序示例分享

    冒泡排序是一種計算機科學領域的較簡單的排序算法,這篇文章主要介紹了c++冒泡排序示例,需要的朋友可以參考下
    2014-03-03
  • C++中類的轉換函數(shù)你了解嗎

    C++中類的轉換函數(shù)你了解嗎

    這篇文章主要為大家詳細介紹了C++中類的轉換函數(shù),文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下,希望能夠給你帶來幫助
    2022-03-03
  • C++超詳細講解構造函數(shù)

    C++超詳細講解構造函數(shù)

    C++的構造函數(shù)的作?:初始化類對象的數(shù)據成員。即類的對象被創(chuàng)建的時候,編譯系統(tǒng)對該對象分配內存空間,并?動調?構造函數(shù),完成類成員的初始化。構造函數(shù)的特點:以類名作為函數(shù)名,?返回類型
    2022-06-06
  • C語言實現(xiàn)放煙花的程序

    C語言實現(xiàn)放煙花的程序

    這篇文章主要為大家詳細介紹了C語言實現(xiàn)放煙花的程序,有音樂播放,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2021-02-02
  • C++之異常處理詳解

    C++之異常處理詳解

    C++中處理異常的過程是這樣的:在執(zhí)行程序發(fā)生異常,可以不在本函數(shù)中處理,而是拋出一個錯誤信息,把它傳遞給上一級的函數(shù)來解決,上一級解決不了,再傳給其上一級,由其上一級處理
    2013-08-08
  • C語言使用廣度優(yōu)先搜索算法解決迷宮問題(隊列)

    C語言使用廣度優(yōu)先搜索算法解決迷宮問題(隊列)

    這篇文章主要介紹了C語言使用廣度優(yōu)先搜索算法解決迷宮問題,結合迷宮問題分析了C語言隊列廣度優(yōu)先搜索算法的相關使用技巧,需要的朋友可以參考下
    2017-09-09
  • C++的內存管理詳細解釋

    C++的內存管理詳細解釋

    這篇文章主要介紹了C/C++中的內存管理小結,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2021-09-09
  • 在C語言中轉換時間的基本方法介紹

    在C語言中轉換時間的基本方法介紹

    這篇文章主要介紹了在C語言中轉換時間的基本方法,分別是mktime()函數(shù)和localtime()函數(shù)的使用,需要的朋友可以參考下
    2015-08-08

最新評論