“菱形继承”在gcc下无法从派生类cast到基类的问题。

天堂里的死神 2013-05-09 11:57:07
gcc4.7.3
下面的代码error处编译不过,想请问怎么能绕过这个检查,使得可以直接从Foo指针转换为A指针?


class A
{};
class B : public A
{};
class C : public A
{};

class Foo
: public A
, public B
, public C
{};



int main()
{
Foo* f = new Foo();
B *ptrb = (B*)(Foo*)f;
C *ptrc = (C*)(Foo*)f;
A *ptr = (A*)(Foo*)f; // error

cout << "Hello world!" << endl;
return 0;
}


VC下正常通过,不会导致编译错误。


传统的菱形继承导致的问题是这样的,这个我会绕,也很能理解,确实从Foo到A,不知道到底是B的A还是C的A。

class A
{};
class B : public A
{};
class C : public A
{};

class Foo
: public B
, public C
{};



int main()
{
Foo* f = new Foo();
A *ptr = (A*)(B*)(Foo*)f;
cout << "Hello world!" << endl;
return 0;
}

不过在最上面的例子中,我已经是写明了Foo是更优先从A派生的,这时如果我需要A的指针,是希望编译器能够放我一马的。

请不要问为什么不用virtual继承什么的,那样跑题了,我现在是要在gcc下编译某个第三方库,没法修改第三方库的头文件,而且这里必须要获取到A在Foo中的偏移……所以,总之,请不要问我为什么要这么做,我只想知道gcc这里能不能用什么办法给绕过去……

多谢!!
...全文
279 18 打赏 收藏 转发到动态 举报
AI 作业
写回复
用AI写文章
18 条回复
切换为时间正序
请发表友善的回复…
发表回复
youyou1912 2013-05-11
  • 打赏
  • 举报
回复
引用 16 楼 noslopforever 的回复:
[quote=引用 13 楼 adlay 的回复:] 如果 A B C 是 COM 接口, 并且 B 和 C 从 A 继承了的话, 你的实现不必去直接继承 A 呀, 你的类只要能转换成 A 就行了, 而继承了 B 和 C 后已经满足这个要求了.
我怀疑对方的思路可能是为了让接口的派生看起来更清晰、更明确? 向下面这样: interface IReflectedObject : public IUnknown {}; interface IDataBindingObject : public IReflectedObject {}; interface IEventBindingObject : public IReflectedObject {}; interface IEventDispatcher : public IEventBindingObject, public IDispatcher {}; class CommonObject : public IEventDispatcher, public IDataBindingObject, public IReflectedObject { };[/quote] 同意楼上啊, 既然是COM, 用VC编译就是了..
天堂里的死神 2013-05-10
  • 打赏
  • 举报
回复
多谢诸位大大。 实际上这里Foo那里写的有点小问题,事实上是A并非Foo的第一继承…… 所以 (A*)(void*)和reintepret_cast都无法达到目的。 此外,真正的代码继承关系更复杂一些,所以我就只是拿出来出问题的地方来弄了,A、B、C其实是几个COM interface,而且还不是我维护的地方,我无权修改这些Interface的声明…… 所以就比较杯具。
youyou1912 2013-05-10
  • 打赏
  • 举报
回复
输出
22ac2d:0
22ac2e:1
22ac2f:2
22ac2d
Hello world!
天堂里的死神 2013-05-10
  • 打赏
  • 举报
回复
额,这样正好A是第一继承所以应该可以解决,但是要是A不是第一继承呢? 比如: class Foo : public B , public C , public A {};
引用 1 楼 chhxxc 的回复:
最牛逼的强转 reinterpret_cast
youyou1912 2013-05-10
  • 打赏
  • 举报
回复
测试了一下, VS2012, g++ 4.5 都把第一个A和F对齐. 因此可以直接reinterpret_cast. 当然仍然不够非常安全.

#include <iostream>
using namespace std;

class A
{
public:
	A(int x = 0){ cout << hex << int(this) << ":" << x << endl; }
};
class B : public A
{
public:
	B():A(1){}
};
class C : public A
{
public:
	C():A(2){}
};

class Foo
	: public A
	, public B
	, public C
{
};



int main()
{
	Foo f;
	cout << hex << (int)(&f) << endl;
	A* pp = reinterpret_cast<A*>(&f);
	cout << "Hello world!" << endl;
	return 0;
}
奇乐二二二 2013-05-10
  • 打赏
  • 举报
回复
最牛逼的强转 reinterpret_cast
www_adintr_com 2013-05-10
  • 打赏
  • 举报
回复
如果代码里面这样用, 首先是检查你的设计是不是有问题. 如果是考验语言和编译器, 当我没说.
www_adintr_com 2013-05-10
  • 打赏
  • 举报
