Why use virtual functions?
Possible Duplicate:
Can someone explain C++ Virtual Methods?
我有一个关于C++虚拟函数的问题。
为什么以及何时使用虚拟函数?有人能给我一个虚拟功能的实时实现或使用吗?
如果要重写派生类的某个行为(读取方法),而不是为基类实现的行为(读取方法),并且要在运行时通过指向基类的指针来执行,则可以使用虚拟函数。
经典的例子是当您有一个名为
类层次结构如下:
下面的代码片段显示了示例的用法;它创建了一个
1 2 3 4 | Shape *basep[] = { &line_obj, &tri_obj, &rect_obj, &cir_obj}; for (i = 0; i < NO_PICTURES; i++) basep[i] -> Draw (); |
上面的程序只是使用指向基类的指针来存储派生类对象的地址。这提供了一个松耦合,因为如果随时随地添加一个新的具体派生类
以上是著名固体设计原理的开闭原理的一个很好的例子。
当需要以相同的方式处理不同的对象时,可以使用虚拟函数。这叫做多态性。让我们假设您有一些基本类-类似于经典形状:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | class Shape { public: virtual void draw() = 0; virtual ~Shape() {} }; class Rectange: public Shape { public: void draw() { // draw rectangle here } }; class Circle: public Shape { public: void draw() { // draw circle here } }; |
现在您可以得到不同形状的向量:
1 2 3 | vector<Shape*> shapes; shapes.push_back(new Rectangle()); shapes.push_back(new Circle()); |
你可以这样画所有的形状:
1 2 3 4 | for(vector<Shape*>::iterator i = shapes.begin(); i != shapes.end(); i++) { (*i)->draw(); } |
通过这种方式,您可以使用一个虚拟方法绘制不同的形状-draw()。根据指针后面对象类型的运行时信息选择正确的方法版本。
通知使用虚函数时,可以将其声明为纯虚函数(如在类形状中,只需将"=0"放在方法proto之后)。在这种情况下,您将无法使用纯虚拟函数创建对象的实例,它将被称为抽象类。
在析构函数之前还要注意"虚"。如果计划通过指向对象基类的指针来处理对象,则应声明析构函数为虚拟的,因此当为基类指针调用"delete"时,将调用所有析构函数链,并且不会出现内存泄漏。
想想动物类,从中衍生出猫、狗和牛。动物类有一个
1 2 3 4 | virtual void SaySomething() { cout <<"Something"; } |
功能。
1 2 3 | Animal *a; a = new Dog(); a->SaySomething(); |
狗应该说"汪汪",猫应该说"喵",而不是印刷"某物"。在本例中,您看到a是一只狗,但有时您有一个动物指针,不知道它是哪种动物。你不想知道它是哪种动物,你只想让它说些什么。所以你只需要调用虚拟功能,猫会说"喵",狗会说"汪汪"。
当然,SaySomething函数应该是纯虚拟的,以避免可能的错误。
您将使用一个虚拟函数来实现"多态性",特别是在您有对象的地方,不知道实际的底层类型是什么,但是知道您想要对它执行什么操作,并且这个(它是如何实现的)的实现取决于您实际拥有的类型。
从本质上讲,这就是通常所说的"里斯科夫替代原则",以1983年左右谈论这一问题的芭芭拉·里斯科夫的名字命名。
在需要使用动态运行时决策的地方,在调用调用函数的代码时,无论现在还是将来,您都不知道哪些类型可以通过它,这是一个好的模型。
但这不是唯一的办法。有各种各样的"回调"可以接受"blob"数据,并且您可能有依赖于传入数据中的头块的回调表,例如消息处理器。为此,不需要使用虚函数,实际上,您可能会使用的是一种方法,即V-Table只使用一个条目实现(例如,只使用一个虚函数的类)。