3

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

我有一个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

4

4 回答 4

2

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

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

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

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

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

于 2012-04-17T00:22:27.717 回答
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声明。发生这种情况是因为可以(但不鼓励)覆盖非虚拟成员函数witchextrascarysaysezsez

于 2012-04-17T00:11:57.567 回答
1

我猜 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() ,它将检查该对象是否是已覆盖 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.
于 2012-04-17T00:14:45.783 回答
1

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

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

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

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

希望这会有所帮助。

于 2012-04-17T00:25:37.473 回答