我在不同平台上使用G ++(4.5.2)遇到了一种非常奇怪的行为;这是代码:
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);
}
问题是,为什么main函数中的两个第一个语句不会使程序崩溃?这是未定义的行为,还是编译器只是调用Class :: say()就好像它是静态的,因为它不会在方法中取消引用“this”指针?
答案 0 :(得分:10)
是的,这是未定义的行为。 You cannot call a member function with a null pointer
在实践中,前两个确实有效,因为this
永远不会被解除引用,因此你的未定义行为不必像第三个那样表现出来,其中内存确实被错误地访问了。
(在所有情况下,每次调用时你都会死掉一点,所以不要这样做。)
答案 1 :(得分:6)
未定义的行为仅表示未定义的内容。这并不意味着“崩溃”。
未定义的行为可以做任何事情,包括按照您的意图工作。
答案 2 :(得分:1)
这是未定义的行为。由于您没有将函数用作“虚拟”,因此只能静态调用函数。
答案 3 :(得分:1)
这是未定义的行为,虽然它会出现在前两个实例中,但它已经被优化以调用Class::say()
而不使用对象本身的任何成员变量(因此,这> - 未被取消引用/使用导致sigserv) ,但第三次试图访问它的成员。同样,以下可能会在VC ++等其他编译器上出错。
答案 4 :(得分:0)
在内部,编译器通过简单地将对象指针this
作为附加参数传递或者更确切地说与堆栈中的所有适当参数一起传递来实现对非静态非虚函数的调用。
该标准并未强制要求在使用不包含相应类或结构的实际对象的内存位置调用成员函数时发生的情况。要求这样做需要非noop运行时检查(如果可能的话),因此从一致的实现中询问是不合理的。
打字系统已经确保您已经不再使用错误类型的对象调用成员函数,但检查NULL
或nullptr
来电不能有意义地覆盖指针类型。
如果您希望在使用空指针调用成员函数时二进制文件崩溃,那么使成员函数virtual
应该完成工作,因为编译器将 取消引用{的vptr {1}}操作系统将通过向您显示手指进行响应。