为何使用虚拟功能?

时间:2012-01-11 18:06:14

标签: c++ virtual-functions

  

可能重复:
  Can someone explain C++ Virtual Methods?

我对C ++虚函数有疑问。

为什么以及何时使用虚拟功能?任何人都可以给我实时实现或使用虚拟功能吗?

4 个答案:

答案 0 :(得分:51)

如果要覆盖派生类的某个行为(读取方法)而不是为基类实现的行为(并且希望在运行时通过指向基类的指针),则使用虚函数。

经典示例是当您有一个名为Shape的基类和从中派生出来的具体形状(类)时。每个具体类都覆盖(实现一个名为Draw()的虚方法)。

类层次结构如下:

Class hierarchy

以下代码段显示了该示例的用法;它创建了一个Shape类指针数组,其中每个指针都指向一个不同的派生类对象。在运行时,调用Draw()方法会导致调用该派生类重写的方法,并绘制(或呈现)特定的Shape

Shape *basep[] = { &line_obj, &tri_obj,
                   &rect_obj, &cir_obj};
for (i = 0; i < NO_PICTURES; i++)
    basep[i] -> Draw ();

上述程序只使用指向基类的指针来存储派生类对象的地址。这提供了松耦合,因为如果随时添加新的具体派生类shape,程序不必急剧改变。原因是实际使用(依赖)具体Shape类型的代码段最少。

以上是着名Open Closed Principle设计原则的SOLID的一个很好的例子。

答案 1 :(得分:22)

当您需要以相同方式处理不同对象时,可以使用虚函数。它被称为多态性。让我们假设您有一些基类 - 类似于经典的形状:

    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 }
    };

现在你可以拥有不同形状的矢量:

    vector<Shape*> shapes;
    shapes.push_back(new Rectangle());
    shapes.push_back(new Circle());

你可以画出这样的形状:

    for(vector<Shape*>::iterator i = shapes.begin(); i != shapes.end(); i++)
    {
          (*i)->draw();
    }

通过这种方式,您可以使用一个虚拟方法绘制不同的形状 - draw()。根据有关指针后面对象类型的运行时信息选择正确的方法版本。

<强>通知 当您使用虚函数时,您可以将它们声明为纯虚拟(就像在类Shape中一样,只需在方法proto之后放置“= 0”)。在这种情况下,您将无法使用纯虚函数创建对象实例,它将被称为Abstract类。

在析构函数之前还要注意“虚拟”。如果您计划通过指向其基类的指针来处理对象,则应该声明析构函数为virtual,因此当您为基类指针调用“delete”时,将调用所有析构函数链并且不会发生内存泄漏。

答案 2 :(得分:20)

想想动物类,从中衍生出来的是猫,狗和牛。动物类有一个

virtual void SaySomething()
{
    cout << "Something";
}

功能

Animal *a;
a = new Dog();
a->SaySomething();

不应该打印“Something”,狗应该说“Bark”,猫应该说“Meow”。在这个例子中,你看到a是一只狗,但有时候你有一个动物指针并且不知道它是哪种动物。你不想知道它是哪种动物,你只想让动物说些什么。所以你只需要调用虚函数,猫会说“喵”,狗就会说“吠”。

当然,SaySomething函数应该是纯虚拟的,以避免可能的错误。

答案 3 :(得分:1)

您将使用虚函数来实现“多态”,特别是在您有对象的情况下,不知道实际的底层类型是什么,但知道您要对其执行什么操作,以及此实现(它是如何做的)取决于你实际拥有的类型。

基本上通常所谓的“利斯科夫替代原则”是以芭芭拉·莱斯科夫(Barbara Liskov)的名字命名的,他在1983年左右谈到这一点。

在需要使用动态运行时决策的地方,在调用调用函数的代码时,您不知道现在或将来可以通过它传递什么类型,这是一个很好的模型。

这不是唯一的方法。有各种各样的“回调”可以采用“blob”数据,并且你可能有回调表依赖于进来的数据中的标题块,例如:消息处理器。为此,不需要使用虚函数,实际上您可能会使用的是如何仅使用一个条目(例如,只有一个虚函数的类)实现v表的方式。