虚函数,它是基类中的const而不是派生中的const

时间:2011-09-21 17:50:44

标签: c++ inheritance virtual const

任何人都可以解释以下代码的输出吗?

#include <iostream>
#include <string>
class Animal
{
public:
    Animal(const std::string & name) : _name(name) { }
    ~Animal() { }
    virtual void printMessage() const
    {
        std::cout << "Hello, I'm " << _name << std::endl;
    }

private:
    std::string _name;
    // other operators and stuff
};

class Cow : public Animal
{
public:
    Cow(const std::string & name) : Animal(name) { }
    ~Cow() { }
    virtual void printMessage()
    {
        Animal::printMessage();
        std::cout << "and moo " << std::endl;
    }
};

int main() {
    Cow cow("bill");
    Animal * animal = &cow;
    cow.printMessage();
    animal->printMessage();
}

输出

  

你好,我是票据   和moo   你好,我是账单

我不明白为什么。指针动物指向Cow类型的对象。 printMessage是一个虚函数。为什么Cow类的实现不是被调用的?

6 个答案:

答案 0 :(得分:13)

Cow未覆盖Animal的虚函数,因为它具有不同的签名。实际发生的是Cow 隐藏 Animal中的函数。

这样做的结果是printMessage上的Animal只会使用Animal中的版本,而不管Cow中的版本(它不会覆盖它),但是从Cow调用它将使用Cow中的那个(因为它隐藏了Animal中的那个)。

要解决此问题,请移除const中的Animal,或在const中添加Cow

在C ++ 2011中,您可以使用override关键字来避免像这样的微妙陷阱:

class Cow : public Animal 
{ 
public: 
    Cow(const std::string & name) : Animal(name) { } 
    ~Cow() { } 
    virtual void printMessage() override
    { 
        Animal::printMessage(); 
        std::cout << "and moo " << std::endl; 
    } 
};

请注意override之后添加的printMessage()。如果printMessage实际上没有覆盖基类版本,这将导致编译器发出错误。在这种情况下,您将收到错误。

答案 1 :(得分:6)

您有两个不同版本的printMessage,一个是const,另一个不是Cow。这两者是无关的,即使它们具有相同的名称。 Animal中的新函数隐藏了Cow中的新函数,因此当您直接调用它时,编译器只会考虑{{1}}版本。

答案 2 :(得分:1)

我花了一段时间才了解彼得亚历山大的 隐藏 答案,但理解它的另一种方法如下:

假设您在Cow类中拼错了方法名称,但在Animal类中拼写正确:

Animal::printMessage()
Cow::mispelledPrintMessage()

然后当你有

Animal *animal;

你只能打电话

animal->printMessage();

但你不能打电话

animal->mispelledPrintMessage();

因为在Animal类中不存在mispelledPrintMessage()。它是Cow类中的 全新的 方法,因此无法通过基本指针进行多态调用。

因此让Animal方法在签名中使用const,但在Cow方法中没有类似于派生类中稍微错误的方法名称。

PS:另一个解决方案(1使两个方法都成为const,2使两个方法成为非const,或者使用新的2011覆盖关键字),是使用强制转换,强制将Animal指针转换为Cow指针:

((Cow*)animal)->printMessage();

但这是一个非常难看的HACK,我不推荐它。

PS:由于这个原因,我总是尝试在Base类的签名中使用const编写我的toString()方法,并且 所有派生类 。另外,在toString()签名中使用const允许您使用const 非const对象调用toString()。如果您省略了const,并尝试使用const对象调用toString(),GCC编译器会抱怨 discards qualifiers 错误消息:

const Bad' as `this' argument of `std::string Bad::toString()' discards qualifiers

代码:

#include <iostream>
#include <string>

class Bad
{
public:
        std::string toString()
        {
                return "";
        }
};

int main()
{
        const Bad       bad;
        std::cout << bad.toString() << "\n";
        return 0;
}

因此,总而言之,由于Cow不会更改任何数据成员,因此最佳解决方案可能是在派生的Cow类中将const添加到printMessage(),以便BASE Animal和DERIVED Cow类在其签名中都具有const。

-dennis bednar -ahd 310

答案 3 :(得分:1)

丹尼斯的帖子更正。

请注意,您可以将动物(动物*)投射到牛*上作为解决方案。但是,一旦Cow *你的Animal类的printMessage将无法使用。 因此((Cow *)动物) - &gt; printMessage()需要((Cow *)动物) - &gt; mispelledPrintMessage()

答案 4 :(得分:0)

试过了。将const添加到Cow类,它将起作用。

即。两个班级virtual void printMessage () const

答案 5 :(得分:0)

虚拟子类 - &gt;不需要。您只需将const添加到子类中的函数以使其成为相同的签名