2012-10-18 64 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的和操作系统将回应向你展示手指。

相关问题