对不起,也许我应该以更好的方式重写我的问题。
我有一个具有函数名的基类名ABC
。
void saysez(ostream &os) const // this is NOT virtual!!
{ os << sez; }
和派生类名DEF
也有函数名
void saysez(ostream &os) const { os << extra << " ";
scary::saysez(os);
因此我们可以从上面的代码中看到两者都有相同的签名。
根据我的理解,如果没有指定virtual
关键字,它应该使用基类函数,但在我的演练实践中输出结果是它使用了派生函数。
所以我想知道为什么使用派生函数而不是base函数?
以下是int main
w.saysez(cout); cout << '\n';
w
是派生类的对象。
以下是带有输出
的剪切代码的链接答案 0 :(得分:2)
我很难理解你的问题。虚拟关键字似乎与此问题无关。你的类女巫有一个成员函数void saysez(ostream&amp;),当你创建这个类的实例并调用w.saysez时,编译器会匹配你在巫术实现中找到的saysez的定义。
虚拟关键字在这里无关紧要。如果不想看到'Double Double'部分,那么就把它变成一个可怕的:
((scary*)&w)->saysez(cout)
你不会看到打印出'Double Double'部分。
请参阅this或关于讨论虚拟关键字的任何其他网站以获取更多信息。
答案 1 :(得分:1)
以下是您可能发布的较小样本,但仍然可以证明您的问题:
#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;
}
输出:
boooo, said the Creep
Boo!, said the Ghost
Toil and Trouble, said the Witch
在witch
的构造函数中,您将sez
数组设置为"Toil and Trouble"
,然后在saysez
中声明的witch
中打印出extra
scary
并调用saysez
的{{1}}函数打印出sez
。发生这种情况是因为override non-virtual member functions可能(但不鼓励)。
答案 2 :(得分:1)
我猜w被声明为巫婆类型的元素,所以当然它会调用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.
答案 3 :(得分:1)
首先,如果我不正确理解你的问题,请纠正我。
我们使用继承有很多原因,其中之一是有效编码。例如,在您的情况下,
您希望继承类scary,这意味着您希望在派生类中具有类scary的功能。您可以复制基类中的所有成员函数,使其具有与基于调用相同的功能,但是如果您决定更改其中一个函数中的某些内容,该怎么办?你必须改变所有这些。所以它是低效的编码方式。
现在在你的课堂上,你问为什么对象w在这一行w.saysez(cout); cout << '\n';
,因为类scary和具有相同成员函数的类,w
应首先在其类中查找匹配,如果找不到它,它将查看基础类。因此,您的对象在其类中调用saysez
成员函数。