c++ 記憶體 繼承

sunmenggmail發表於2012-04-15

http://blog.csdn.net/randyjiawenjie/article/details/6829823


1.C++的虛擬函式是怎樣實現的?

      C++的虛擬函式使用了一個虛擬函式表來存放了每一個虛擬函式的入口地址,這個虛擬函式表又使用一個虛擬函式指標來進行訪問。通常,虛擬函式指標都放在物件模型的第一個位置存放,這樣訪問徐函式指標的速度最快,沒有偏移量。通過虛擬函式指標,找到虛擬函式表,進而再做一個次偏移量計算,來得到真實虛擬函式的入口地址,從而訪問了虛擬函式。這樣看來,訪問一個虛擬函式將使用兩次間接計算,故要失去一些時間效率。另外,使用了虛擬函式,那麼就要消耗一些空間,存放虛擬函式指標。但是,這都是值得的,為了實現多型。

2. 虛擬函式表是每個物件一個還是每個類一個呢?

     這個事搜狗的面試題目。其實討論過,我之前看到這個問題,立馬就想到了this指標。http://topic.csdn.net/u/20081031/16/6784c211-a475-4af5-a331-c3df887a2d0c.html比較詳細的討論。

    每個類只有若干個虛擬函式表,每個物件可以有若干個虛擬函式表指標。(這都是在多重繼承的情況下出現的。單重繼承的情況下,就是一個物件有一個虛擬函式表指標,每一個類有一個虛擬函式表)函式呼叫的時候會將this指標作為引數發過去,所以沒有必要為每一個物件做一個虛擬函式表。
     “每個類有一個或多個虛擬函式表,單純的繼承下來的類有一個虛擬函式表,虛繼承下來的類就有可能有多個虛擬函式表(只是有可能),物件沒有虛擬函式表,但是有指向虛擬函式表的指標。物件之所一能呼叫“自己的函式”,是因為類的成員函式不知不覺的在編譯時多了一個this指標引數來識別成員資料屬於哪個物件。”

    關於這個問題,在《more effective c++》中的條款24有詳細敘述,的確是每一個類的一個虛擬函式表。至於放的的位置,書中也有講述。

3.函式指標和指標函式的區別?

     就想通過這個問題來判斷我的C水平?C語言我看的數都有三本。

    函式指標,一個指標,指向了函式的呼叫地址。  void (*fun)(void)

  指標函式,就是一個函式的返回值是指標。 int *  fun(void)

  這道題目我真的很冤,指標函式,我真的沒有聽說過。

   陣列指標和指標陣列我知道什麼區別

4. C++ 深拷貝和淺拷貝的區別?如果要delete一個淺拷貝,需要注意什麼條件?

   現在有一個指標p1指向了一個記憶體空間m1;

   淺拷貝就是再用一個新的指標p2指向這片記憶體空間m1;

   深拷貝就是用一個新的指標p3指向m1的副本m2

   delete一個淺拷貝,首先要測試是不是有其它的指標還在指向這片空間。不然,直接就是野指標了。為什麼野指標那麼是絕對要禁止的?野指標現在指向了一片記憶體區間,這片記憶體區間以前是有意義的,現在被釋放了,作業系統可能會講這邊區間放上其它程式資料。那麼,野指標仍然指向了這片區間。如果此時使用野指標,改變了這片記憶體的資料,那麼程式應該直接崩潰。而且,這樣子的崩潰可能出現,可能等一會兒出現,不定。帶來的bug很難查詢。

5.  dynamic_cast的用法?

http://blog.csdn.net/wingfiring/article/details/633033

作為四個內部型別轉換操作符之一的dynamic_cast和傳統的C風格的強制型別轉換有著巨大的差別。除了dynamic_cast以外的轉換,其行為的都是在編譯期就得以確定的,轉換是否成功,並不依賴被轉換的物件。而dynamic_cast則不然。在這裡,不再討論其他三種轉換和C風格的轉換。
首先,dynamic_cast依賴於RTTI資訊,其次,在轉換時,dynamic_cast會檢查轉換的source物件是否真的可以轉換成target型別,這種檢查不是語法上的,而是真實情況的檢查。
先看RTTI相關部分,通常,許多編譯器都是通過vtable找到物件的RTTI資訊的,這也就意味著,如果基類沒有虛方法,也就無法判斷一個基類指標變數所指物件的真實型別, 這時候,dynamic_cast只能用來做安全的轉換,例如從派生類指標轉換成基類指標.而這種轉換其實並不需要dynamic_cast參與.
也就是說,dynamic_cast是根據RTTI記載的資訊來判斷型別轉換是否合法的。

  1. struct B1{  
  2.     virtual ~B1(){}  
  3. };  
  4. struct B2{  
  5.     virtual ~B2(){}  
  6. };  
  7. struct D1 : B1, B2{};  
  8. int main()  
  9. {  
  10.     D1 d;  
  11.     B1* pb1 = &d;  
  12.     B2* pb2 = dynamic_cast<B2*>(pb1);//L1  
  13.     B2* pb22 = static_cast<B2*>(pb1);  //L2  
  14.     return 0;  
  15. }  

pb2將會為NULL指標,pb22將會編譯出錯。

  1. <span style="font-size:16px;">  
  2. </span>  
http://baike.baidu.com/view/1745213.htm

dynamic_cast < type-id > ( expression )  

該運算子把expression轉換成type-id型別的物件。Type-id必須是類的指標、類的引用或者void*;  

