我在理解C ++中虚方法的目的时遇到了一些麻烦。如果方法不是在编译时创建的,那么方法是否必须是虚拟的?例如,如果你必须在运行时选择一个农场动物,那么所有动物的方法都需要是虚拟的,因为在用户选择一个之前你不知道它是否会被创建。如果我错了,请纠正我。
答案 0 :(得分:7)
不,那是完全错误的。如果需要根据对象的类型选择方法并且在编译时不知道类型,则方法必须是虚拟的。如果您的代码如下所示:
Animal *x;
if(y==2)
{
x = new Animal();
x->DoSomething();
}
编译器在运行时知道x
的类型是'Animal'。所以它知道要调用哪个版本的“DoSomething”。但看看这段代码:
Animal *x;
if(y==1) x=new Zebra();
else if (y==2) x=GetSomeAnimal();
else x=new Giraffe();
x->DoSomething();
这里,x
的类型在编译时是未知的。它可以是斑马,它可以是长颈鹿,也可以是GetSomeAnimal
函数返回的任何类型的动物。无法知道DoSomething
的来电是否应该完全调用Zebra::DoSomething
,Giraffe::DoSomething
或其他内容。所以Animal::DoSomething
需要是虚拟的。
只是为了表明它与将要创建的内容无关,请考虑一下:
void MyFunction(Animal &x)
{
x.DoSomething();
}
void MyOtherFunction(int x)
{
Giraffe g;
Zebra z;
if(x==2) MyFunction(g);
else MyFunction(f);
}
这里非常清楚,将创建一个Giraffe
和一个Zebra
。但如果Animal::DoSomething
不是虚拟的,那么MyFunction
会在长颈鹿上调用Animal::DoSomething
而不是Giraffe::DoSomething
,而在Zebra上调用Zebra::DoSomething
。 (当然,如果那是你想要的,请不要将该方法设为虚拟。)
答案 1 :(得分:0)
了解虚拟方法here的用途。我还建议您查看"Object Oriented Programming in ANSI C"本书。
答案 2 :(得分:0)
嗯,virtual
是C ++中内置的支持OOD's多态的东西(特别是run-time or dynamic)。
当你想要相同类型的不同对象(Animal
)表现不同时(getProduceName()
返回“pork”或“beef”或“egg”或...)取决于上下文(是否{ {1}}实际上是Animal
或Pig
或Cow
或......)您制作的行为/功能Chicken
。
通常好的OOD会有良好的接口/实现分离。在C ++中,这是使用继承和virtual
类/方法实现的。
所以实际上,当您abstract
多态/ getProduceName()
时,实际上是在尝试从不同的实现中提取接口(virtual
)(Animal
或{{1} }或Pig
或...)来保护您的客户端代码不同的实现。这样,如果您必须支持新的实现,只要它符合接口Cow
,您就不必更改客户端代码。
回答这个问题: 如果方法在编译时没有创建,那么方法是否必须是虚拟的?
方法是否为虚拟方法并不取决于创建对象的时间(编译/运行时)。这取决于您希望如何设计应用程序。创建函数Chicken
可帮助您从不同的实现中提取接口。如果某个函数(Animal
)在所有实现类(virtual
)中具有相同的实现,则不需要将其设置为虚拟。
PS:恕我直言多态性是一个好的OOD的副产品,你不设计你的类来实现“运行时多态性”,然后在你有一个好的OO设计时展示运行时多态性。
答案 3 :(得分:0)
虚拟功能的一个重要目的是允许旧代码调用新代码。
考虑一下。函数将Car对象作为参数。假设它执行的操作包括test_drive(),refuel(),calculate_ability(),getStoppingDistance()等。所有这些方法都取决于您传入的汽车类型。
但每年都有新车出现。现在,在现实世界中,我们只需导入一个包含构成所有汽车的属性的XML文件。但是假设为了争论我们必须静态地做到这一点:每次我们发现新车时,我们都必须重建我们的整个计划。如果我们的计划很庞大,那将非常不方便。在某种程度上,我们调用相同的函数,但在某种程度上我们不是 - 因为对象的类型不同。我们为什么要重新编译?
虚拟功能救援!当汽车制造商发布新车时,他们会同时发布一个头文件,声明新类型(继承自具有虚函数的公共基类)和一个库(定义特定方法的目标文件)。因此,我们只需重新链接到这个新库,而不是重新编译我们的应用程序。因此,旧代码(我们的应用程序)能够调用新代码(汽车附带的新库)。
答案 4 :(得分:0)
假设你有这段代码:
Animal * a = new Pig();
指针a
具有Animal
的静态类型和Pig
的动态类型。
现在让我们说我们正在打电话
a->MakeASound();
如果MakeASound
是虚拟的,则调用动态类型MakeASound
方法(Pig
)。
如果不是,则调用静态类型MakeASound
(Animal
),无论Pig
是否覆盖MakeASound
方法。