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函数覆盖。