C++ 在基类中为常量而在派生类中不为常量的虚拟函数

C++ 在基类中为常量而在派生类中不为常量的虚拟函数,c++,inheritance,virtual,constants,C++,Inheritance,Virtual,Constants,有人能解释一下下面代码的输出吗 #include <iostream> #include <string> class Animal { public: Animal(const std::string & name) : _name(name) { } ~Animal() { } virtual void printMessage() const { std::cout << "Hello, I'm "

有人能解释一下下面代码的输出吗

#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();
}
#包括
#包括
类动物
{
公众:
动物(const std::string&name):\u name(name){}
~Animal(){}
虚空printMessage()常量
{

std::cout您有两个不同版本的
printMessage
,一个是
const
,另一个不是。这两个版本是不相关的,即使它们的名称相同。
Cow
中的新函数隐藏了
Animal
中的函数,因此当您直接调用它时,编译器只考虑
Cow
版本。
Cow
没有覆盖
Animal
的虚拟函数,因为它有不同的签名。实际发生的是
Cow
正在
Animal
中隐藏函数

这样做的结果是,在
动物
上调用
打印消息
将只使用
动物
中的版本,而不管
奶牛
中的版本(它不会覆盖它),但从
奶牛
调用它将使用
奶牛
中的版本(因为它会对
动物
隐藏该版本)

要解决此问题,请删除
动物中的
常量
,或在
奶牛中添加
常量

<>在C++ 2011中,你将能够使用<代码>覆盖> <代码>关键字来避免这样的微妙陷阱:

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; 
    } 
};
class奶牛:公共动物
{ 
公众:
奶牛(const std::string&name):动物(name){}
~Cow(){}
虚拟void printMessage()重写
{ 
动物::printMessage();

std::cout刚刚尝试过。将
常量添加到Cow类中,它就会工作


i、 e.
virtual void printMessage()const

我花了一些时间才理解Peter Alexander的隐藏答案,但另一种理解方法如下:

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

Animal::printMessage()
Cow::mispelledPrintMessage()
那么当你有一个

Animal *animal;
你只能打电话

animal->printMessage();
但是你不能打电话

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

因此,让Animal方法在签名中包含const,而不是Cow方法中包含const,这有点类似于派生类中的方法名称有点错误

PS:另一个第四种解决方案(1使两种方法都为常量,2使两种方法都为非常量,或3使用new 2011 override关键字)是使用强制转换,将动物指针强制转换为奶牛指针:

((Cow*)animal)->printMessage();
但这是一个非常丑陋的黑客,我不推荐它

PS:我总是尝试在基类和所有派生类的签名中使用const编写我的toString()方法。正是出于这个原因,在toString()签名中使用const可以使用const或non-const对象调用toString()对于const对象,GCC编译器将发出丢弃限定符错误消息:

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;
}
#包括
#包括
坏的
{
公众:
std::string toString()
{
返回“”;
}
};
int main()
{
常数坏坏坏;

std::cout更正丹尼斯的帖子

注意:您声明可以将动物(动物*)投射到Cow*,作为解决方案。但是,一旦Cow*,您的动物类打印消息将不可用。
因此((Cow*)animal)->printMessage()需要是((Cow*)animal)->mispelledPrintMessage()

子类中的虚函数-->无需。您只需向子类中的函数添加常量,使其具有相同的签名即可

+1以获得一个完整、最小的工作示例您是否尝试将Cow类中的函数更改为
虚空printMessage()const
。如果基类函数必须是const,而派生类函数必须是non-const,则可以使用非虚拟接口习惯用法,在该习惯用法中,基类函数不是虚拟的,而是调用非const的私有虚拟函数,因此可以被派生类中的non-const函数覆盖。