-6

在底层,一个类的 C++ 方法就像一个 C 函数,它的第一个参数是类的实例 - 或结构。

例如:

void Foo::Do();

将等效于 C 中的此声明:

void Do(Foo* this);

因此,在方法中使用成员 m_someMember 就像在 C 函数中使用 this->m_someMember。

经过这么多年的 C/C++ 编程经验,我最近才问自己:如果我从一个为 NULL 的实例指针调用一个方法怎么办?

我的猜测是:如果该方法根本没有引用任何成员,它什么时候会崩溃?

所以我做了一个快速测试(在 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

在对为空的实例指针进行崩溃的事后调试时,这可能会很麻烦。你可能认为如果这个方法无效就不能调用这个方法。

另一方面,如果方法被声明为虚拟的:

virtual void Foo::Do() { ... }

然后是一行:

pNullFoo->Do();

会产生缺页异常。为什么?因为具有虚拟方法的类的实例具有指向它们所属的子类虚拟方法的 vtable 的指针。所以编译器要做的第一件事就是让 pNullFoo 访问它的 vtable 成员,然后砰!

总之,这是一个更好的设计,让像 Do 这样的非上下文函数被实现为过程例程而不是方法,除非它们是虚拟的。

4

2 回答 2

4

在 NULL 指针上调用成员函数会调用未定义的行为。undefined 并不意味着它会崩溃,也不意味着它会做正确的事情——它是undefined。什么事情都可能发生。

我唯一一次在生产代码中看到这一点是使用 Microsoft 的CWnd::GetSafeHWND功能。但是由于他们编写了编译器,所以他们可以侥幸逃脱。

于 2013-08-28T18:27:39.003 回答
2

不需要访问任何成员数据的成员函数的正确设计是将它们定义为静态:

static void Do();

然后这样称呼它:

Foo::Do();

你甚至不需要一个对象或一个指针来做到这一点。

于 2013-08-28T18:31:29.637 回答