我正在学习C++,我刚刚开始学习虚拟函数。
从我(在书中和网上)读到的内容来看,虚拟函数是基类中的函数,可以在派生类中重写。
但在本书的早些时候,当我学习基本继承时,我能够在派生类中重写基函数,而不使用虚函数。
那么我在这里错过了什么?我知道虚拟函数还有很多,它似乎很重要,所以我想清楚它到底是什么。我只是在网上找不到一个直截了当的答案。
我正在学习C++,我刚刚开始学习虚拟函数。
从我(在书中和网上)读到的内容来看,虚拟函数是基类中的函数,可以在派生类中重写。
但在本书的早些时候,当我学习基本继承时,我能够在派生类中重写基函数,而不使用虚函数。
那么我在这里错过了什么?我知道虚拟函数还有很多,它似乎很重要,所以我想清楚它到底是什么。我只是在网上找不到一个直截了当的答案。
当前回答
我认为您所指的是这样一个事实:一旦方法被声明为virtual,您就不需要在重写中使用“virtual”关键字。
class Base { virtual void foo(); };
class Derived : Base
{
void foo(); // this is overriding Base::foo
};
如果在Base的foo声明中不使用“virtual”,那么Derived的foo将只是隐藏它。
其他回答
解释了虚拟功能的需求[易于理解]
#include<iostream>
using namespace std;
class A{
public:
void show(){
cout << " Hello from Class A";
}
};
class B :public A{
public:
void show(){
cout << " Hello from Class B";
}
};
int main(){
A *a1 = new B; // Create a base class pointer and assign address of derived object.
a1->show();
}
输出将为:
Hello from Class A.
但具有虚拟功能:
#include<iostream>
using namespace std;
class A{
public:
virtual void show(){
cout << " Hello from Class A";
}
};
class B :public A{
public:
virtual void show(){
cout << " Hello from Class B";
}
};
int main(){
A *a1 = new B;
a1->show();
}
输出将为:
Hello from Class B.
因此,使用虚拟函数可以实现运行时多态性。
为什么我们需要虚拟功能?
虚拟函数避免了不必要的类型转换问题,我们中的一些人会争论,当我们可以使用派生类指针来调用派生类中特定的函数时,为什么需要虚拟函数!答案是,它否定了在大型系统开发中继承的全部思想,因为在大型系统中,非常需要使用单指针基类对象。
让我们比较以下两个简单的程序,以了解虚拟函数的重要性:
无虚拟功能的程序:
#include <iostream>
using namespace std;
class father
{
public: void get_age() {cout << "Fathers age is 50 years" << endl;}
};
class son: public father
{
public : void get_age() { cout << "son`s age is 26 years" << endl;}
};
int main(){
father *p_father = new father;
son *p_son = new son;
p_father->get_age();
p_father = p_son;
p_father->get_age();
p_son->get_age();
return 0;
}
输出:
Fathers age is 50 years
Fathers age is 50 years
son`s age is 26 years
具有虚拟功能的程序:
#include <iostream>
using namespace std;
class father
{
public:
virtual void get_age() {cout << "Fathers age is 50 years" << endl;}
};
class son: public father
{
public : void get_age() { cout << "son`s age is 26 years" << endl;}
};
int main(){
father *p_father = new father;
son *p_son = new son;
p_father->get_age();
p_father = p_son;
p_father->get_age();
p_son->get_age();
return 0;
}
输出:
Fathers age is 50 years
son`s age is 26 years
son`s age is 26 years
通过仔细分析这两个输出,可以理解虚拟函数的重要性。
当基类中有函数时,可以在派生类中重新定义或重写它。
重新定义方法:派生类中给出了基类方法的新实现。不便于动态绑定。
重写方法:在派生类中重新定义基类的虚拟方法。虚拟方法有助于动态绑定。
所以当你说:
但在书的早些时候,当我了解基本遗传时能够重写派生类中的基方法,而不使用“虚拟”。
因为基类中的方法不是虚拟的,所以您不是在重写它,而是在重新定义它
虚拟函数用于支持运行时多态性。
也就是说,virtual关键字告诉编译器不要在编译时做出(函数绑定的)决定,而是推迟到运行时”。
您可以通过在函数的基类声明中的关键字virtual之前使其成为虚拟函数。例如类基础{虚虚函数();}当基类具有虚拟成员函数时,从基类继承的任何类都可以使用完全相同的原型重新定义该函数,即只能重新定义功能,而不能重新定义函数的接口。类派生:公共基{void函数();}基类指针可用于指向基类对象和派生类对象。当使用基类指针调用虚拟函数时,编译器在运行时决定要调用函数的哪个版本(即基类版本或重写的派生类版本)。这被称为运行时多态性。
OOP答案:亚型多态性
在C++中,需要虚拟方法来实现多态性,如果应用维基百科中的定义,则更准确地说是子类型或子类型多态性。
维基百科,分类,2019-01-09:在编程语言理论中,子类型化(也称为子类型多态性或包含多态性)是类型多态性的一种形式,其中子类型是通过某种可替代性概念与另一个数据类型(父类型)相关的数据类型,这意味着程序元素(通常是子例程或函数),编写为对父类型的元素进行操作也可以对子类型的元素执行操作。
注意:子类型表示基类,子类型表示继承类。
关于亚型多态性的进一步阅读
https://en.wikipedia.org/wiki/Subtypinghttps://en.wikipedia.org/wiki/Polymorphism_(computer_science)#子类型
技术答案:动态调度
如果您有一个指向基类的指针,那么方法的调用(声明为虚拟)将被分派到所创建对象的实际类的方法。这就是亚型多态性是如何在C++中实现的。
进一步阅读C++中的多态性与动态调度
http://www.cplusplus.com/doc/tutorial/polymorphism/https://en.cppreference.com/w/cpp/language/virtual
实现答案:创建vtable条目
对于方法上的每个修饰符“virtual”,C++编译器通常会在声明方法的类的vtable中创建一个条目。这就是常见的C++编译器实现动态调度的方式。
进一步阅读vtables
https://en.wikipedia.org/wiki/Virtual_method_table
示例代码
#include <iostream>
using namespace std;
class Animal {
public:
virtual void MakeTypicalNoise() = 0; // no implementation needed, for abstract classes
virtual ~Animal(){};
};
class Cat : public Animal {
public:
virtual void MakeTypicalNoise()
{
cout << "Meow!" << endl;
}
};
class Dog : public Animal {
public:
virtual void MakeTypicalNoise() { // needs to be virtual, if subtype polymorphism is also needed for Dogs
cout << "Woof!" << endl;
}
};
class Doberman : public Dog {
public:
virtual void MakeTypicalNoise() {
cout << "Woo, woo, woow!";
cout << " ... ";
Dog::MakeTypicalNoise();
}
};
int main() {
Animal* apObject[] = { new Cat(), new Dog(), new Doberman() };
const int cnAnimals = sizeof(apObject)/sizeof(Animal*);
for ( int i = 0; i < cnAnimals; i++ ) {
apObject[i]->MakeTypicalNoise();
}
for ( int i = 0; i < cnAnimals; i++ ) {
delete apObject[i];
}
return 0;
}
示例代码输出
Meow!
Woof!
Woo, woo, woow! ... Woof!
代码示例的UML类图