我在Linux上使用GCC。
我希望了解正在运行的虚拟功能。
我应该编写什么样的C ++代码来查看和理解在有和没有虚函数的情况下,静态和动态绑定是如何发生的?
如何“看”他们最终如何受约束以及在此过程中究竟发生了什么?
答案 0 :(得分:1)
这是一个例子。您可以使用设置为虚函数的函数来构建和运行此代码。要获得虚拟行为,动态分派,动态绑定,请使用定义的预处理器宏IS_VIRTUAL
构建它。要查看静态绑定,请在不定义宏的情况下构建它。
#include <iostream>
#if defined(IS_VIRTUAL)
#define CONDITIONAL_VIRTUAL virtual
#else
#define CONDITIONAL_VIRTUAL
#endif
struct A {
CONDITIONAL_VIRTUAL void foo() { std::cout << "A\n"; }
};
struct B : A {
CONDITIONAL_VIRTUAL void foo() { std::cout << "B\n"; }
};
// global objects
A a; B b;
enum object_type { get_A, get_B };
A *get_object(object_type t) {
switch (t) {
case get_A: return &a;
case get_B: return &b;
}
}
int main() {
std::cout << "Choose A or B: ";
char c;
std::cin >> c;
A *x = get_object( c == 'A' ? get_A : get_B );
x->foo();
}
绑定与x->foo()
的评估相关。编译器必须确定要为该表达式执行的代码。通过静态和动态绑定,编译器查看x
并查看其类型为A*
,因此它会查看struct A
并查找foo()
声明。
使用静态绑定,编译器会发现foo()
不是virtual
,因此编译器会继续执行并生成调用foo()
方法的代码。简单。
使用动态绑定,编译器会看到标记为virtual
的方法,因此编译器会生成代码,该代码在运行时将使用与对象x
关联的函数指针表来选择调用的方法,然后调用找到的任何方法。编译器还在其他位置生成代码,以便为全局a
和b
对象创建表。对于全局a
对象,它使表格指向A::foo()
,而对于全局b
,它使表格指向B::foo()
。因此,如果x
指向b
对象,则表格查找将导致B::foo()
,这就是将被调用的函数。
一般情况下,编译器必须确保所有具有虚拟方法的对象都有一个表,指向要调用的正确函数,以便每当对程序可以对象进行虚拟调用时,运行时,获取与该对象关联的表,并查找正确的方法进行调用。
因此,在静态和动态模式下构建上述程序,然后运行它并观察每个输入获得的输出。使用每种输入组合和绑定类型获得的输出填写下表。
Binding | static dynamic
Input
-----
A ? ?
B ? ?
在所有情况下,输出都是通过评估相同的x->foo()
方法调用生成的。在哪些情况下是动态绑定的证据?这是否符合您对上述动态绑定解释的理解?
答案 1 :(得分:-1)
class Base {
public:
int Foo();
virtual int Bar();
};
class D1 : public Base {
public:
int Foo();
virtual int Bar();
};
class D2 : public Base {
public:
int Foo();
virtual int Bar();
};
main()
{
Base * b = (rand() < 100) ? new D1 : new D2;
// Always calls Base::Foo()
b->Foo();
// Call either D1::Bar() or D2::Bar()
b->Bar();
}