【问题标题】:C++ Inheritance and virtual keyword on base_class with derived class带有派生类的 base_class 上的 C++ 继承和虚拟关键字
【发布时间】:2023-03-12 00:59:02
【问题描述】:

抱歉,也许我应该以更好的方式重写我的问题。

我有一个基类名称ABC,它具有函数名称。

void saysez(ostream &os) const // this is NOT virtual!!
            { os << sez; }

还有派生类名DEF也有函数名

void saysez(ostream &os) const { os << extra << " ";
                         scary::saysez(os);

所以我们可以从上面的代码中看到两者具有相同的签名。

据我了解,如果没有指定 virtual 关键字,它应该使用基类函数,但在我的演练实践中,输出结果证明它使用的是派生函数。

所以我想知道为什么它使用派生函数而不是基函数?

下面是来自int main的电话

     w.saysez(cout);  cout << '\n';

w 是派生类的对象。

下面是带有输出的截断代码的链接

http://codepad.org/Pz5jwMVP

【问题讨论】:

  • 请正确格式化您的代码。目前情况很糟糕。
  • 抱歉,我现在要重新格式化它们。
  • I have a question on the last line of the output - 那么你应该单独提供相关代码,因为剩下的只是噪音。
  • @amit 如果这些都没有帮助,我会删除?
  • @Ali:您应该提供描述手头问题的最少可编译代码 - 这可能会为您提供更准确和更好的答案。

标签: c++ inheritance virtual


【解决方案1】:

我很难理解你的问题。 virtual 关键字似乎与这个问题无关。你的女巫类有一个成员函数 void sayez(ostream&),当你创建这个类的一个实例并调用 w.saysez 时,编译器会匹配它在你的女巫实现中找到的 sayez 的定义。

virtual 关键字在这里并不重要。如果不想看到“Double Double”部分,则将 w 转换为可怕的:

((scary*)&w)->saysez(cout)

而且您不会看到打印出来的“双倍”部分。

请参阅this 或任何其他讨论虚拟关键字的网站以获取更多信息。

【讨论】:

  • 我已经改变了我的问题,希望这会更有意义。
【解决方案2】:

首先,如果我没有正确理解您的问题,请纠正我。

我们使用继承的原因有很多,其中之一就是高效编码。例如,在您的情况下,

你想继承可怕的类,这意味着你想在派生类中拥有可怕的类的功能。您可以复制基类中的所有成员函数,使其具有与基类调用相同的功能,但是如果您决定更改其中一个函数中的某些内容怎么办?你必须改变所有这些。所以这是一种低效的编码方式。

现在在你的类中,你问为什么对象 w 在这一行 w.saysez(cout); cout &lt;&lt; '\n'; 因为类可怕和具有相同成员函数的类,w 应该首先在其类中查找匹配,如果它没有'找不到它,然后它将在基础类中查找。因此,您的对象正在其类中调用saysez 成员函数。

希望这会有所帮助。

【讨论】:

  • 非常感谢,现在我明白为什么它使用了来自女巫的sayez。那么如果 base 中有一个 virtual 关键字,它还会使用派生的那个吗?
  • 同样,这并不能解释为什么 witch 对象在未声明为 virtual 时选择被覆盖的成员函数。请参阅 ds1848 的 linkmine 以正确了解正在发生的事情。
【解决方案3】:

这里有一个较小的示例,您可以发布它仍然可以证明您的问题:

#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",然后在witch 中声明的saysez 中打印出extra 并调用scarysaysez 函数打印出sez。发生这种情况是因为可以(但不鼓励)override non-virtual member functions

【讨论】:

  • 好吧,我的问题是,如果该函数上没有 virtual 关键字,那么为什么它使用派生类而不是基类?我虽然规则是,如果没有指定虚拟关键字,它应该使用来自基类而不是派生类的关键字?
  • 抱歉,我确实更新了几次答案,您可能没有看到this link,它解释了覆盖非虚拟成员函数是合法的,但应该避免。将在对象的静态类型上调用被覆盖的成员函数。
  • 我已经改变了我的问题,如果您可以再看一遍,看看它是否与您最初的想法相同。再次抱歉!
【解决方案4】:

我猜测 w 被声明为 witch 类型的元素,所以它当然会调用 witch::saysez()。但是,如果您这样做了:

scary* w2 = new witch();
w2->saysez(cout);

...那么您应该会看到可怕的::saysez() 行为。

您似乎误解了非虚函数如何与继承的类一起工作。虚拟和非虚拟功能都可以被覆盖。在 Foo 类的对象上,如果调用 Foo::MyNonVirtualFn(),它只会执行 Foo::MyNonVirtualFn() 的主体。如果 Foo 有一个实现 MyNonVirtualFn() 的超类,或者如果它被调用的对象实际上是 Foo 的子类的一个实例,它覆盖了 MyNonVirtualFn() —— 这些都不重要。如果函数调用是 Foo::MyNonVirtualFn(),那么这就是运行的函数的版本。 (如果您没有明确说明您调用的 MyNonVirtualFn() 版本是从您调用它的类型推断出来的——对于指针或引用类型,这可能与对象。)

虚拟函数的不同之处在于,如果您在一个对象上调用 Foo::MyVirtualFn(),它将检查该对象是否是 Foo 的子类的一个实例,该子类已覆盖 MyVirtualFn() .所以,例如:

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.

【讨论】:

  • 好吧,我的问题是,如果该函数上没有 virtual 关键字,那么为什么它使用派生类而不是基类?我虽然规则是,如果没有指定虚拟关键字,它应该使用来自基类而不是派生类的关键字?
  • 这并不能真正回答问题 - 如果我是初学者,我会在阅读后更加困惑,特别是因为您发布的代码似乎表现出多态行为,而是表现出早期绑定.请至少说明为什么会发生这种情况。
  • 是的,抱歉——一开始并没有添加太多细节,因为我不能 100% 从他的问题中确定如果我完全理解他的误解是什么,现在我知道了已经做出了更详细的回应。
猜你喜欢
  • 2016-03-04
  • 2015-07-13
  • 2020-10-26
  • 2013-02-26
  • 2012-12-19
  • 1970-01-01
  • 2011-06-21
相关资源
最近更新 更多