C++不支持非成员虚拟功能的原因是什么?

What is the reason non-member virtual functions are not supported in C++

本文关键字:是什么 功能 不支持 成员 虚拟 C++      更新时间:2023-10-16

我很想知道C++中没有非成员虚函数的原因是什么。特别是考虑到它只是在你想要实现它时增加代码层,因为你可以定义一个虚拟成员函数,然后从非成员函数调用它。

编辑: 仅供参考,您可以这样做:

struct Base
{
virtual void say() const
{
std::cout << "Basen";
}
};
struct Derived : public Base
{
void say() const final
{
std::cout << "Derivedn";
}
};
void say(Base* obj)
{
obj->say();
}
say(static_cast<Base*>(new Derived()));

编辑 2: 确实存在你想要虚拟多态性的情况,因为你可以有下面的情况,它不能以类似的方式工作,因为它打印 Base,而如果你用上面的代码调用它,它会以类似的方式打印派生。我认为这总结了问题的症结。

void say(Base* obj)
{
std::cout << "Basen";
}
void say(Derived* obj)
{
std::cout << "Derivedn";
}
say(static_cast<Base*>(new Derived()));

非成员函数不需要隐式this指针即可调用它。

但是虚函数需要一个this指针(即对象实例)才能使多态性起作用。

还有一个矛盾:所以不可能有一个多态的非成员函数。

拥有虚拟的非成员函数在技术上很难编译。

虚拟函数通常通过 vtable 实现。具有虚拟成员函数的类存储指向该 vtable 的指针,并且该 vtable 添加了所有必需的函数。调用虚拟函数时,将在 vtable 中查找要调用的确切函数。

考虑一下:我正在用C++编写一个库。为了方便用户并减少编译时间,该库分布为:

  • 库的头文件
  • 提供标头中定义的函数的实现的二进制文件。

那么问题出在哪里呢?

这些二进制文件还将包含头文件中具有虚函数的任何类的 vtables。为了将虚函数添加到基类,编译器必须读取和处理库文件的二进制表示形式,修改 vtables 以添加必要的函数。

这将大大增加链接的复杂性(使编译器部分负责这样做),并且会膨胀可执行文件的大小(任何动态加载的库都必须静态链接,因为编译器可能没有修改其内容的权限)。

是否有技术解决方法?

是的,尽管它要求类的实现存在于头文件中,就像模板一样。或者,新的模块系统可以通过放弃单独的实现文件来提供一种实现此功能的方法。

即便如此,编译器开发人员也需要做大量工作,并且对此功能的需求并不大。此功能提供的主要好处是能够快速轻松地重载特定派生类的函数,这本身被认为是一种代码气味(因为你几乎要破坏封装 - 编写返回指向基类的指针的函数的库编写器可能想要更改它返回的派生类, 例如)。

当你想在自由函数中使用多态性时,你基本上有两个选择。要么重载函数,要么调用虚函数:

#include <iostream>
struct base {
virtual void func() = 0;
};
struct foo : base { void func() { std::cout << "foon"; } };
struct bar : base { void func() { std::cout << "barn"; } };
void f(foo& f) { f.func(); }
void f(bar& f) { f.func(); }
void g(base& b) { b.func(); }
int main() {
foo a;
bar b;
f(a);
f(b);
g(a);
g(b);
}

考虑到与成员函数的主要区别在于隐式this参数,g实际上非常接近我所说的"虚拟自由函数"。但是,除此之外,C++中没有虚拟的非成员功能。