回复
既然是 com ,还是用 vc 去编译吧。 不是所有的代码都符合标准,跨平台,支持各种编译器的。 你要不修改代码就绕过去,就只有看gcc的编译选项了,估计你得修改 gcc 编译器才行。虽然gcc是开源的,也不是那么容易。
derekrose 2013-05-10
  • 打赏
  • 举报
回复
3L有点吊!
always_learn 2013-05-10
  • 打赏
  • 举报
回复
A *ptr = (A*)(Foo*)f; // error ==> A *ptr = (A*)(void*)f;
飞天御剑流 2013-05-10
  • 打赏
  • 举报
回复
3楼的解法是比较妙的,同事提醒一下楼主,FOO中存在3个A类子对象,你获得的只是其中一个A类子对象的地址。
天堂里的死神 2013-05-10
  • 打赏
  • 举报
回复
恩,多谢,最好是能不改,改的话会比较麻烦。看来这个问题确实没什么太好的办法…… 您说的这个,最后实在不行就按您说的这个来……
天堂里的死神 2013-05-10
  • 打赏
  • 举报
回复
引用 13 楼 adlay 的回复:
如果 A B C 是 COM 接口, 并且 B 和 C 从 A 继承了的话, 你的实现不必去直接继承 A 呀, 你的类只要能转换成 A 就行了, 而继承了 B 和 C 后已经满足这个要求了.
我怀疑对方的思路可能是为了让接口的派生看起来更清晰、更明确? 向下面这样: interface IReflectedObject : public IUnknown {}; interface IDataBindingObject : public IReflectedObject {}; interface IEventBindingObject : public IReflectedObject {}; interface IEventDispatcher : public IEventBindingObject, public IDispatcher {}; class CommonObject : public IEventDispatcher, public IDataBindingObject, public IReflectedObject { };
天堂里的死神 2013-05-10
  • 打赏
  • 举报
回复
引用 12 楼 jiandingzhe 的回复:
你需要用虚继承,以表明B和C的其中一个不持有A基类。 具体你查书吧,我没用过这个特性。
主要是因为ABC都是COM接口,是绝对不能用虚继承的……
天堂里的死神 2013-05-10
  • 打赏
  • 举报
回复
引用 13 楼 adlay 的回复:
如果 A B C 是 COM 接口, 并且 B 和 C 从 A 继承了的话, 你的实现不必去直接继承 A 呀, 你的类只要能转换成 A 就行了, 而继承了 B 和 C 后已经满足这个要求了.
关键就是那部分是来自其他模块的,我改不了,否则就不纠结了……
www_adintr_com 2013-05-10
  • 打赏
  • 举报
回复
引用 11 楼 noslopforever 的回复:
多谢诸位大大。 实际上这里Foo那里写的有点小问题,事实上是A并非Foo的第一继承…… 所以 (A*)(void*)和reintepret_cast都无法达到目的。 此外,真正的代码继承关系更复杂一些,所以我就只是拿出来出问题的地方来弄了,A、B、C其实是几个COM interface,而且还不是我维护的地方,我无权修改这些Interface的声明…… 所以就比较杯具。
如果 A B C 是 COM 接口, 并且 B 和 C 从 A 继承了的话, 你的实现不必去直接继承 A 呀, 你的类只要能转换成 A 就行了, 而继承了 B 和 C 后已经满足这个要求了.
jiandingzhe 2013-05-10
  • 打赏
  • 举报
回复
你需要用虚继承,以表明B和C的其中一个不持有A基类。 具体你查书吧,我没用过这个特性。
ri_aje 2013-05-10
  • 打赏
  • 举报
回复
如果你能够控制 Foo 的实现,可以试一下下面的。

#include <iostream>
using namespace std;

class A {};
class B : public A {};
class C : public A {};
struct A_proxy_t : A { };

class Foo
: public A_proxy_t
, public B
, public C
{};

int main ()
{
 A *ptr = (A_proxy_t*)new Foo;
}
A_proxy_t 唯一的作用就是辨别继承路径,并且应该保持空实现。

65,198

社区成员

发帖
与我相关
我的任务
社区描述
C++ 语言相关问题讨论,技术干货分享,前沿动态等
c++ 技术论坛(原bbs)
社区管理员
  • C++ 语言社区
  • encoderlee
  • paschen
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
  1. 请不要发布与C++技术无关的贴子
  2. 请不要发布与技术无关的招聘、广告的帖子
  3. 请尽可能的描述清楚你的问题,如果涉及到代码请尽可能的格式化一下

试试用AI创作助手写篇文章吧