指向虚拟成员函数的指针在基类的构造函数中是否有效?

时间:2010-06-22 11:50:46

标签: c++ member-function-pointers

我的问题不是从基类构造函数中调用调用虚拟成员函数,而是基类构造函数中的指针到虚拟成员函数是否有效。

鉴于以下内容

class A
{
    void (A::*m_pMember)();

public:
    A() :
        m_pMember(&A::vmember)
    {
    }

    virtual void vmember()
    {
        printf("In A::vmember()\n");
    }

    void test()
    {
        (this->*m_pMember)();
    }
};

class B : public A
{
public:
    virtual void vmember()
    {
        printf("In B::vmember()\n");
    }
};

int main()
{
    B b;
    b.test();

    return 0;
}

对于所有兼容的c ++编译器,这会产生“In B :: vmember()”吗?

6 个答案:

答案 0 :(得分:3)

“有效”是应用于指针时的特定术语。数据指针在指向对象或NULL时有效;函数指针在指向函数或NULL时有效,指向成员的指针在指向成员或NULL时有效。

但是,根据你关于实际输出的问题,我可以推断出你想问别的东西。我们来看看你的vmember函数 - 或者我应该说函数吗?显然有两个功能体。你本可以只派出一个虚拟的,这样就确认实际上有两个vmember函数,它们都是虚拟的。

现在,问题在于取得成员函数的地址是否已经选择了实际的函数。您的实现显示它们没有,并且只有在实际取消引用指针时才会发生这种情况。

必须以这种方式工作的原因是微不足道的。获取成员函数的地址不涉及实际对象,这是解决虚拟调用所需的内容。让我告诉你:

namespace {
  void (A::*test)() = &A::vmember;
  A a;
  B b;
  (a.*test)();
  (b.*test)();
}

初始化test时,根本没有AB类型的对象,但是可以采用&A::vmember的地址。然后,该相同的成员指针可以与两个不同的对象一起使用。这会产生什么,但“在A :: vmember()\ n”和“在B :: vmember()\ n”中?

答案 1 :(得分:3)

指针有效,但是你必须记住,当通过指针调用虚函数时,它总是根据左边使用的对象的动态类型来解析 - 手边。这意味着当您从构造函数调用虚函数时,无论是直接调用它还是通过指针调用它都无关紧要。在这两种情况下,调用将解析为其构造函数当前正在工作的类型。当你在对象构造(或破坏)期间调用它们时,这就是虚函数的工作方式。

另请注意,指向成员函数的指针通常不会在初始化时附加到特定函数。如果目标函数是非虚拟的,则可以说指针指向特定函数。但是,如果目标函数是虚函数,则无法指出指针指向的位置。例如,语言规范明确指出,当您比较(相等)两个恰好指向虚函数的指针时,结果是未指定

答案 2 :(得分:1)

阅读this article以深入讨论成员函数指针以及如何使用它们。这应该回答你所有的问题。

答案 3 :(得分:1)

我在Old New Thing(Raymond Chen的博客,有时也被称为微软的查克诺里斯)上找到了一点解释。

当然它没有说明合规性,但它解释了原因:

B b;

b.A::vmember(); // [1]

(b.*&A::vmember)(); // [2]

1和2实际上调用了一个不同的函数......这真是令人惊讶。它还意味着您无法使用指向成员函数的指针实际阻止运行时调度:/

答案 4 :(得分:0)

我认为不是。通过VMT解析指向虚拟成员函数的指针,因此与调用此函数的方式相同。这意味着它无效,因为在构造函数完成后填充了VMT。

答案 5 :(得分:-1)

IMO implementation defined 获取虚拟功能的地址。这是因为虚函数是使用 vtables 实现的,它们是特定于编译器实现的。由于在完成类ctor的执行之前不保证vtable完成,因此指向这样的表(虚函数)中的条目的指针可以是implementation defined behavior

几个月前,我在问here提出了一个相关的问题;基本上说,在C ++标准中没有规定获取虚函数的地址。

因此,无论如何即使它适合您,解决方案也不会是可移植的。