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!