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

发布于 2024-09-06 17:35:57 字数 552 浏览 1 评论 0原文

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

鉴于以下情况,

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()”吗?

My question is not about calling a virtual member function from a base class constructor, but whether the pointer to a virtual member function is valid in the base class constructor.

Given the following

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;
}

Will this produce "In B::vmember()" for all compliant c++ compilers?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

过度放纵 2024-09-13 17:35:57

指针是有效的,但是您必须记住,当通过指针调用虚拟函数时,它始终根据左侧使用的对象的动态类型进行解析。这意味着当您从构造函数调用虚拟函数时,直接调用它还是通过指针调用它并不重要。在这两种情况下,调用都将解析为其构造函数当前正在工作的类型。当您在对象构造(或销毁)期间调用虚拟函数时,这就是虚拟函数的工作方式。

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

The pointer is valid, however you have to keep in mind that when a virtual function is invoked through a pointer it is always resolved in accordance with the dynamic type of the object used on the left-hand side. This means that when you invoke a virtual function from the constructor, it doesn't matter whether you invoke it directly or whether you invoke it through a pointer. In both cases the call will resolve to the type whose constructor is currently working. That's how virtual functions work, when you invoke them during object construction (or destruction).

Note also that pointers to member functions are generally not attached to specific functions at the point of initalization. If the target function is non-virtual, they one can say that the pointer points to a specific function. However, if the target function is virtual, there's no way to say where the pointer is pointing to. For example, the language specification explicitly states that when you compare (for equality) two pointers that happen to point to virtual functions, the result is unspecified.

江南烟雨〆相思醉 2024-09-13 17:35:57

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

但是,从您关于实际输出的问题,我可以推断您想问其他问题。让我们看看您的 vmember 函数 - 或者我应该说函数?显然有两个函数体。您可以仅将派生函数设为虚拟,这样也可以确认确实存在两个 vmember 函数,而它们恰好都是虚拟的。

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

它必须以这种方式工作的原因很简单。获取成员函数的地址并不涉及实际的对象,这是解析虚拟调用所需要的。让我告诉你:

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

当我们初始化 test 时,根本没有 AB 类型的对象,但是是否可以采取&A::vmember 的地址。然后,同一个成员指针可以与两个不同的对象一起使用。除了“In A::vmember()\n”和“In B::vmember()\n”之外,这还能产生什么?

"Valid" is a specific term when applied to pointers. Data pointers are valid when they point to an object or NULL; function pointers are valid when they point to a function or NULL, and pointers to members are valid when the point to a member or NULL.

However, from your question about actual output, I can infer that you wanted to ask something else. Let's look at your vmember function - or should I say functions? Obviously there are two function bodies. You could have made only the derived one virtual, so that too confirms that there are really two vmember functions, who both happen to be virtual.

Now, the question becomes whether when taking the address of a member function already chooses the actual function. Your implementations show that they don't, and that this only happens when the pointer is actually dereferenced.

The reason it must work this way is trivial. Taking the address of a member function does not involve an actual object, something that would be needed to resolve the virtual call. Let me show you:

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

When we initialize test, there is no object of type A or B at all, yet is it possible to take the address of &A::vmember. That same member pointer can then be used with two different objects. What could this produce but "In A::vmember()\n" and "In B::vmember()\n" ?

韵柒 2024-09-13 17:35:57

我在旧新事物(a Raymond Chen(有时被称为微软的 Chuck Norris)的博客)。

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

B b;

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

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

1 和 2 实际上调用了不同的函数……这确实令人惊讶。这也意味着您实际上无法使用指向成员函数的指针来阻止运行时分派:/

I have found a little explanation on the Old New Thing (a blog by Raymond Chen, sometimes referred to as Microsoft's Chuck Norris).

Of course it says nothing about the compliance, but it explains why:

B b;

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

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

1 and 2 actually invoke a different function... which is quite surprising, really. It also means that you can't actually prevent the runtime dispatch using a pointer to member function :/

洛阳烟雨空心柳 2024-09-13 17:35:57

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

I think no. Pointer to virtual member function is resolved via VMT, so the same way as call to this function would happen. It means that it is not valid, since VMT is populated after constructor finished.

§普罗旺斯的薰衣草 2024-09-13 17:35:57

IMO 它是实现定义的来获取虚拟函数的地址。这是因为虚拟函数是使用特定于编译器实现的vtable来实现的。由于在类构造器执行完成之前无法保证 vtable 是完整的,因此指向此类表(虚拟函数)中的条目的指针可能是实现定义的行为

我在 SO 这里几个月前;这基本上表明 C++ 标准中没有指定获取虚函数的地址。

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

IMO it is implementation defined to take address of a virtual function. This is because virtual functions are implemented using vtables which are compiler implementation specific. Since the vtable is not guaranteed to be complete until the execution of the class ctor is done, a pointer to an entry in such a table (virtual function) may be implementation defined behavior.

There is a somewhat related question that I asked on SO here few months back; which basically says taking address of the virtual function is not specified in the C++ standard.

So, in any case even if it works for you, the solution will not be portable.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文