我们使用virtual在 Cpp 中实现动态绑定,即在运行时决定必须根据创建的实际对象而不是引用或指针调用哪个函数变量
class A
{
int a;
public:
virtual void show();
};
void A::show() { cout<<a<<endl; }
class B:public A
{
int b;
public:
void show() { cout<<b<<endl; }
};
class C:public A
{
int c;
public:
void show() { cout<<c<<endl; }
};
假设someFunction(A& aref)
。它可以采用B
或C
或A
注意:假设数据成员的值已设置
我的意思是定义了路径(它可以是A或B或C)。它不完全是run time dependent
[就像要求用户输入年龄而用户输入一些单词或其他数据类型]。
但为什么这称为为run time binding
?
如果要分配的对象兼容,编译器会事先进行检查
或没有。
术语是否用于表示引用变量与特定类型的对象之间没有严格关联,并且在运行时决定。 还有更多吗?
答案 0 :(得分:5)
虚拟方法在对象中创建 virtual table ,用于调用方法。
在运行时查找正确的方法。
最明显的情况是,如果你有一个基类列表,它包含不同类型的对象:
std::list<A*> myList = new std::list<A*>();
myList.push_back(new A());
myList.push_back(new B());
myList.push_back(new C());
for (A* a : myList)
{
a->show();
}
在这个小例子中,所有对象都是不同的类型,编译器将它们全部视为 A 对象(有一个 A 类型的变量,调用 show( )),但仍然会调用正确的方法。
答案 1 :(得分:2)
编译器无法始终确定变量的确切类型。使用基类的指针时,它们可以是任何派生类型。一般来说,派生类型的基类指针只能在运行时解析。
答案 2 :(得分:2)
其他翻译单元中可以有其他子类型。此外,即使您考虑在一个批处理中编译的所有翻译单元,动态链接也可以在源代码之后几年引入新的子类型,并且可以编译它的编译器被grue吃掉。它确实在运行时决定:调用virtual
方法生成的代码从对象中的(指针)中获取一个函数指针,因此它是一个运行时值,可以取任何值。 / p>
答案 3 :(得分:1)
实际上,你需要在运行时相关的上下文中使用一个类来实际看到后期绑定。
考虑这样的事情:
// foo.hpp
struct A {
virtual void show();
virtual ~A();
};
struct B : A {
void show();
};
A* get_stuff(std::string x);
// the implementation is in foo.cpp and doesn't interest us here
// main.cpp
#include "foo.hpp"
int main()
{
std::string input;
// .. read stuff from the user
A* a = get_stuff(input);
a->show(); // how would the compiler know which show is being called?
}
答案 4 :(得分:1)
静态绑定和动态绑定之间的区别在于静态绑定是在编译时定义的 - 这意味着无论什么类型的对象,它总是从编译器确定的任何类中调用相同的成员函数,并且动态绑定(或运行时绑定)在代码运行时定义。这意味着编译器已经生成了代码,该代码将在执行代码时“选择”相应的函数,具体取决于它所属的类。
该对象显然需要兼容,但编译器不必完全知道对象在编译期间是哪个类。想象一下,我们有一个程序可以读取文件中的“形状,中心点和大小”,其中大小只是“1-5”,从额外的小到超大,以及一个可以创建类的对象的函数square
,circle
或triangle
。现在喂它:
Square 300, 300, 5
Circle 120, 300, 2
Circle 240, 300, 2
Triangle 300, 300, 1
当然,编译器不知道你是想制作正方形,圆形还是三角形,或者它们的顺序是什么。而且每个形状的虚拟“绘制”功能明显不同。
那可能(如果坐标匹配)画出一些看起来像脸的东西(好吧,两只眼睛和一个正方形的鼻子至少)。