是否真的有必要使用虚拟
或覆盖
?
我知道在这个总的话题上有很多问题,例如:
从这些和其他标记为重复项的内容中(许多“重复项”的答案都包含至少对我来说是新的不同信息),我学到了一些东西(我认为,大致为什么它们是真的):没有虚拟的覆盖不会编译。没有覆盖的虚拟将编译,但如果你犯了一个错误并且你的方法签名不正确,编译器不会告诉你。
但如果我把两者都省略了会发生什么?例子:
struct BaseClass {
int a_number() {
return 1;
}
};
struct DerivedClass : BaseClass {
int a_number() {
return 2;
}
};
这将编译,并且无论
我实例化 BaseClass
还是 DerivedClass
,a_number都会返回相应的结果。所以它的行为就像我已经覆盖了该函数一样。上面的代码是错误的有什么原因吗?是向后兼容性问题,还是其他问题?
如果这个问题在这里我错过的相关问题之一中直接回答,我深表歉意,谢谢。
编辑:StackOverflow一直给我指C的“虚拟”关键字,用于派生类中的函数。有必要吗?问题,正如Wyck在下面所做的。我不认为它解决了我的问题,因为我在评论中告诉了他/她原因,因为他们是暂时的,我将在这里重复:“我特别问的是超类方法中的虚,而那个帖子似乎是关于子类中的虚以及它如何传播的。下面公认的答案回答了我的问题,但我不认为你的链接回答了我的问题(它可能回答了对C/C更有经验的人的问题,但我不认为它回答了像我这样来自Python和Java的新手的问题)。”
重点是:我认为这些问题是相关的,但不是相同的。
我已经接受了塞尔比的回答,因为这是第一个完整的回答我的问题的“答案”。Wyck的回答提供了许多有用的、更通用的信息。
静态多态性(可以在编译时确定要调用的方法)不需要virtual
关键字。而动态多态性(其中要调用的方法必须在运行时根据所讨论的对象实例的具体类型确定)确实需要virtual
关键字。
这里有一种方法来判断你是否需要动态多态:猫发出什么声音:喵喵!狗发出什么声音:汪!动物发出什么声音?你不知道,因为“动物”不够具体,不知道它发出什么声音。在这种情况下,你需要动态多态。
没有虚拟方法,你说你知道如何实现有关基类的方法。在你的例子中,你已经提供了一个对任何 BaseClass
都有益的a_number
的非虚拟实现。你说是1
。这意味着“你的a_number是什么”的问题现在可以在你不知道派生类是什么类型的情况下得到回答。(我怀疑你会惊讶地发现情况确实如此。只有当你碰巧知道你正在处理派生类型时,你最终才会调用派生类型的a_number
实现。换句话说,编译器必须有某种方式知道调用DerivedClass::a_number。更自然的事情(可能符合您的期望)是使用虚拟方法,以便使用派生类的实现而不是基类的实现,即使被视为基类对象也是如此。
如何将对象视为基类类型,但在运行时仍然调用派生类的实现,其神奇之处在于对象具有vtable,您可以了解它。
对于动物声音的情况,非虚拟(静态多态)方法将是愚蠢的。当你要求一个动物说话
时,它应该是一个虚函数,因为我们希望派生类提供不同的实现,但我们希望能够要求任何动物发出声音。
但是在某些情况下,静态多态就足够了,不需要虚拟
关键字。但在派生类中行为不会改变的情况总是如此。
正如你声明的那样(没有虚拟方法),如果没有 BaseClass::a_number
被声明为虚拟
,DerivedClass 的实例在强制转换为 BaseClass
时将不会调用 DerivedClass
中的实现
示例:
BaseClass* instance1 = new DerivedClass();
instance1->a_number(); // returns "1", even though the object is really an instance of Derived
如果按如下方式声明了 BaseClass:
struct BaseClass {
virtual int a_number() {
return 1;
}
};
那么下面的代码就像你期望的那样工作
BaseClass* instance2 = new DerivedClass();
instance2->a_number(); // returns "2", virtual method invocation
override
关键字是可选的,但建议在 DerivedClass 中使用:
struct DerivedClass : BaseClass {
int a_number() override {
return 2;
}
};
正如您已经观察到的,覆盖
不会改变程序行为,但是如果a_number
没有在Baseclass
中以相同的方式声明,编译器将发出错误。它对捕捉打字错误很有用。
链接的问题不一样 - 甚至没有提到 编辑:新的副本列表包含一个合法的副本,我在搜索中没有找到。 在问这个问题之前,我不知道是否在派生类成员中使用的选择对某些人来说将是一个有争议的问题。 我刚刚遇到了一些如下所示的源代码: 我不确定如何解释这个,甚至在读完这个之后。 这里的< code>override是指< code>virtual吗?< code>void B::method()未被标记为虚函数
在过去的几周里,有一些关于< code>virtual和< code>override的事情困扰着我的大脑。我了解到,当你用虚函数做继承时,你必须添加< code>virtual来让编译器知道搜索正确的函数。后来我还了解到在c 11中有一个新的关键字- 。现在我有点迷茫;我需要在程序中同时使用虚拟关键字和重写关键字吗,或者最好只使用其中一个? 解释我自己-我的意思的代码示例: 最好的方法是什么?
在C 11之前,当在派生类中重写虚拟函数时,建议将虚拟关键字也添加到派生类函数中,以明确其意图。 如今,这样的函数被标记为“override ”,这包含了必须有一个虚基类函数的概念。因此,我现在宁愿省略虚拟的: 然而,这会导致MSVC 2012中的IntelliSense错误:“覆盖”修饰符需要带有显式“虚拟”关键字的虚函数声明 显然编译器编译了类,但错误让我想到了它。是否仍有正当理由添加虚拟关键
我想知道为什么我们不应该重写非虚拟函数?
我把另一个项目放在我的主项目中,这个项目叫做OAuthSwift,它包含了一个框架。当我试图构建它时,我得到了这个错误: :0:错误:无效的虚拟文件系统覆盖文件“/users/gabrieljones/library/developer/xcode/deriveddata/paul”的_console-bbbxnfmomaaurfeabxmnchoqmwpa/build/intermediates
问题内容: 我应该覆盖信号并使用它来打印消息。它不应该结束程序。 到目前为止,所发生的是,当按下它时,它会打印消息,但会结束程序。 当我问我的教授时,他告诉我要这样做:您需要使信号处理程序不要继续处理信号。现在,信号正在由您的代码处理,然后转到父处理程序。 我应该添加一种方法还是需要将信号安装程序移动到某个地方? 到目前为止,这是我的代码: 问题答案: 哇, 方式 太多的代码去筛选。但是,如果使用