如果type-id是類指標型別,那麼expression也必須是一個指標,如果type-id是一個引用,那麼expression也必須是一個引用。  dynamic_cast主要用於類層次間的上行轉換和下行轉換,還可以用於類之間的交叉轉換。  

在類層次間進行上行轉換時,dynamic_cast和static_cast的效果是一樣的;  

在進行下行轉換時,dynamic_cast具有型別檢查的功能,比static_cast更安全。

http://blog.csdn.net/wingfiring/article/details/633033

dynamic_cast 確實很好地解決了我們的問題,但也需要我們付出代價,那就是與 typeid 相比,dynamic_cast 不是一個常量時間的操作。為了確定是否能完成強制型別轉換,dynamic_cast`必須在執行時進行一些轉換細節操作。因此在使用 dynamic_cast 操作時,應該權衡對效能的影響


5. 請閱讀以下的程式碼,試試看,你能發現錯誤或者不恰當地方嗎?(提示:   不止一處)
http://topic.csdn.net/t/20061129/14/5194019.html

  1. class   Wheel{};  
  2. class   Color{};  
  3.   
  4. class   Car  
  5. {  
  6. private:  
  7.     Wheel*   wheels;  
  8. public:  
  9.     Car(int   wheel_count){wheels   =   new   Wheel[wheel_count];}  
  10.     ~Car(){delete   wheels;}  
  11. };  
  12.   
  13. class   Roadster   :   public   Car  
  14. {  
  15. public:  
  16.     Color   color;  
  17.   
  18.     Roadster(){};  
  19.     Roadster(const   Roadster&   rs)  
  20.     {  
  21.         this-> color   =   rs.color;  
  22.     }  
  23.     ~Roadster(){};  
  24.     Roadster&   operator=(const   Roadster&   rhs)  
  25.     {  
  26.         this-> color   =   rhs.color;  
  27.     }  
  28.     const   Roadster&   clone()  
  29.     {  
  30.         Roadster   the_new   =   *this;  
  31.         return   the_new;  
  32.     }  
  33. };  
  34.   
  35. int   main(int   argc,   char*   argv[]){  
  36.     Roadster*   yours   =   new   Roadster;  
  37.     Roadster   mine;     mine   =   yours-> clone();  
  38.     Car*   pCar   =   yours;  
  39.     delete   pCar;  
  40.     return   0;  
  41. };   

修改如下:

  1. class   Wheel{};  
  2. class   Color{};  
  3.   
  4. class   Car  
  5. {  
  6. private:  
  7.     Wheel*   wheels;  
  8.     //1.   沒有儲存wheels的下標  
  9.     //2.   最好用vector  
  10. public:  
  11.     Car(int   wheel_count){wheels   =   new   Wheel[wheel_count];}      
  12.     ~Car(){delete   wheels;}  
  13.     //3.   解構函式要虛擬  
  14.     //4.   delete   wheels;   ->   delete   []wheels;  
  15.     //5.   要提供拷貝建構函式  
  16.     //6.   要過載賦值操作符  
  17. };  
  18.   
  19. class   Roadster   :   public   Car  
  20. {  
  21. public:  
  22.     Color   color;  
  23.     //7.   成員變數要私有  
  24.   
  25.     Roadster(){};  
  26.     //8.   要呼叫父類的建構函式  
  27.     Roadster(const   Roadster&   rs)  
  28.     {  
  29.         this-> color   =   rs.color;  
  30.     }  
  31.     //9.   要呼叫父類的拷貝建構函式  
  32.     ~Roadster(){};  
  33.     //10.   解構函式要虛擬(原則:解構函式要麼虛擬且公有,要麼保護且非虛擬)  
  34.     Roadster&   operator=(const   Roadster&   rhs)  
  35.     {  
  36.         this-> color   =   rhs.color;  
  37.     }  
  38.     //11.   需要呼叫父類的賦值操作符  
  39.     //12.   沒有返回值  
  40.     //13.   返回的型別最好是const   Roadster&,   而不是Roadster&  
  41.     const   Roadster&   clone()  
  42.     {  
  43.         Roadster   the_new   =   *this;  
  44.         return   the_new;  
  45.     }  
  46.     //14.   不能返回區域性變數的引用  
  47.     //15.   按照這裡的邏輯,   其實可以直接返回*this  
  48. };  
  49.   
  50. int   main(int   argc,   char*   argv[]){  
  51.     //16.   這裡的{}風格與前面的不一致  
  52.     Roadster*   yours   =   new   Roadster;  
  53.     Roadster   mine;     mine   =   yours-> clone();  
  54.     //17.   最好是一行只有一條語句,   不要兩條語句放在一行  
  55.     //18.   其實這裡可以直接是Roadster   mine   =   yours-> clone();  
  56.     Car*   pCar   =   yours;  
  57.     delete   pCar;  
  58.     //我認為這裡是沒有問題的,作為Car類的使用者,他在這裡用是沒有問題的,錯誤的只是Car類的程式碼  
  59.     return   0;  
  60. };   

6.C++程式進入main函式之前,退出main函式之後會做些什麼?


main函式執行之前,主要就是初始化系統相關資源:
1.設定棧指標
2.初始化static靜態和global全域性變數,即data段的內容
3.將未初始化部分的賦初值:數值型short,int,long等為0,bool為FALSE,指標為NULL,等等,即.bss段的內容
4.執行全域性構造器,C++中建構函式
5.將main函式的引數,argc,argv等傳遞給main函式,然後才真正執行main函式

main 函式之後會執行相反的工作。

相關文章