在幕后,类的C ++方法就像一个C函数,其第一个参数是类的实例 - 或结构。
例如:
void Foo::Do();
将等同于C:
中的此声明void Do(Foo* this);
因此,在方法中使用成员m_someMember就像在C函数中使用this-> m_someMember。
经过这么多年的C / C ++编程经验,我最近才问自己:如果我从一个实例指针调用一个方法,该怎么办?
我的猜测是:如果该方法完全没有成员,那为什么会崩溃?
所以我做了一个快速测试(在Windows平台上,使用Visual C ++ 2008):
class Foo
{
public:
Foo() {}
virtual ~Foo() {}
void Do();
};
void Foo::Do()
{
cout << "Calling 'Do' for " << this << endl;
}
int _tmain(int argc, _TCHAR* argv[])
{
Foo foo;
foo.Do();
Foo* pNullFoo = 0;
pNullFoo->Do();
return 0;
}
其中输出如下:
Calling 'Do' for 0038FE5C
Calling 'Do' for 00000000
在对null为空的实例指针进行事后调试时,这可能会很麻烦。您可能认为如果此方法无效,则无法调用此方法。
另一方面,如果该方法被声明为虚拟:
virtual void Foo::Do() { ... }
然后行:
pNullFoo->Do();
将产生页面错误异常。为什么?因为具有虚方法的类的实例具有指向它们所属的子类虚拟方法的vtable的指针。 所以编译器要做的第一件事就是让pNullFoo访问它的vtable成员,然后爆炸!
总之,这是更好的设计,有非上下文功能,比如Do被实现为程序例程而不是方法,除非它们是虚拟的。
答案 0 :(得分:4)
在NULL指针上调用成员函数会调用未定义的行为。未定义并不意味着它会崩溃,也不意味着它会做正确的事情 - 它是 undefined 。任何事情都可能发生。
我在生产代码中看到这一点的唯一一次是微软的CWnd::GetSafeHWND
功能。但是既然他们编写了编译器,他们就可以逃脱它。
答案 1 :(得分:2)
不需要访问任何成员数据的成员函数的正确设计是将它们定义为静态:
static void Do();
然后称之为:
Foo::Do();
你甚至不需要有一个对象或指针来做到这一点。