我的一位朋友问我“如何在 C++ 中实现运行时多态性?” 我回答“通过继承”他说“不,只能使用虚函数来实现”。
首先,术语多态性是模棱两可的:在一般计算科学意义上,它指的是隐式调用特定类型代码的能力,无论是在编译时还是运行时。在 C++ 标准中,它的定义非常狭窄,是虚拟调度(这是标准的特权)。显然,你朋友的问题是有意义的,因为他在问它是如何在 C++ 中实现的,他的观点必须来自 C++ 之外——在计算科学术语的更大背景下。
当然,虚拟功能/调度是一个答案,但它们是唯一的答案......?
为了尝试回答这个问题,对什么行为符合运行时多态性有一个清晰的概念是有帮助的。考虑:
void f(X& x)
{
// the only situation where C++ doesn't know the run-time type of a variable is
// where it's an instance of a class/struct accepted by reference or pointer
...some operation involving "x"...
}
任何可能导致涉及“x”的操作被调用的不同机器代码的任何机制,其中原因与“x”的运行时类型特别相关,都非常接近运行时多态性,但还有一个最后一个问题:分支是由语言隐式决定的,还是由程序员明确安排的?
在虚拟分派的情况下,编译器隐含地知道创建虚拟分派表和查找分支到适合类型的代码。
但是,假设我们有一个函数指针,该指针以前设置为寻址适合类型的代码,或者用于控制 aswitch
到特定类型的特定类型数字或枚举case
。这些在功能上实现了与运行时虚拟分派相同的行为,但设置必须由开发人员显式完成,并且没有编译器强制执行来确保纯粹在运行时类型上完成确定。他们是否有资格是有争议的。因为 C++ 在虚拟分派中具有完全隐式的机制,并且因为在 C++ 标准中多态性对虚拟分派的定义很窄,所以我猜大多数 C++ 程序员会说“不”。
但是在 C 的世界中,将 sayqsort
或bsearch
(两个标准 libC 函数,通过函数指针参数使用运行时分派处理任意类型)描述为运行时多态可能有助于快速理解......更正常的说法是它们是通用实现虽然。
尽管如此,毫无疑问,有数百本计算科学教科书包含运行时多态性的函数定义,我敢打赌,使用函数指针或其他程序员初始化的元数据进行调度可以满足其中很大一部分。因此,过于坚持只有一个明确的答案是没有意义的。
我的问题是:-
1)运行时多态性是否只能通过虚函数实现?
如上所述,在 C++ 的上下文中,我倾向于“是”,但它(无休止地)有争议。
2) 上面的例子是运行时多态还是编译时?
也没有......甚至没有两个函数可以根据类型选择 - 你总是运行相同的机器代码func()
:编译器选择的机器代码假设类型是A
.
3)如果我有以下代码: -
void func2(A& myA)
{
cout << myA.i << endl;
// dynamic/static cast myA to myB
cout<<myB.j << endl;
}
它是一种什么样的多态性?或者它甚至是多态性?
根本不是多态的,因为您没有基于类型的分支。动态转换可以在 myA 的运行时类型中查询编译器填充的类型元数据,如果您使用它来仅有条件地调用访问myB.j
- 这将是未定义的行为,除非myA
是 a B
- 那么你回到手动、明确地开发人员协调特定类型的行为,以及上面讨论了这是否符合您的“多态性”。