C++虚拟关键字vs覆盖函数
我正在学习C++并且正在学习有关virtual关键字的知识。我搜索了互联网试图了解它无济于事。我进入了我的编辑器,做了下面的实验,期待它打印两次基本消息(因为我的印象是需要virtual关键字来覆盖函数)。但是,它打印出两个不同的信息。有人可以向我解释为什么我们需要虚拟关键字,如果我们可以简单地覆盖功能,并仍然看起来像多态行为?也许有人可以帮助我和其他人在未来理解虚拟与压倒一切。 (我得到的输出是“我是基础”,后面是“我是派生的”)。C++虚拟关键字vs覆盖函数
#include <iostream>
using namespace std;
class Base{
public:
void printMe(){
cout << "I am the base" << endl;
}
};
class Derived: public Base{
public:
void printMe(){
cout << "I am the derived" << endl;
}
};
int main() {
Base a;
Derived b;
a.printMe();
b.printMe();
return 0;
}
请考虑以下示例。说明需要virtual
和override
的重要线是c->printMe();
。请注意,c
的类型是Base*
,但由于多态性,它能够正确地从派生类中调用重写的方法。
#include <iostream>
class Base{
public:
virtual void printMe(){
std::cout << "I am the base" << std::endl;
}
};
class Derived: public Base{
public:
void printMe() override {
std::cout << "I am the derived" << std::endl;
}
};
int main() {
Base a;
Derived b;
a.printMe();
b.printMe();
Base* c = &b;
c->printMe();
return 0;
}
输出是
I am the base
I am the derived
I am the derived
虽然,你还没有解释'override'关键字的用途是什么。 –
我做过(只有我),但人们倾向于首先回答他们看到的投票。 –
你不在这里看到的行为,因为你已经宣布b
是Derived
所以编译器知道哪些功能使用的类型。为了揭露为什么virtual
是必要的,你需要的东西混合起来:
int main() {
Base a;
Base *b = new Derived();
a.printMe();
b->printMe();
delete b;
return 0;
}
现在b
是Base*
型这意味着它要在虚函数表上使用Base
加任何的功能的。这打破了你的实现。你可以通过正确地声明virtual
来修复它。
你能解释你对第3行新关键字的使用吗?我对这方面的新作品有点生疏。我知道你正在创建一个基类型的指针;那么你是否给它分配足够的内存来保存派生类?第3行如何工作? –
这只是一个使用'new'的直接C++分配。这很像C的'malloc',但内置更多的智能,并且自动调用初始化器。一本好的C++参考书应该涵盖'new'和'delete'的基础知识。如果你没有,那么[C++的作者](http://www.stroustrup.com/4th.html)是一个很好的开始。 – tadman
@tadman:当我评论其他答案时,我认为我们最好不要在示例代码中为新手使用裸'new'和'delete',因为它们在现代C++中通常被认为是不好的练习。参考文献(或'b =&a')也可以说明这一点。 –
随着你的代码,如果你这样做
Derived derived;
Base* base_ptr = &derived;
base_ptr->printMe();
你觉得会发生什么事?它不会打印出I am the derived
,因为该方法不是虚拟的,并且调度是通过调用对象的静态类型完成的(即Base
)。如果将其更改为虚拟方法,则调用的方法将取决于对象的动态类型而不是静态类型。
override
是加入C++ 11一个新的关键字。
你应该使用它,因为:
编译器会检查是否有基类包含一个匹配
virtual
方法。这很重要,因为方法名称或参数列表中的某些拼写错误(允许使用重载)可能会导致出现这样的情况,即当它没有被重写时会被覆盖。如果对一种方法使用
override
,如果在不使用关键字override
的情况下重写另一种方法,则编译器将报告错误。这有助于在符号冲突发生时检测到不需要的覆盖。virtual
并不意味着“覆盖”。在类doent使用“覆盖”关键字比重写一个方法,你可以简单地写这个方法省略“虚拟”关键字,重写将隐式发生。开发人员在C++ 11之前编写了virtual
,以表明他们的覆盖意图。简单地说就是virtual
的意思是:这个方法可以在一个子类中重写。
“虚拟并不意味着”覆盖“,如果你忽略它,覆盖仍然有效。”?我认为你的意思是,如果你在派生类中忽略它,而不是在基数中省略 – ROX
是的,这是我的意思。我改进了文字,使其更加清晰。 –
注意:'使用命名空间标准;'是一个不好的习惯,如果你现在可以停下来,你可能会避免将来的很多麻烦。 'std ::'前缀是有原因的:它避免了与你自己的类,结构和变量的冲突。 – tadman
试试'Base * p = new Derived; p-> printMe();'有和没有'虚拟'。 – HolyBlackCat
澄清 - 当通过指针或对其基类的引用访问对象时,实现多态行为。 – DeiDei