任何人都可以解释以下代码的输出吗?
#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类的实现不是被调用的?
答案 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添加到子类中的函数以使其成为相同的签名