我阅读了很多博客,并且了解了如何在 C++ 中使用虚函数。但是,我仍然不明白为什么我们使用虚函数。你能给我一个真实世界的例子,这样我就可以更容易地想象虚函数的实际含义。
7 回答
需要提到的重要一点是继承(关键字 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。
首先,这.
现在,一个现实生活中的例子。我有一个带有三个选项卡的 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
}
}
决定使用虚函数是一件简单的事情。您只需要知道何时要覆盖基本方法。以下面的代码为例:
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.
希望这可以帮助。
他们实际上在Wiki上举了一个例子
http://en.wikipedia.org/wiki/Virtual_function
使用动物。Animals 是超类,所有动物都吃(超类虚函数)。每只动物的吃法可能与所有其他动物不同(覆盖虚拟功能)。我有一个任意动物的列表,当我调用eat函数时,它们会显示出自己不同的饮食习惯。
如果您熟悉 Java - 这应该很容易。在 Java 中,所有类方法实际上都是虚拟的。如果您在派生类中覆盖它,并通过基类引用调用它,则将调用覆盖,而不是基类。
这不是 C++ 中的默认行为。如果您希望函数以这种方式运行,则必须在基类中将其声明为虚拟。很容易。
Java 充满了虚函数。它只是没有明确的关键字。
虚函数的目的是实现动态调度。
您说您熟悉 Java,因此对于虚拟函数的实际使用,请考虑 Java 中您可能使用interface
或使用@Override
公共/受保护方法的任何地方。
我们有一个具有方法的基类(动物),可以由它的子类(例如)以不同的方式实现。当我们将这个方法声明为虚拟时,我们可以对该方法进行寻址,它将从它的子定义中实现。如果您处理子项的重载方法,则不必使用 virtual,但是当您处理父项的方法时,您必须使用。
例如,如果您有一个动物向量,每个动物都是不同的。您将方法(例如)声明为虚拟并从动物类中调用它,它将从相应的子类中调用。
如果我错了,请纠正我,这就是我的理解。