此问题已在此处有答案:
What will happen when I call a member function on a NULL object pointer? [duplicate](6个回答)
5年前关闭。
假设我们有一节课
class A
{
int x;
public:
void sayHi()
{
cout<<"Hi";
}
};
int main()
{
A *a=NULL;
a->sayHi();
}
上面的代码将在Turbo C(我测试的地方)上编译,并打印Hi
作为输出。
我期待崩溃,因为a
是NULL
。如果我把sayHi()
函数设为虚拟函数,它会说
Abnormal temination(Segmentation fault in gcc)
我知道很多都是依赖于实现的,但如果有人能对任何实现做一些说明,或者只是给予一个概述,那就太好了。
4条答案
按热度按时间pu3pd22g1#
很明显,代码有未定义的行为,也就是说,你得到的任何东西都是偶然的。也就是说,系统在调用非虚成员函数时不需要知道对象:只能根据签名调用它。此外,如果一个成员函数不需要访问一个成员,它根本不需要真正需要一个对象,可以直接运行。这是在代码打印一些输出时观察到的。然而,这是否是系统实现的方式并没有定义,也就是说,没有任何东西说它是有效的。
当调用虚函数时,类型系统开始查看与对象相关联的类型信息记录。当在
NULL
指针上调用虚函数时,不存在这样的信息,尝试访问它可能会导致某种崩溃。尽管如此,它并不需要,但它对大多数系统都是如此。顺便说一句,
main()
always返回int
。ryevplcw2#
在C++中,类的方法不存储在该类的示例中。它们只是一些“特殊”函数,除了程序员指定的参数外,它们还透明地接受
this
指针。在本例中,
sayHi()
方法不引用任何类字段,因此,this
指针(即NULL
)永远不会被跟踪。但请不要搞错,这仍然是未定义的行为。当你调用这个函数时,你的程序可能会选择向你的联系人列表发送恶意的电子邮件。在这个特殊的例子中,它做了最糟糕的事情,而且似乎起作用了。
自从我回答了这个问题之后,
virtual
方法的情况就被添加了进来,但是我不会细化我的答案,因为它已经被其他人的答案包含了。7eumitmz3#
通常,从类示例化的对象的布局如下:
v_ptr
是指向v-table的指针,其中包含对象的虚拟函数和RTTI数据的地址。没有虚函数的类没有v-table,相应的对象没有v_ptr
。在上面的例子中,
class A
没有虚方法,因此没有v-table。这意味着要调用的sayHi()
的实现可以在编译时确定,并且是不变的。编译器生成的代码将隐式
this
指针设置为a
,然后跳转到sayHi()
的开头。由于实现不需要对象的内容,所以当指针为NULL
时它可以工作是一个令人高兴的巧合。如果你要使
sayHi()
成为虚拟的,编译器无法在编译时确定要调用的实现,所以生成的代码在v表中查找函数的地址并调用它。在a
是NULL
的示例中,编译器读取地址0
的内容,导致中止。uxhixvfz4#
如果你调用一个类的非虚方法,对于编译器来说,知道这个函数属于哪个类就足够了,并且通过解引用(尽管是NULL)指向一个类的指针来调用这个方法,编译器就可以得到这个信息。**sayHi()**方法几乎只是一个函数,它将指向类示例的指针作为隐藏参数。这个指针是NULL,但是如果你没有在方法中引用任何属性,这也没关系。
一旦你把这个方法设为虚拟的,情况就改变了。编译器在编译时不再知道什么代码与方法相关联,必须在运行时弄清楚。它所做的是查看一个基本上包含所有虚方法的函数指针的表;该表与类示例相关联,因此它会查看与NULL指针相关的内存片段,因此在这种情况下会崩溃。