C++从内部对象访问外部包含对象的合规实现方案问询
符合标准的解决方案
在你给出的前提条件下,我们可以通过以下方式实现安全且符合标准的外部实例访问:
核心原则
要让指针计算合法,需满足两个关键条件:
- 包含类
Foo必须是标准布局类型:- 无虚函数、无虚基类
- 所有非静态数据成员的访问权限(public/protected/private)一致
- 无嵌套的非标准布局类作为基类
- 所有非静态数据成员要么都在最派生类,要么都在同一个基类中
- 使用
std::launder(C++17及以上)包装转换后的指针,避免编译器优化导致的未定义行为。
针对单个成员的示例修改
#include <cstdint> #include <cstddef> #include <new> // 引入std::launder class Foo { private: // 所有非静态成员权限一致,满足标准布局 struct Bar { void moo(); }; uint32_t meow; Bar bar; }; void Foo::Bar::moo() { // 计算Foo对象的起始地址,并用std::launder确保指针有效性 auto foo_ptr = std::launder(reinterpret_cast<Foo*>( reinterpret_cast<uint8_t*>(this) - offsetof(Foo, bar) )); foo_ptr->meow++; }
针对数组成员的示例修改
#include <cstddef> #include <new> struct Cow { virtual void moo() = 0; }; struct Foo { constexpr static uint32_t N_COWS = 2; explicit Foo(); struct CowImpl: public Cow { uint8_t idx; void moo() override; }; CowImpl cows[N_COWS]; // 与其他成员权限一致,Foo满足标准布局 void moo(uint8_t cowIndex); }; Foo::Foo() { for (uint32_t i = 0; i < N_COWS; i++) { cows[i].idx = i; } } void Foo::CowImpl::moo() { const size_t total_offset = offsetof(Foo, cows) + idx * sizeof(CowImpl); auto foo_ptr = std::launder(reinterpret_cast<Foo*>( reinterpret_cast<uint8_t*>(this) - total_offset )); foo_ptr->moo(idx); }
说明
- 即使内部类(如
CowImpl)因虚函数不是标准布局,只要Foo是标准布局,offsetof(Foo, cows)就是合法的。 - 单一继承场景下,
this指向的是CowImpl对象的起始地址,因此指针算术计算结果准确。 std::launder的作用是告知编译器:转换后的指针指向一个已存在的、生命周期有效的Foo对象,避免编译器因优化忽略该指针的有效性。
内容的提问来源于stack exchange,提问作者Maxim
相关产品推荐
相关产品推荐

