C++:将派生指针转换为 void 指针,然后转换为抽象指针,并访问成员函数

Posted

技术标签:

【中文标题】C++:将派生指针转换为 void 指针,然后转换为抽象指针,并访问成员函数【英文标题】:C++: Casting derived pointer to void pointer and then to abstract pointer, and accessing member functions 【发布时间】:2014-01-27 04:16:06 【问题描述】:

我有一个控制器函数来处理许多对象,这些对象的特定类都派生自纯虚拟类。

class Abstract 
public:
    virtual bool ReadyForWork() = 0;
    virtual void DoWork() = 0;
;

然后我有以下内容:

class Specific : public virtual Abstract 
public:
    bool ReadyForWork();
    void DoWork();
;

程序创建一个 Specific 实例并将其分配给链中的一个 void 指针。

vChain->Append(new Specific());

到目前为止,一切都很好。当控制器函数启动并尝试访问具体的 Abstract 虚拟函数的实现时,虽然...... eww。

Specific 对象就在那里,我可以通过将 vChain 的内部 void* 转换为 Specific* 来访问它的内容,并且一切都会检查出来,所以内存不足不是问题。但是当我将 void* 转换为 Abstract* 时,编译器会丢失对 Specific 实现的引用,将函数地址更改为与以前完全不同的地址——即使指向对象本身的指针是正确的。尝试

// ...
Abstract *vWorkObj = (Abstract*)vChain->GetObj();
    if (vWorkObj->ReadyForWork()) 
// ...

在我的脸上产生以下结果:

Unhandled exception at 0x00000054 in Proj.exe:
0xC0000005: Access violation executing location 0x00000054.

但如果我这样做

// ...
Abstract *vWorkObj = (Specific*)vChain->GetObj();
    if (vWorkObj->ReadyForWork()) 
// ...

它运行顺利。不幸的是,这对我没有多大帮助,因为将会有多个类继承自 Abstract。

我在一个概念验证项目中尝试过类似的东西,并且运行顺利:

// ...
bool ReadyForWork(Abstract *pObjPtr) 
// ...

// ...
Specific *vObj = new Specific();
if (ReadyForWork(vObj)) 
// ...

显然编译器不喜欢在运行时解析继承类。 有没有办法访问抽象类的继承者而不显式告诉编译器它是哪个继承者?

我怀疑与 void* 之间的转换可能是导致编译器迷失方向的原因,但如果链尽可能保持通用性,它将节省一些麻烦。

我正在为 Windows 桌面使用 VS2013 Express。感谢您的宝贵时间。

【问题讨论】:

尝试不使用虚拟继承。 vChain->Append() 的参数类型是什么? @EJP:呵呵!去掉虚拟继承修复了它。谢谢。 Append() 恰好采用 void* 参数。 【参考方案1】:

问题的直接根源可能是您使用了虚拟继承,这使您的类的布局变得复杂。它通常只在复杂的多继承场景中需要,所以你可能不需要它。

您需要一个指向SpecificAbstract 子对象的指针,在您的情况下,这可能不是同一个内存地址。因此,如果您将Abstract* 存储在vChain 中,请先转换为Abstract*,以确保存储正确的地址:

vChain->Append(static_cast<Abstract*>(new Specific()));

除此之外还有一个问题,为什么vChain 首先包含void*。如果它将存储Abstract*,则根本不需要显式转换,编译器会自动找出正确的指针。

【讨论】:

虚拟继承是问题所在。至于Chain取一个void*,那仅仅是因为它在整个程序中用于组织各种事物,而不仅仅是Abstracts。如果我为每种类型都有一个链子而不是到处洒石膏会更整洁,但它也会破坏首先拥有一个通用链子的意义。 听起来有点像Chain可能应该是一个模板,这样你就可以有Chain&lt;Abstract*&gt;Chain&lt;Foo&gt;等等。标准库中可能还有一个数据结构可以满足您的需求,例如std::vector&lt;Abstract*&gt; 这是有道理的。 Chain 对这个特定程序做了一些非常具体的事情,所以我不太确定用矢量替换它......我还没有尝试过模板,这听起来就像任何开始的好时机。谢谢。【参考方案2】:

通常,当您将foo* 转换为void* 时,只有可以安全地转换回完全相同的类型。在某些情况下它有效,在某些情况下它恰好有效,但基本上在每种情况下你最好遵循这条规则。

如果您打算稍后将void* 转换为abstract*,请将foo* 转换为abstract*将其转换为void*。最好的这种转换是隐式的。

abstract* p = new foo;
void* v = static_cast<void*>(p);
abstract*p2 = static_cast<abstract*>(v);

最后,如果您知道稍后将转换为 abstract*,请考虑将同时存储的指针类型更改为 abstract*

【讨论】:

好的,我会注意的!根据他在上面回答中的评论,听起来更安全的做法是将 Chain 变成模板。

以上是关于C++:将派生指针转换为 void 指针,然后转换为抽象指针,并访问成员函数的主要内容,如果未能解决你的问题,请参考以下文章

无法将参数 1 从派生指针转换为基类指针引用

为啥C ++允许将指向基对象的指针转换为派生类的指针[重复]

有没有办法将共享 ptr 转换为 void 指针,然后将 void 指针转换回共享指针?

C++:: 指针 - 从基类转换为派生类

在 C++ 中将指针转换为 volatile void**

向上强制转换和向下强制转换