我最近在某个类中发现了这个奇怪的函数:
void* getThis() {return this;}
在代码的后面,有时会像这样使用:bla->getThis()
(其中bla是指向定义了该函数的类的对象的指针)。我无法理解这有什么作用。在某些情况下,指向对象的指针是否与对象的
this
不同(其中bla != bla->getThis()
)?这似乎是一个愚蠢的问题,但我想知道我是否漏掉了什么。
我最近在某个类中发现了这个奇怪的函数:
void* getThis() {return this;}
在代码的后面,有时会像这样使用:bla->getThis()
(其中bla是指向定义了该函数的类的对象的指针)。this
不同(其中bla != bla->getThis()
)?当然,指针值可能不同!下面是一个演示此问题的示例(您可能需要在系统上使用derived1
而不是derived2
来获得差异)。重点是,当涉及到虚拟继承和多重继承时,this
指针通常会进行调整。这可能是一个罕见的情况,但确实发生过。
此习语的一个潜在用例是能够将已知类型的对象存储为void const*
(或void*
; 在这里const
正确性无关紧要)并在需要时还原它们:如果您有一个复杂的继承层次结构,您不能仅仅将任何奇怪的指针转换为void*
并希望能够将其还原为原始类型!也就是说,在下面的示例中轻松获取例如指向base
的指针并将其转换为void*
,您将调用p->getThis()
,这样比static_cast<base*>(p)
并获得可以安全转换为base*
的void*
更容易,使用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';
}
getThis()
获得的指针转换为base const*
。您无法使用从d2
获取的指针通过将其转换为void const*
来执行相同的操作(但是,您可以将从这样获得的指针转换为derived2 const*
,即撤消隐式转换)。 - Dietmar KühlgetBase
或类似的名称,而不是 getThis
! - DavegetThis()
的代码恰好是通用的,即模板化的代码:通用代码确实喜欢使用一致的名称,如getThis()
,而不是有时使用getWidget()
,有时使用getGadget()
。 - Dietmar Kühl不可以。 在有限的情况下是可以的。
这看起来像是受 Smalltalk 启发的东西,在其中所有对象都有一个 yourself
方法。可能有一些情况下使用它会使代码更加清晰。正如评论所指出的,这似乎是在 c++ 中实现这种习惯用法的一种奇怪方式。
在您的特定情况下,我建议使用 grep 查找该方法的实际用法,以了解它是如何被使用的。
grep
部分。老实说,在我20多年的C++编程经验中,我从未遇到过需要使用这种结构的情况,但也许这只是我的个人经验。 - syamvoid* p0 = bla; void* p1 = bla->getThis();
可能会出现 p0 != p1
的情况!这只需要 bla
是指向适当构造类型的指针即可。 - Dietmar Kühl我在许多(很多很多)年前遇到过类似的东西。如果我记得正确的话,当一个类正在操作同一类的其他实例时需要它。一个例子可能是一个容器类,它可以包含自己的类型/(类?)。
这可能是覆盖this关键字的一种方法。 假设您有一个内存池,在程序开始时完全初始化,例如您知道随时可以处理最多50个消息CMessage。 您创建一个大小为50 * sizeof(CMessage)(无论这个类是什么)的池,并且CMessage实现了getThis函数。
这样,您只需覆盖“this”以访问池,而不是覆盖new关键字。 这也意味着对象可能在不同的内存空间上定义,例如在SRAM上,在引导模式下,然后在SDRAM上。
在这种情况下,当被覆盖时,相同的实例可能会在整个程序中返回不同的getThis值。
void*
类型。为什么他们不直接转换成void*
,我不知道。在别人的代码中有时候会看到一些有趣的东西。 - Dave