C++ 多重继承指针比较

C++ 多重继承指针比较,c++,inheritance,pointers,multiple-inheritance,C++,Inheritance,Pointers,Multiple Inheritance,我有一个派生的类,它直接从两个基类继承,Base1和Base2。一般来说,我想知道比较指针和基类是否安全,以确定它们是否是相同的派生的对象: 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

我有一个派生的类,它直接从两个基类继承,
Base1
Base2
。一般来说,我想知道比较指针和基类是否安全,以确定它们是否是相同的
派生的
对象:

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?
Base1*p1;
碱基2*p2;
/*
*这里发生了很多事情。p1和p2现在指向其
*基类型或派生类型
*/
//断言(p1==p2)//这是违法的
断言(p1==static_cast(p2))//这样行吗?
断言(static_cast(p1)=static_cast(p2))//这个怎么样?

指针保证是有效的,但不一定指向派生的
对象。我猜想这可能是好的,但我想知道从技术C++的角度看是否可以。实际上我从来没有对指针做过任何操作,我只是想知道它们是否指向同一个对象


编辑:如果我能保证
p1
p2
指向
Derrived
对象,似乎是安全的。我基本上想知道如果它们不这样做是否安全——如果其中一个或两个指向一个基本对象,比较是否一定会失败?同样,我可以保证指针是有效的(即,
p1
永远不会指向
Base2
对象,反之亦然)

简短的回答是否定的,这通常不是一个好主意

注意:假设您想要所有类的自定义等价,如果您想要检查它们是否是相同的对象,最好执行
(派生*)

更好的解决方案是为
Base1
Base2
派生的
重载
==
操作符

假设
Base1
有一个参数
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;
}

在比较之前转换到
派生*
是正确的方法


有一个类似的主题:

基于这个问题,它似乎是无效的:


基本上,由于对象在内存中的布局方式,对
Base1*
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的地址定义为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相当,但随后您会遇到各种其他问题,如虚拟现实。简而言之,我不推荐这种方法

一个更好的方法是强制转换为派生的*,就像Iliashkevich所说的,但是,如果您的对象不是派生的*,那么这将带来一个问题


(免责声明:我在3-4年内没有使用C++,所以我可能有点偏离我的游戏。请温柔):

< P>使用<代码> DyrimeCase< /COD>,并注意NULL。< /P>
#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));
}
#包括
结构Base1{virtual~Base1(){};
结构Base2{virtual~Base2(){};
派生结构:Base1,Base2{};
布尔相等(基1*p1,基2*p2){
导出*d1=动态_型铸造(p1);
导出*d2=动态_投射(p2);
如果(!d1 | |!d2)返回false;
返回d1==d2;
}
int main(){
导出d;
Base1*p1=&d;
Base2*p2=&d;
碱基1 b1;
碱基2 b2;
断言(等质量(p1,p2));
断言(!IsEqual(p1和b2));
断言(!IsEqual(&b1,p2));
断言(!IsEqual(&b1和&b2));
}

事实证明,实现您所追求目标的最短途径是:

assert(dynamic_cast<void*>(p1) == dynamic_cast<void*>(p2));
两个不同基的内存布局类似于
派生的
(在一个通用实现上-堆栈的增长方向与堆的相反)。第一个
static\u cast
使指针保持原样,但第二个将指针
sizeof(Base1)
向后移动,因此现在它们都指向
&b1
,断言成功-即使对象不同


只有在您确定强制转换是正确的情况下,才应使用
static\u cast
。这不是你的情况,所以你必须使用<代码> DyrimCysCase<代码>,可能如上面所建议的。

我感觉到C++不支持多重继承。你是说“Base1-> Base2->派生的”C++确实支持多重继承:“这是最简单的方法,在脚上开枪,但是,C++支持MI”这是最简单的方法来射击自己的脚“不”指针是有效的,但不一定指向一个派生对象。这并不是说你的建议不起作用,但你必须使用
dynamic\u cast
,你必须处理它们都返回NULL的情况(这可能意味着它们指向同一个对象,也可能不意味着它们指向同一个对象)。哦,它们永远不会为NULL-我想我已经提到过了。它们保证指向其类型的有效对象。@Lucretiel,如果调用
dynamic\u cast(p)
,并且
p
指向
Base1
类型的有效对象,则调用将返回
NULL
。直接将这样的强制转换与两个
Base1
s进行比较总是会重新进行
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 *tmp = dynamic_cast<Derived*>(p1);
  assert( tmp && tmp == dynamic_cast<Derived*>(p2) );
{
assert(dynamic_cast<void*>(p1) == dynamic_cast<void*>(p2));
Base2 b2;
Base1 b1;
assert(static_cast<Derived*>(&b1) == static_cast<Derived*>(&b2));  // succeeds!