C++ C++;具有派生类的基类上的继承和虚关键字
对不起,也许我应该用更好的方式重写我的问题 我有一个具有函数名的基类名C++ C++;具有派生类的基类上的继承和虚关键字,c++,inheritance,virtual,C++,Inheritance,Virtual,对不起,也许我应该用更好的方式重写我的问题 我有一个具有函数名的基类名ABC void saysez(ostream &os) const // this is NOT virtual!! { os << sez; } 从上面的代码可以看出,这两个代码都有相同的签名 根据我的理解,如果没有指定virtual关键字,它应该使用基类函数,但在我的演练实践中,输出结果表明它使用的是派生的函数 所以我想知道为什么在基函数上使用派生函数 下面是来自int ma
ABC
void saysez(ostream &os) const // this is NOT virtual!!
{ os << sez; }
从上面的代码可以看出,这两个代码都有相同的签名
根据我的理解,如果没有指定virtual
关键字,它应该使用基类函数,但在我的演练实践中,输出结果表明它使用的是派生的函数
所以我想知道为什么在基函数上使用派生函数
下面是来自int main
w.saysez(cout); cout << '\n';
w.saysez(cout);cout这里有一个你可以发布的小样本,它仍然可以证明你的问题:
#include <iostream>
using namespace std;
#include <cstring>
class scary {
char is[31];
char sez[31];
public:
scary() {
strcpy(is, "Creep");
strcpy(sez, "boooo");
}
scary(const char i[], const char s[])
{
strcpy(is, i); strcpy(sez, s);
}
virtual void sayis(ostream &os) const { os << is; }
void saysez(ostream &os) const // this is NOT virtual!!
{ os << sez; }
};
ostream &operator<<(ostream &os, const scary &x) {
x.saysez(os);
os << ", said the ";
x.sayis(os);
return os;
}
class ghost: public scary {
public:
ghost():scary("Ghost", "Boo!")
{
}
};
class witch: public scary {
char extra[31];
public:
witch(): scary("Witch", "Toil and Trouble") {
strcpy(extra, "Double, Double");
}
void saysez(ostream &os) const {
os << extra << " ";
scary::saysez(os);
}
};
int main() {
scary s;
ghost g;
witch w;
cout << s << '\n' << g << '\n' << w << '\n';
return 0;
}
在witch
的构造函数中,您将sez
数组设置为“辛苦和麻烦”
,然后在saysez
中声明的witch
中打印出额外的,并调用可怕的saysez
函数来打印出sez
。之所以会发生这种情况,是因为有可能(但不鼓励)这样做。我猜w被声明为witch类型的元素,所以它当然要调用witch::saysez()。但是,如果你做到了:
scary* w2 = new witch();
w2->saysez(cout);
…那么你应该看到可怕的::saysez()行为
看起来您误解了非虚拟函数如何与继承类一起工作。可以重写虚拟函数和非虚拟函数。在类Foo的对象上,如果调用Foo::MyNonVirtualFn(),它将只执行Foo::MyNonVirtualFn()的主体。如果Foo有一个实现MyNonVirtualFn()的超类,或者如果调用它的对象实际上是覆盖MyNonVirtualFn()的Foo子类的实例——这些都不重要。如果函数调用是Foo::MyNonVirtualFn(),那么这就是运行的函数的版本。(如果您没有明确说明调用MyNonVirtualFn()的版本,则会从调用它的类型中推断出来——对于指针或引用类型,这可能与对象的类型不完全相同。)
虚拟函数的不同之处在于,如果在对象上调用Foo::MyVirtualFn(),它将检查该对象是否是覆盖了MyVirtualFn()的Foo子类的实例。例如:
Base* base = new Base();
Derived* derived = new Derived();
Base* derived_as_base = derived;
base->MyNonVirtualFn(); // Calls Base::MyNonVirtualFn().
derived->MyNonVirtualFn(); // Calls Derived::MyNonVirtualFn().
derived_as_base->MyNonVirtualFn(); // Calls Base::MyNonVirtualFn() because it's called on a Base*.
base->MyVirtualFn(); // Calls Base::MyNonVirtualFn().
derived->MyVirtualFn(); // Calls Derived::MyNonVirtualFn().
derived_as_base->MyVirtualFn(); // Calls Derived::MyNonVirtualFn() because it uses a lookup table.
我很难理解你的问题。虚拟关键字似乎与此问题无关。您的类witch有一个成员函数void saysez(ostream&),当您创建该类的实例并调用w.saysez时,编译器将匹配它在witch实现中找到的saysez定义
虚拟关键词在这里并不重要。如果不想看到“双双”部分,请将w投射到一个可怕的角色:
((scary*)&w)->saysez(cout)
而且你不会看到打印出来的“双-双”部分
有关更多信息,请访问或访问任何其他讨论虚拟关键字的网站。首先,如果我没有正确理解您的问题,请纠正我
我们使用继承有很多原因,其中之一就是高效地编码。比如你的情况,
您希望继承类thrary,这意味着您希望在派生类中具有类thrary的功能。您可以复制基类中的所有成员函数,使其具有与基调用相同的功能,但是如果您决定更改其中一个函数中的某些内容,该怎么办?你必须全部改变。因此,这是一种低效的编码方式
现在在你的课堂上,你问为什么对象w在这行w.saysez(cout);请正确格式化你的代码。当前很糟糕。很抱歉,我现在要重新格式化它们。我在输出的最后一行有一个问题
-那么你应该单独提供相关代码,因为剩下的只是杂音。@amit如果这些都没有真正的帮助,我会删除吗?@Ali:你应该提供描述手头问题的最小可编译代码-这可能会让你得到更准确、更好的答案。我的问题是,如果该函数上没有virtual
关键字,那么为什么要使用它呢一个来自派生类,但不是来自基类?。我认为规则是,如果没有指定虚拟关键字,它应该使用来自基类而不是派生类的虚拟关键字?这并不能真正回答这个问题-如果我是初学者,读过之后会更困惑,特别是因为您发布的代码似乎表现出多态行为,而不是早期绑定。请至少说明发生这种情况的原因。是的,对不起——一开始没有添加太多细节,因为从他的问题中我无法100%确定我是否完全理解他的误解,现在我知道了,我已经回答了更详细的问题。我的问题是,如果该函数上没有virtual
关键字,那么为什么使用派生类而不是基类?。我认为规则是,如果没有指定虚拟关键字,它应该使用来自基类而不是派生类的关键字?对不起,我确实更新了我的答案几次,您可能没有看到这一点,这说明重写非虚拟成员函数是合法的,但应该避免。重写的成员函数将在对象的静态类型上调用。我已更改了我的问题,您是否可以再次查看它,看看它是否与您最初的想法相同。再次抱歉!非常感谢你,现在我明白为什么它用巫婆的saysez来代替了。那么,如果base中有一个virtual关键字,它还会使用派生的关键字吗?同样,这并不能解释为什么witch
对象在未声明为virtual
时选择重写的成员函数。请参阅ds1848和,以正确理解正在发生的事情。我已经改变了我的问题,希望这会更有意义。
Base* base = new Base();
Derived* derived = new Derived();
Base* derived_as_base = derived;
base->MyNonVirtualFn(); // Calls Base::MyNonVirtualFn().
derived->MyNonVirtualFn(); // Calls Derived::MyNonVirtualFn().
derived_as_base->MyNonVirtualFn(); // Calls Base::MyNonVirtualFn() because it's called on a Base*.
base->MyVirtualFn(); // Calls Base::MyNonVirtualFn().
derived->MyVirtualFn(); // Calls Derived::MyNonVirtualFn().
derived_as_base->MyVirtualFn(); // Calls Derived::MyNonVirtualFn() because it uses a lookup table.
((scary*)&w)->saysez(cout)