考虑两个指针
A* a;
B* b;
A和B都是多态类。 如何检查a和b是否指向同一个对象?
更确切地说,如果存在类型D的某个对象d,则指定a和b指向同一对象,使得* a和* b都在d的类层次结构中的某处。
我建议采用以下解决方案:
dynamic_cast<void*>(a) == dynamic_cast<void*>(b)
确实,根据标准,
dynamic_cast<void*>(v)
产生“指向v。指向的最派生对象的指针(n3242.pdf:§5.2.7-7)。 如果两者的派生最多是同一个对象,则指针指向同一个对象。
我很确定从实际角度来看它应该始终正常工作。但理论上,乍一看提议的平等似乎会产生假阳性,例如,如果b指向A的第一个成员(不是A的祖先)。虽然实际上不可能为A及其成员获取相同的地址,因为A的虚拟表指针应位于此成员之前,因此该标准不强制要求虚拟表,也不对类布局说明任何内容。
所以,我的问题是:
从标准角度来看,建议的解决方案是否正确?
是否有关于私有(受保护)继承或cv资格的警告?
有更好的解决方案吗?
[编辑]
我尝试提供一些示例来说明相对复杂的情况。在这种情况下,动态交叉铸造和静态铸造是模糊的。
// proposed impplementation:
template<typename P, typename Q>
bool test_ptrs(const P* p, const Q* q)
{
return (dynamic_cast<const void*>(p) == dynamic_cast<const void*>(q));
}
struct Root
{
virtual ~Root(){};
};
struct A: public Root // nonvirtually
{
};
struct B: public Root // nonvirtually
{
};
struct C: public A, B // nonvirtual diamond started with Root
{
Root another_root_instance;
};
int main()
{
C c;
A* pa= &c;
B* pb= &c;
bool b = (dynamic_cast<void*>(pa) == dynamic_cast<void*>(pb));
Root* pra= dynamic_cast<Root*> (pa);
Root* prb= dynamic_cast<Root*> (pb);
//Root* prc= dynamic_cast<Root*> (&c); // runtime error, ambiguous cast
Root* prr= dynamic_cast<Root*>(pra);
Root* pcar= dynamic_cast<Root*>(pra);
Root* pcbr= dynamic_cast<Root*>(prb);
if(
test_ptrs(pa, pb)
&& test_ptrs(pra, prb)
&& !test_ptrs(pa,&c.another_root_instance)
)
{
printf("\n test passed \n");
}
}
答案 0 :(得分:2)
在我看来,解决这个问题最不臭的方法是为A&amp; amp;引入一个基类。 B:
#include <iostream>
struct Base
{
virtual ~Base() {};
};
struct A : public virtual Base
{
int a;
virtual ~A() {};
virtual void afunc() {};
};
struct B : public virtual Base
{
int b;
virtual ~B() {};
virtual void bfunc() {};
};
struct C: A, B
{};
int main()
{
C c;
A *a = &c;
B *b = &c;
std::cout << "a* == " << &(*a) << std::endl;
std::cout << "b* == " << &(*b) << std::endl;
std::cout << "a == b == " << ((void*)a == (void*)b) << std::endl;
Base* ba = a;
Base* bb = b;
std::cout << "ba* == " << &(*ba) << std::endl;
std::cout << "bb* == " << &(*bb) << std::endl;
std::cout << "ba == bb == " << (ba == bb) << std::endl;
return 0;
}
答案 1 :(得分:1)
我试图通过比较这些指针指向的地址来解决这个问题。
因此理论上我们可以说像
a *和b *指向同一个对象,如果存在某个类型为C的对象c,则* a和* b都位于C的类层次结构中。“
逻辑
我们必须重新审视上述声明 “a *和b *指向同一个对象,但在类型C的obj c 的内存中有自己的访问区域,因此* a和* b都在C的类层次结构中的某个位置“”
struct Aa
{ int a;
Aa() {a= 0;}
};
struct Bb
{ int b;
Bb() { b= 0;}
};
struct C: Aa, Bb {
};
C c;
Aa *a1 = &c;
Aa *a2 = &c;
Bb *b1 = &c;
Bb *b2 = &c;
cout << &c << "\t"<< &(*a1)<<"\t"<< &(*a2)<<endl;
cout << &c << "\t"<< &(*b1)<<"\t"<< &(*b2)<<endl;
输出:
虽然这不能解决您的问题,但我们有一点可以在这里推断。
答案 2 :(得分:0)
由于使用dynamic_cast
你也可以在类型层次结构中投射“横向”,我建议:
(b != nullptr? dynamic_cast<B*>(a) == b : a == nullptr)
如果a
指向*b
开头的某个子对象,则dynamic_cast<B*>(a)
必然会返回空指针(因为B
无法包含自身)。因此,如果b不是空指针,dynamic_cast<B*>(a) == b
只有在两者共享同一个派生类时才会成功。必须特别处理b是空指针的情况,因为如果a
不为null,但未指向派生自B
的类,则dynamic_cast
测试将失败。< / p>
然而,有些情况涉及多重继承,其中此解决方案会给出假阴性(与您的解决方案不同,它永远不会产生假阴性,但会产生误报)。然而,可能发生这种情况的类层次结构是我不应该创建的层次结构(即包含多个B类型的间接基础的相同派生类)。您可以通过交换a
和b
的角色再次进行测试来减少漏报的数量(然后仅当 A
和B
在派生类最多的时候,测试会失败)。
您还可以将您和我的测试结合起来,得出三个结果: