在尝试理解虚函数和RTTI的内部工作原理时,我通过检查gcc编译器观察了后续事实:
当结构或类具有虚函数而不是它们占用的空间时,通过使用指针确定它们的类型来扩展它们。
更糟糕的是,当多重继承处于活动状态时,每个子结构都会添加另一个指针。所以例如结构为:
struct C: public A, public B{...}
创建一个结构,其中包含A和B的数据以及两个指针。 这是记忆和效率消耗。 问题是,是否需要。
我没有处理这些论点的经验,但是我想问的问题是否有错误的逻辑如下:
a)当我们声明某个结构类型的变量时,我们知道它的类型。因此,当我们必须通过值将该变量传递给另一个函数时,我们知道它的类型,我们可以将它作为一个普通的旧结构传递,并附加一个指示其类型的额外值(我的意思是编译器应该采用照顾那个)。
b)当我们通过引用或指针传递结构时,我们再次知道起始类型,我们可以传递指针以及指示类型的额外值。
为了将按值传递的结构分配给更窄的类型,我们只需要采用必要的子部分(偏移计算)。 为了更改为更窄的指针,我们必须调整指针并相应地更改指示类型的额外值。
因此,沿着这一行,我们需要在堆栈上的函数之间传递被调用的参数时才有额外的类型指示,并且不需要将它们保存在每个结构中。
我错过了什么? 我看到的唯一情况是需要与数据一起存储的类型信息是我们使用联盟时我们需要保存任何类型的特定结构列表,但是任何情况都可以通过其他方式解决。 所以问题是,除了在函数之间传递参数时,还需要动态类型信息,以及最终是否由标准强制执行。
答案 0 :(得分:1)
我错过了什么?
rtti和dynamic_cast
需要能够从任何一个接口推断出对象的布局。
除非每个接口封装一个指向此信息的指针,否则你将如何做到这一点?
RTTI示例:
struct A { virtual ~A() = default; }; // virtual base class
struct B : A {}; // derived polymorphic class
A* p = new B();
std::cout << typeid(*p).name() << std::endl;
为读者练习:
打印哪个班级名称?
为什么?
答案 1 :(得分:0)
我认为,以下代码将解释所有内容:
// in header.h
struct Base {
virtual void foo() = 0;
};
// in impl.cpp
void work(Base* b) {
b->foo();
}
假设上面的代码被编译到C ++库中。之后从未接触过。
现在,一段时间后,我们编写以下代码:
#include <header.h>
struct D : Base {
void foo() { std::cout << "Foo!\n"; }
};
void my() {
D* d = new D;
work(d);
}
如您所见,我们无法将任何类型传递给work()
- 编译时,D
甚至不存在!通过指向虚函数表的虚函数的优雅 - 这就是那些指针 - 是允许上面的设计。