多重继承指针比较
Multiple inheritance pointer comparison
我有一个类Derived
,它直接从两个基类 Base1
和 Base2
继承。我想知道一般来说,比较基类的指针以确定它们是否是同一个Derived
对象是否安全:
Base1* p1;
Base2* p2;
/*
* Stuff happens here. p1 and p2 now point to valid objects of either their
* base type or Derived
*/
//assert(p1 == p2); //This is illegal
assert(p1 == static_cast<Base1*>(p2)); //Is this ok?
assert(static_cast<Derived*>(p1) == static_cast<Derived*>(p2)); //How about this?
指针保证有效,但不一定指向Derived
对象。我的猜测是这可能没问题,但我想知道从技术C++的角度来看它是否可以。我实际上从不对指针进行任何操作,我只想知道它们是否指向同一个对象。
编辑:如果我能保证p1
和p2
指向Derrived
对象,这似乎是安全的。我基本上想知道如果他们不这样做是否安全 - 如果一个或两个都指向基本对象,比较一定会失败吗?同样,我可以保证指针是有效的(即,p1
永远不会指向Base2
对象,反之亦然)
好吧,不,它不起作用。
我个人是举例学习的忠实粉丝,所以这里有一个:
#include <iostream>
class Base1
{
public:
Base1()
{
numberBase1 = 1;
}
int numberBase1;
};
class Base2
{
public:
Base2()
{
numberBase2 = 2;
}
int numberBase2;
};
class Derived : public Base1, public Base2
{
public:
Derived()
{
numberDerived = 3;
}
int numberDerived;
};
int main()
{
Derived d;
Base1 *b1 = &d;
Base2 *b2 = &d;
std::cout << "d: " << &d << ", b1: " << b1 << ", b2: " << b2 << ", d.numberDerived: " << &(d.numberDerived) << std::endl;
return 0;
}
我计算机上的一次运行输出了以下内容:
d: 0035F9FC, b1: 0035F9FC, b2: 0035FA00, d.numberDerived: 0035FA04
所以。。如果我们将 d 的地址定义为 0,则 b1 为 0,b2 为 +4,d 的数量为 +8。这是因为我机器上的 int 长度为 4 字节。
基本上,你必须查看C++内部如何表示类的布局:
Address: Class:
0 Base1
4 Base2
8 Derived
..因此,总的来说,实例化派生类将为派生类的基类分配空间,并最终为派生对象本身腾出空间。由于我们这里有 3 个整数,因此将是 12 个字节。
现在,你要问的是(除非我误解了什么)是是否可以比较不同基类指针的地址,看看它们是否指向同一个对象,答案是否定的 - 至少不是直接的,就像在我的示例中一样,b1 将指向 0035F9FC,而 b2 将指向 0035FA00。在C++中,此偏移全部在编译时完成。
你可能会用RIIA和sizeof()做一些魔术,并确定多少偏移b2应该与b1相当,但随后你遇到了各种其他麻烦,如虚拟。简而言之,我不推荐这种方法。
更好的方法是像 ialiashkevich 所说的那样投射到 Derived*,但是,如果你的对象不是 Derived* 的实例,这将带来问题。
(免责声明;我已经 3-4 年没有使用过C++了,所以我可能有点偏离了我的游戏。温柔:))
在比较之前投射到Derived*
是正确的方法。
有一个类似的话题:C++指针多继承乐趣
好吧,事实证明,实现您正在寻找的目标的最短方法是:
assert(dynamic_cast<void*>(p1) == dynamic_cast<void*>(p2));
动态强制转换为void*
有效地将给定指针向下转换为其派生最多的类,因此可以保证如果两者都指向同一对象,断言不会失败。
事实上,动态投射到无效指针是有实际用途的......
编辑:要回答问题的编辑,比较是不安全的。请考虑以下代码:
Base2 b2;
Base1 b1;
assert(static_cast<Derived*>(&b1) == static_cast<Derived*>(&b2)); // succeeds!
两个不同基础的内存布局类似于Derived
的内存布局(在通用实现上 - 堆栈与堆相反)。第一个static_cast
保持指针不变,但第二个sizeof(Base1)
指针向后移动,所以现在它们都指向&b1
,并且断言成功 - 即使对象不同。
只有当您确定强制转换正确时,才应使用static_cast
。这不是您的情况,因此您必须使用 dynamic_cast
,可能如上所述。
简短的回答是否定的,这通常不是一个好主意。
注意:这是假设您希望所有类的自定义等效性,如果您想检查它们是否是同一对象,最好执行(Derived *)
。
一个更好的解决方案是重载==
运算符进行Base1
、Base2
和Derived
。
假设Base1
有 1 个参数param1
表示相等,Base2
有另一个参数param2
表示相等:
virtual bool Base1::operator==(object& other){
return false;
}
virtual bool Base1::operator==(Base1& other)
{
return this.param1 == other.param1;
}
virtual bool Base2::operator==(object& other){
return false;
}
virtual bool Base2::operator==(Base2& other)
{
return this.param2 == other.param2;
}
virtual bool Derived::operator==(object& other){
return false;
}
virtual bool Derived::operator==(Derived& other){
return this.param1 == other.param1 && this.param2 == other.param2;
}
virtual bool Derived::operator==(Base1& other){
return this.param1 == other.param1;
}
virtual bool Derived::operator==(Base2& other){
return this.param2 == other.param2;
}
基于这个SO问题,它似乎是无效的:C++的多重继承是如何实现的?
基本上,由于对象在内存中的布局方式,转换为Base1*
或Base2*
会导致指针突变,如果没有dynamic_cast
,我无法在运行时任意反转,我想避免这种情况。谢谢大家!
使用 dynamic_cast
,并注意 NULL。
#include <cassert>
struct Base1 { virtual ~Base1() {} };
struct Base2 { virtual ~Base2() {} };
struct Derived : Base1, Base2 {};
bool IsEqual(Base1 *p1, Base2 *p2) {
Derived *d1 = dynamic_cast<Derived*>(p1);
Derived *d2 = dynamic_cast<Derived*>(p2);
if( !d1 || !d2 ) return false;
return d1 == d2;
}
int main () {
Derived d;
Base1 *p1 = &d;
Base2 *p2 = &d;
Base1 b1;
Base2 b2;
assert(IsEqual(p1, p2));
assert(!IsEqual(p1, &b2));
assert(!IsEqual(&b1, p2));
assert(!IsEqual(&b1, &b2));
}
assert(p1 == p2); //This is illegal
assert(p1 == static_cast<Base1*>(p2)); //Is this ok?
assert(static_cast<Derived*>(p1)
== static_cast<Derived*>(p2)); //How about this?
它们都不是一个好的解决方案。第一个不会编译,因为您无法比较不相关类型的指针。第二个也不会编译(除非Base1
和Base2
通过继承相关),原因相同:您不能static_cast
到不相关类型的指针。
第三种选择是边缘。也就是说,它不正确,但在许多情况下它会起作用(只要继承不是虚拟的)。
比较标识的正确方法是对派生类型使用 dynamic_cast
并检查 null:
{
Derived *tmp = dynamic_cast<Derived*>(p1);
assert( tmp && tmp == dynamic_cast<Derived*>(p2) );
{
- 智能指针作为无序映射键,并通过引用进行比较
- std::less是否应该允许在编译时比较不相关的指针?
- 禁止指针和整数之间的比较C++
- ISO C++禁止指针和整数 [-fpermissive] [c++] 之间的比较
- 谷歌测试比较指针数组的内容
- 使用模板专用化来比较指针引用
- 重载 >= 运算符来比较指针
- 以C++(Arduino)为单位比较指针到字节
- 使用关系运算符比较指针意味着什么
- C++不会让我比较指针和整数
- 比较指针对象相等性
- 用于比较指针到 NULL 的编码标准
- 无法比较指针和整数c++
- 如何比较指针
- 在stl_algorithm .h中比较指针时出错
- ISO C++禁止在C++代码中比较指针和整数[-fpermissive]
- 比较指针和整数时出现编译器错误
- 标准::地图比较指针
- 比较指针.多重继承问题
- ISO c++禁止在devc++中比较指针和整数[-fpermissive]错误