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