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)