c++ - 为什么基指针可以访问虚函数中的派生成员变量
问题描述
class Base {
public:
virtual void test() {};
virtual int get() {return 123;}
private:
int bob = 0;
};
class Derived: public Base{
public:
virtual void test() { alex++; }
virtual int get() { return alex;}
private:
int alex = 0;
};
Base* b = new Derived();
b->test();
当test
和get
被调用时,隐式this
指针被传入。是因为Derived
类具有与纯基对象相同的子内存布局,那么this
指针既可用作基指针又可用作派生指针?
另一种说法是,Derived 的内存布局就像
vptr <-- this
bob
alex
这就是它可以在 in 中使用alex
的原因b->test()
,对吧?
解决方案
在 ofDerived
的方法中,隐式this
指针始终是Derived*
指针(更一般地说,this
指针始终与被调用的类类型匹配)。这就是为什么Derived::test()
并且Derived::get()
可以访问该Derived::alex
成员。那与 . 无关Base
。
对象的内存布局Derived
以 的数据成员开始Base
,然后是可选的填充,然后是 的数据成员Derived
。这允许您在需要对象的Derived
任何地方使用Base
对象。当您将指针传递给Derived*
指针Base*
或Derived&
引用时Base&
,编译器将在编译时相应地调整指针/引用以指向对象的Base
一部分Derived
。
当您b->test()
在运行时调用 whereb
是一个Base*
指针时,编译器知道test()
并且virtual
将生成访问b
的 vtable 中的适当插槽并调用所指向的方法的代码。但是,编译器不知道b
在运行时实际指向的派生对象类型(这就是多态的全部魔力),因此它不能this
在编译时自动将隐式指针调整为正确的派生指针类型。
在 whereb
指向Derived
对象的情况下,b
的 vtable 指向Derived
的 vtable。编译器知道Derived
从. 开始的确切偏移量Base
。因此,test()
inDerived
的 vtable 的插槽将指向编译器生成的私有存根,以将隐式Base *this
指针调整为Derived *this
指针,然后再跳转到Derived::test()
.
在幕后,它大致(不完全)实现如下伪代码:
void Derived_test_stub(Base *this)
{
Derived *adjusted_this = reinterpret_cast<Derived*>(reinterpret_cast<uintptr_t>(this) + offset_from_Base_to_Derived);
Derived::test(adjusted_this);
}
int Derived_get_stub(Base *this)
{
Derived *adjusted_this = reinterpret_cast<Derived*>(reinterpret_cast<uintptr_t>(this) + offset_from_Base_to_Derived);
return Derived::get(adjusted_this);
}
struct vtable_Base
{
void* funcs[2] = {&Base::test, &Base::get};
};
struct vtable_Derived
{
void* funcs[2] = {&Derived_test_stub, &Derived_get_stub};
};
Base::Base()
{
this->vtable = &vtable_Base;
bob = 0;
}
Derived::Derived() : Base()
{
Base::vtable = &vtable_Derived;
this->vtable = &vtable_Derived;
alex = 0;
}
...
Base *b = new Derived;
//b->test(); // calls Derived::test()...
typedef void (*test_type)(Base*);
static_cast<test_type>(b->vtable[0])(b); // calls Derived_test_stub()...
//int i = b->get(); // calls Derived::get()...
typedef int (*get_type)(Base*);
int i = static_cast<get_type>(b->vtable[1])(b); // calls Derived_get_stub()...
实际的细节涉及更多,但这应该让您对多态性如何能够在运行时调度虚拟方法有一个基本的了解。
推荐阅读
- java - 将sql查询转换为jpql
- java - jstl fmt:formatDate - 小时/分钟/秒不出现
- php - 无法使用 ajax 在 php 中下载 Excel 格式的文件
- python - SyntaxError: 非 ASCII 字符 '\xe2
- python - Keras,从 DataFrameIterator 获取相应标签的 numpy 数组
- wso2 - 授权承载访问令牌未在 WSO2 API MANAGER 存储的 API 控制台中自动填充
- redis - 如何在 Lua 脚本中将 ...array 传递给 redis rpush?
- node.js - FS 用于跨平台开发
- java - 没有为每个请求创建新的 HashMap 实例变量?
- python - 使用特定 OpenCV 功能时,Keras 模型不使用 GPU?