是直到运行时才创建的方法的虚拟方法
Are virtual methods for methods that are not created until runtime?
我在理解c++中虚方法的目的时遇到了一点麻烦。如果一个方法的对象不是在编译时创建的,那么它必须是虚拟的吗?例如,如果您必须在运行期间挑选农场动物,那么该动物的所有方法都需要是虚拟的,因为在用户挑选一个动物之前,您不知道它是否会被创建。如果我说错了,请纠正我。
不,这是完全错误的。如果需要根据对象的类型选择方法,并且在编译时不知道类型,则方法必须是虚拟的。如果你的代码看起来像这样:
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::DoSomething
。(当然,如果这是你想要的,不要使方法成为虚方法。)
在这里阅读关于虚方法的目的。我还建议你看一看"面向对象编程"这本书。
好吧,virtual
是内置在c++中支持OOD的多态性(特别是运行时或动态的)的东西。
当您希望相同类型(Animal
)的不同对象的行为不同(getProduceName()
返回"猪肉"或"牛肉"或"鸡蛋"或…)取决于上下文(Animal
实际上是Pig
或Cow
或Chicken
或…),您使该行为/函数virtual
。
通常一个好的OOD应该有良好的接口/实现分离。在c++中,这是通过继承和abstract
类/方法来实现的。
所以实际上当你使getProduceName()
多态/virtual
你实际上是试图提取接口(Animal
)从不同的实现(Pig
或Cow
或Chicken
或…),以保护您的客户端代码从不同的实现。这样,如果你必须支持一个新的实现,你不必改变客户端代码,只要它坚持接口Animal
。
所以要回答这个问题: 如果一个方法的对象在编译时没有创建,那么它必须是虚拟的吗?
方法是否为虚方法并不取决于对象何时创建(编译/运行时)。这取决于您希望如何设计应用程序。创建一个函数virtual
可以帮助您从不同的实现中提取接口。如果某个函数(getOwnerName()
)在所有实现类({return ownerName;}
)中具有相同的实现,则不需要将其设为虚函数。
虚函数的一个重要用途是允许旧代码调用新代码。
考虑这一点。函数接受一个Car对象作为参数。假设它执行了test_drive()、refuel()、calculate_ability()、getStoppingDistance()等操作。所有这些方法都取决于您传入的汽车类型。
但是每年都有新车问世。现在,在现实世界中,我们只需导入一个XML文件,其中包含构成所有汽车的属性。但是为了方便讨论,假设我们必须静态地执行此操作:每次发现新车时,我们都必须重新构建整个程序。如果我们的程序很大,这将是非常不方便的。在某种程度上,我们调用了相同的函数,但在某种程度上又不是,因为对象的类型不同。为什么我们必须重新编译?
虚函数来拯救!当汽车制造商发布新车时,他们同时发布声明新类型的头文件(它继承了带有虚函数的公共基类)和库(定义特定方法的对象文件)。我们不需要重新编译应用程序,而是重新链接到这个新库。因此,旧代码(我们的应用程序)能够调用新代码(汽车附带的新库)。
假设你有这样的代码:
Animal * a = new Pig();
指针a
的静态类型是Animal
,动态类型是Pig
。
现在假设我们调用
a->MakeASound();
如果MakeASound
是虚拟的,则调用动态类型(Pig
)的MakeASound
方法。
如果不是,则调用静态类型的MakeASound
(Animal
),无论Pig
是否覆盖MakeASound
方法。
- 用常见虚拟函数实现的任意组合来实现派生类的正确方法是什么
- 在模板基类中为继承类中的可选重写生成虚拟方法
- 跨 DLL 边界访问虚拟方法是否安全/可能?
- 是否可以使用基类非虚拟方法中的派生类虚拟方法?
- 如何编写 operator= 用于使用虚拟方法与非平凡成员的匿名联合
- 让编译器告诉什么确切的纯虚拟方法使结构抽象?
- 使用模板而不是虚拟方法的管道模式
- 派生类调用父类的方法,该方法调用重写的虚拟方法调用错误的方法
- 从纯虚拟类 (A) 派生的指针无法访问来自纯类 (B) 的重载方法
- 为什么调用没有正文的纯虚拟方法不会导致链接器错误?
- 出于什么目的,非虚拟方法将与C++一起使用?
- 为什么使用存储在虚拟方法表中的地址调用虚拟函数的函数会返回垃圾?
- 如何重写继承的嵌套类中存在的虚拟方法
- 私有虚拟方法有什么用?
- 派生类中纯虚拟基方法的专业化
- 基类可以声明虚拟方法但不定义它吗?仍然在派生类中定义
- googletest:测试基类具有纯虚拟方法的派生类时的核心转储
- 确保模拟的 GTest 方法覆盖虚拟方法
- 使用回调函数从构造函数调用虚拟/派生方法的替代方法?
- 如何调用孩子的方法:虚拟关键字不起作用