我最近在某个课程中遇到了这个奇怪的功能:
void* getThis() {return this;}
稍后在代码中有时会这样使用它:(bla->getThis()
其中bla是指向定义此函数的类的对象的指针。)而且我似乎无法意识到这有什么好处。是否存在指向对象的指针与对象的this
(where bla != bla->getThis()
)不同的情况?
这似乎是一个愚蠢的问题,但我想知道我是否在这里遗漏了什么..
我最近在某个课程中遇到了这个奇怪的功能:
void* getThis() {return this;}
稍后在代码中有时会这样使用它:(bla->getThis()
其中bla是指向定义此函数的类的对象的指针。)而且我似乎无法意识到这有什么好处。是否存在指向对象的指针与对象的this
(where bla != bla->getThis()
)不同的情况?
这似乎是一个愚蠢的问题,但我想知道我是否在这里遗漏了什么..
当然,指针值可以不同!下面是一个演示问题的示例(您可能需要derived1
在您的系统上使用而不是derived2
获得差异)。关键是this
指针通常会在涉及虚拟多重继承时进行调整。这可能是一种罕见的情况,但它会发生。
这个习惯用法的一个潜在用例是能够在将已知类型的对象存储为void const*
(或void*
;const
正确性在这里无关紧要)之后恢复它们:如果你有一个复杂的继承层次结构,你就不能只转换任何奇怪的指向 a 的指针,void*
并希望能够将其恢复为原始类型!也就是说,为了轻松获得例如指向(来自下面的示例)的指针并将base
其转换为隐式转换,但前提是您转换回原始指针来自的确切类型。也就是说,哪里void*
p->getThis()
static_cast<base*>(p)
void*
base*
static_cast<base*>(v)
static_cast<base*>(static_cast<void*>(d))
d
指向派生自的类型的对象的指针base
是非法的,但是static_cast<base*>(d->getThis())
是合法的。
现在,为什么首先要更改地址?在示例base
中是两个派生类的虚拟基类,但可能还有更多。其类实际上继承自的所有子对象将在进一步派生类的对象中base
共享一个共同的主题(在下面的示例中)。根据不同类的排序方式,该子对象的位置可能相对于相应的派生子对象不同。结果,指向对象的指针通常不同于指向虚拟继承的类的子对象的指针base
concrete
base
base
base
. 如果可能,相关偏移量将在编译时计算,或者在运行时来自诸如 vtable 之类的东西。沿继承层次结构转换指针时会调整偏移量。
#include <iostream>
struct base
{
void const* getThis() const { return this; }
};
struct derived1
: virtual base
{
int a;
};
struct derived2
: virtual base
{
int b;
};
struct concrete
: derived1
, derived2
{
};
int main()
{
concrete c;
derived2* d2 = &c;
void const* dptr = d2;
void const* gptr = d2->getThis();
std::cout << "dptr=" << dptr << " gptr=" << gptr << '\n';
}
不,是的,在有限的情况下。
这看起来像是受 Smalltalk 启发的东西,其中所有对象都有一个yourself
方法。在某些情况下,这可能会使代码更清晰。正如评论所指出的,这看起来是一种奇怪的方式,甚至可以在 C++ 中实现这个习语。
在您的具体情况下,我会 grep 了解该方法的实际用法,以了解它是如何使用的。
您的班级可以有自定义operator&
(因此&a
可能不会返回this
)a
。这就是std::addressof存在的原因。
我在很多(很多很多)年前遇到过这样的事情。如果我没记错的话,当一个类操作同一类的其他实例时需要它。一个例子可能是一个容器类,它可以包含自己的类型/(类?)。
这可能是一种覆盖 this 关键字的方法。假设您有一个内存池,在程序开始时完全初始化,例如您知道在任何时候您最多可以处理 50 条消息,CMessage。您创建一个大小为 50 * sizeof(CMessage) 的池(无论此类可能是什么),CMessage 实现 getThis 函数。
这样,您无需覆盖 new 关键字,而是覆盖“this”,访问池。这也可能意味着对象可能定义在不同的内存空间上,比如说在 SRAM 上、在引导模式下,然后在 SDRAM 上。
在这种情况下,同一个实例可能会通过程序为 getThis 返回不同的值,当然,在被覆盖时是故意的。