2012-10-18 68 views
1

我在不同的平臺上使用G ++(4.5.2)時遇到了一個非常奇怪的行爲;這裏的代碼:爲什麼在C++中發生這種情況?

class Class 
{ 
private: 
    std::string rString; 

public: 
    Class() 
    { 
    this->rString = "random string"; 
    std::cout << "Constructor of Class" << std::endl; 
    } 

    virtual ~Class() 
    { 
    std::cout << "Destructor of Class" << std::endl; 
    } 

    void   say() const 
    { 
    std::cout << "Just saying ..." << std::endl; 
    if (this == NULL) 
     std::cout << "Man that's really bad" << std::endl; 
    } 

    void   hello() const 
    { 
    std::cout << "Hello " << this->rString << std::endl; 
    } 

}; 


int  main() 
{ 
    Class *c = NULL; 

    /* Dereferencing a NULL pointer results 
    in a successful call to the non-static method say() 
    without constructing Class */ 
    (*c).say(); // or c->say() 

    /* Dereferencing a NULL pointer and accessing a random 
    memory area results in a successful call to say() 
    as well */ 
    c[42000].say(); 

    /* Dereferencing a NULL pointer and accessing a 
    method which needs explicit construction of Class 
    results in a Segmentation fault */ 
    c->hello(); 

    return (0); 
} 

問題是,爲什麼主函數中的兩個第一條語句不會崩潰程序?這是未定義的行爲,還是編譯器僅僅調用Class :: say()就好像它是靜態的,因爲它不會在方法內部取消引用「this」指針?

+0

你的編輯是完全沒有必要的,而且很不恰當:'inline'在這裏沒有效果,因爲在類中定義的函數會自動'inline'。 –

+0

你錯了,編譯器可能或不可能在他將生成的二進制文件中「嵌入」這些方法。僅僅因爲這些方法是在類「Class」中定義和實現的,並不意味着它們實際上會被內聯。此外,使用'inline'關鍵字預設我的原型並不意味着此代碼將由編譯器內聯。我只是給編譯器一個提示,說爲這些方法生成序言可能會很重。 –

+0

不,你*錯了。你拿起了一些基本上是正確的,但不完全的半信息。 [在類中定義的成員函數(與剛剛聲明的相反)自動爲'inline'](http://msdn.microsoft.com/en-us/library/bw1hbe6y(v = vs80).aspx) (該標準的§7.1.2/ 3)。請注意,'inline'的作用不僅僅是建議編譯器執行調用內聯。 §7.1.2有更多細節。 –

回答

10

是的,這是未定義的行爲。 You cannot call a member function with a null pointer

實際上,前兩個確實工作,因爲this永遠不會被解除引用,因此您的未定義行爲不必像第三個那樣顯示,其中內存確實被錯誤訪問。

(在任何情況下,你死了一點點每次叫的時間裏面,所以不要做。)

+0

從技術上講,'this' *被使用(在'Class :: say'內部),但它永遠不會被解除引用。所以沒有無效的內存訪問。 – bitmask

+0

@bitmask:沒錯,我說得太鬆散。 :) – GManNickG

+0

這就是我們在Nit挑選者公司在這裏。 – bitmask

6

未定義行爲只是意味着會發生什麼沒有定義。這並不意味着「崩潰」。

未定義的行爲完全可以做任何事情,包括以您打算的方式工作。

1

這是未定義的行爲。既然你沒有使用函數作爲「虛擬」,這將只是靜態調用函數。

1

它是未定義的行爲,雖然它會出現在前兩個實例中,但它已被優化,無需使用對象本身的任何成員變量即可調用Class::say()(因此this->不被取消引用/使用導致sigserv),但3rd試圖訪問它的成員。同樣,以下可能會對像VC++這樣的其他編譯器產生誤解。

0

在內部,編譯器通過簡單地傳遞對象指針this作爲附加參數,或者連同堆棧上的所有適當參數,來實現對非靜態非虛函數的調用。

該標準沒有規定當調用成員函數的某些內容不是持有相應類或結構的實際對象的內存位置時應該發生的情況。要求這樣做將需要非noop運行時檢查(如果可能的話),因此不合理的要求從符合實施。

打字系統已經確保您已經用盡了方法來調用具有錯誤類型對象的成員函數,但是檢查NULLnullptr調用不能被指針類型覆蓋。
如果你希望你的二進制崩潰時,成員函數被調用,空指針,使得成員函數virtual應該做的工作,因爲編譯器將取消引用的this vptr的和操作系統將回應向你展示手指。

相關問題