0

我阅读了很多博客,并且了解了如何在 C++ 中使用虚函数。但是,我仍然不明白为什么我们使用虚函数。你能给我一个真实世界的例子,这样我就可以更容易地想象虚函数的实际含义。

4

7 回答 7

2

需要提到的重要一点是继承(关键字 virtual 是其基础)不应仅用于代码重用,为此请使用委托。

当我们有一个类说 BroadbandConnection 并带有一个名为 connection() 的方法时,委托将是。然后您的经理说我们要添加加密,因此您创建了一个 BroadbandConnectionWithEncryption 类。您的本能可能是使用继承,然后使新类 BroadbandConnectionWithEncryption 派生自 BroadbandConnection。

这样做的缺点是初始类的创建者没有将其设计为继承,因此您需要更改其定义以使方法 connection() 虚拟,以便您可以覆盖其在派生类中的行为。这并不总是理想的。一个更好的想法是在这里使用委托来实现代码重用。

class BroadBandConnection
{
public:
    void Connection (string password)
    {
        //connection code.
    }
};

class BroadBandConnectionWithEndcryption
{
public:
    void Connection (string password)
    {
        mbroadbandconnection.Connection(password);
        //now do some stuff to zero the memory or
        //do some encryption stuff
    }

private:
    BroadBandConnection mbroadbandconnection;
};

关键字 virtual 用于多态性。顾名思义,它是一个对象具有多种形式的能力。这种决定将在设计接口或类时做出。

class IShape
{
    virtual void Draw () = 0; 
};

class Square
{
    void Draw()
    {
        //draw square on screen 
    }
};

class Circle
{
    void Draw()
    {
        //draw circle on screen
    }
};

我使用 = 0 将 Draw() 设为纯虚拟。我本可以省略它并添加一些默认实现。对于没有合理默认实现的接口,纯虚拟是有意义的。

这让我做的是将 Shape 对象传递给各种方法,它们不需要关心我刚刚给它们的东西。他们所知道的是,我必须提供一些东西来支持形状自行绘制的能力。

IShape* circle = new Circle ();
IShape* square = new Square ();

void SomeMethod (IShape* someShape)
{
   someShape->Draw(); //This will call the correct functionality of draw
}

将来,随着人们开始思考新的形状,他们可以从 IShape 派生,只要他们为 Draw 实现一些功能。他们可以将此对象传递给 SomeMethod。

于 2012-06-20T20:36:14.720 回答
1

首先,.

现在,一个现实生活中的例子。我有一个带有三个选项卡的 GUI 程序。每个选项卡都是派生自公共基础 TabBase 的类的对象。它有一个虚函数 OnActivate()。当一个选项卡被激活时,调度程序在当前选项卡上调用它。有一些常见的操作和特定于此选项卡的操作。这是通过虚函数实现的。

好处是控制器不需要知道它是什么类型的选项卡。它存储了一个 TabBase 指针数组,并且只对它们调用 OnActivate()。虚函数的魔力确保调用正确的覆盖。

class TabBase
{
    virtual void OnActivate()
    {
    //Do something...
    }
};

class SearchTab: public TabBase
{
    void OnActivate() //An override
    {
        TabBase::OnActivate(); //Still need the basic setup
        //And then set up the things that are specific to the search tab
    }
}
于 2012-06-20T18:55:57.113 回答
0

决定使用虚函数是一件简单的事情。您只需要知道何时要覆盖基本方法。以下面的代码为例:

  class animal
 {
   public:
 void sound()
 {
  cout << "nothing";
 }
};
class bird : public animal
{
 public:
void sound()
{
cout << "tweet";
}

};

在这种情况下,我想覆盖bird()。但如果我没有呢?这就是会发生的事情:

animal * a = new bird;
a->sound();
 **Output**
nothing

屏幕什么也不会说,因为出于所有意图和目的,C++ 只看到动物。但是,如果您将其声明为虚拟的,它就会知道在类层次结构中搜索最低的方法。再试一遍:

 class animal{
   public:
    virtual void sound(){cout<<"nothing";}
  };
 class bird : public animal
 {
    public:
   void sound()
    {
      cout << "tweet";
    }
  };
   animal * a = new bird;
  a->sound();
 **Output**
   tweet.

希望这可以帮助。

于 2012-06-20T19:05:59.233 回答
0

他们实际上在Wiki上举了一个例子

http://en.wikipedia.org/wiki/Virtual_function

使用动物。Animals 是超类,所有动物都吃(超类虚函数)。每只动物的吃法可能与所有其他动物不同(覆盖虚拟功能)。我有一个任意动物的列表,当我调用eat函数时,它们会显示出自己不同的饮食习惯。

于 2012-06-20T18:58:00.520 回答
0

如果您熟悉 Java - 这应该很容易。在 Java 中,所有类方法实际上都是虚拟的。如果您在派生类中覆盖它,并通过基类引用调用它,则将调用覆盖,而不是基类。

这不是 C++ 中的默认行为。如果您希望函数以这种方式运行,则必须在基类中将其声明为虚拟。很容易。

Java 充满了虚函数。它只是没有明确的关键字。

于 2012-06-20T18:58:38.890 回答
0

虚函数的目的是实现动态调度

您说您熟悉 Java,因此对于虚拟函数的实际使用,请考虑 Java 中您可能使用interface或使用@Override公共/受保护方法的任何地方。

于 2012-06-20T18:59:15.947 回答
0

我们有一个具有方法的基类(动物),可以由它的子类(例如)以不同的方式实现。当我们将这个方法声明为虚拟时,我们可以对该方法进行寻址,它将从它的子定义中实现。如果您处理子项的重载方法,则不必使用 virtual,但是当您处理父项的方法时,您必须使用。

例如,如果您有一个动物向量,每个动物都是不同的。您将方法(例如)声明为虚拟并从动物类中调用它,它将从相应的子类中调用。

如果我错了,请纠正我,这就是我的理解。

于 2012-06-20T18:55:08.960 回答