【发布时间】:2017-02-22 02:09:04
【问题描述】:
给定一个基类B,如:
class B
{
public:
virtual ~B() = default;
public:
virtual int f() const = 0;
};
和许多派生类Ai: public B (i=1,..,N),实现f(),我收到一个void*,肯定持有来自外部程序的派生Ai 类之一 - 到执行f() 方法。
可以为每个可能的派生类型创建一个入口点,并且可以正常工作:
// for each derived class Ai
void executeF(void* aPtr, int* result)
{
auto aObjPtr = static_cast<Ai*>(aPtr);
*result = aObjPtr->f();
}
不过,应该可以只用一个函数来达到同样的效果,比如:
void executeF(void* aPtr, int* result)
{
auto bObjPtr = static_cast<B*>(aPtr); // works
*result = bObjPtr->f(); // Access violation
}
上述情况成功,但f() 的执行失败,MSVC 2013 出现“访问冲突”。
上面的函数有问题吗?如果是这样,有没有办法用一个函数来完成任务?
我已经阅读了一些材料,其中声称必须将 void* 仅投射到它所拥有的特定类(也在下面的评论中建议)。但是,这段代码编译并执行得很好:http://ideone.com/e0Lr6v
关于如何调用所有内容的更多上下文:
我无法在此处提供整个代码,因为它太长了,但总而言之.. 函数 executeF、对象构造函数 Ai 以及库中定义和操作对象 A、@987654336 的所有内容@ 作为仅在 void* 类型上运行的导出函数提供。仅供参考,这个库正在使用 MSVC 2013 编译和构建。
另一面(R 语言的包装器)是用 g++ 编译和构建的——它动态加载上述库,导出所需的函数并调用它。这方面唯一可用的是 void* 持有对象 Ai - 它只是发送创建对象的请求,调用它们的方法,释放它们。
例如(示意性地)创建一个 A1 类型的对象:
// "objects" library
void createA1(void** aObj)
{
*a1Obj = new A1();
}
// caller library
auto const createA1func = (int(__CC *)(void**)) GetProcAddress(getDLL(), "CreateA1");
void* a1Obj = NULL;
createAFunc(a1Obj);
// ... return the a1Obj to the external environemnt to keep it around
然后,有了a1Obj,用它做一些工作:
// caller library
auto const executeFfunc = (int(__CC *)(void*, int*)) GetProcAddress(getDLL(), "executeF");
int res(0);
executeFfunc(a1Obj, &res);
因此,如果我在双方上为Ai 的每种类型编写一个单独的函数,一切正常。但如果我能以某种方式在这里使用基类,那么样板代码就会大大减少。
【问题讨论】:
-
你可以在调用
executeF之前尝试强制转换,这样executeF总是得到B*而不是Ai* -
当从
void *转换时,只有当您转换回与转换来源相同的类型时,才能保证工作。不是它的基类或派生类。 -
请不要同时标记C和C++;两个标签都有用的情况很少见。在这种情况下,您使用的是 C++ 编译器,而 C 编译器会发出不属于您的问题的错误消息……因此您的代码是 C++,这就是您应该使用的标签。
-
如果将
A1*指针作为void*传递给DLL,则不能将executeF()中的void*直接转换为B*,必须将其转换为A1*.如果您将多个A1*、A2*、A3*等指针传递给DLL,请先将它们全部转换为B*,然后再将它们作为void*传递给DLL,然后您可以将void*转换为@ 987654361@直接到B*。 -
@OlegShirokikh 对象指针如何准确进入 DLL? DLL 如何确切地知道使用对象指针调用
executeF()?您遗漏了重要信息。 一些 代码正在创建对象并将它们转换为void*。 那个代码需要先将它们转换为B*,然后再将B*转换为void*。
标签: c++ inheritance casting void-pointers