我的问题不是从基类构造函数中调用调用虚拟成员函数,而是基类构造函数中的指针到虚拟成员函数是否有效。
鉴于以下内容
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()”吗?
答案 0 :(得分:3)
“有效”是应用于指针时的特定术语。数据指针在指向对象或NULL
时有效;函数指针在指向函数或NULL
时有效,指向成员的指针在指向成员或NULL
时有效。
但是,根据你关于实际输出的问题,我可以推断出你想问别的东西。我们来看看你的vmember
函数 - 或者我应该说函数吗?显然有两个功能体。你本可以只派出一个虚拟的,这样就确认实际上有两个vmember
函数,它们都是虚拟的。
现在,问题在于取得成员函数的地址是否已经选择了实际的函数。您的实现显示它们没有,并且只有在实际取消引用指针时才会发生这种情况。
必须以这种方式工作的原因是微不足道的。获取成员函数的地址不涉及实际对象,这是解决虚拟调用所需的内容。让我告诉你:
namespace {
void (A::*test)() = &A::vmember;
A a;
B b;
(a.*test)();
(b.*test)();
}
初始化test
时,根本没有A
或B
类型的对象,但是可以采用&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 ++标准中没有规定获取虚函数的地址。
因此,无论如何即使它适合您,解决方案也不会是可移植的。