作为一个更大项目的一部分,我有一个非常具体的问题。该项目由一个可执行文件和不同的 dll 组成,其中一些在运行时动态加载/卸载。我可以完全访问所有子项目,但类的基本结构需要如下所示:
其中一个库提供了一个模板类,它继承自一个非模板类,如下所示:
//lets call this object.dll
//its of course splitted into cpp/h files
class Object{};
template<typename T>
class TemplateObject : public Object{};
//whether i inherit here or just use a member does not change the problem...
class SharedObject : public std::shared_ptr<Object>{};
template<typename T>
SharedObject create(T& o){
return SharedObject{new TemplateObject<T>{o}};
};
构造函数被正确地实现以使这些事情起作用。它背后的基本思想是这样的:
-
TemplatedObject
是一个管理对某种类型 T 的访问的类,在我的例子中,这是一个函数指针。 -
Object
是公共(public)基类,为我提供(有限)访问TemplatedObject
的功能. - 两个
TemplatedObject
和Object
不应该被复制、移动或以任何其他方式被弄乱,因此对这个 dll 的用户来说是隐藏的。即使情况并非如此,它们的复制/移动分配/构造函数也可能会被删除或私有(private)化。 - 相反,
SharedObject
可能会传递给其他 dll 和可执行文件,仅提供所需的访问权限。 - 要创建这些对象,有一个(模板化的)工厂函数
create
.此函数从不同的模块(dll 和可执行文件)调用。
这工作正常,只要最后一个 shared_ptr 始终在与创建对象的第一个 shared_ptr 相同的模型中销毁。如果没有,我会遇到访问冲突。我确信我的问题是由这样一个事实引起的,即内存在一个模块中分配并在另一个模块中释放(参见其他问题)。 object.dll 不是动态加载/卸载的,所以如果所有创建的对象都在 object.dll 的堆内存中,我完全没问题。
我尝试做的是:我像这样为 Object
重载了运算符 new/delete |和 TemplateObject
static void* operator new(std::size_t sz)
{
return allocate(sz);
}
static void* operator new[](std::size_t sz)
{
return allocate(sz);
}
static void operator delete(void* ptr, std::size_t sz)
{
deallocate(ptr, sz);
}
static void operator delete[](void* ptr, std::size_t sz)
{
deallocate(ptr,sz);
}
并在object.dll中定义了两个全局(非模板)函数
void* allocate(std::size_t sz)
{
return ::operator new(sz);
}
void deallocate(void* ptr, std::size_t sz)
{
::operator delete(ptr);
}
因为我在 windows 下工作,所以我用 declspec(dllimport/export) 标记了这两个函数
我可以通过调试输出验证 TemplateObject 和全局函数的运算符是否实际被调用,但是当我将 shared_ptr 保存在与原始模块不同的模块中时,我仍然会遇到访问冲突错误。
这让我得出结论,我要么
- 忘记实现某些特定版本的删除(或可能是新的)
或
- 需要实现我自己的分配器以与 shared_ptr 一起使用,它至少调用 object.dll 中的分配函数,但我不知道如何执行此操作。我可以从 std::allocator 派生吗?然后覆盖一个特殊函数(我猜是 allocate()/deallocate())?还是我需要实现一个全新的?
我想如果我的 create()
会起作用不必进行模板化,但在这种情况下,我很高兴能就如何解决问题或在何处找到有关自定义分配器的良好介绍提供任何帮助。
编辑
我逐步完成了在错误模块内重置共享指针的过程,发现在 <memory>
内的这一行中抛出访问冲突:
void _Decref()
{ // decrement use count
if (_MT_DECR(_Mtx, _Uses) == 0)
{ // destroy managed resource, decrement weak reference count
--> _Destroy();
_Decwref();
}
}
这看起来我的自定义删除运算符从未被调用过。 我还尝试使用 Deleter 类作为 SergeyA 在评论中提出的建议 - 相同的错误,相同的行。
最佳答案
我可能弄错了,但看起来您正在使用私有(private) msvcrt (/MT) 构建 dll 和 exe,而您应该使用共享 (/MD)。 msvcrt 的每个实例都有自己的堆、FILE 对象以及标准库管理的任何其他内容,因此使用另一个实例进行删除就像在野指针上调用 free。
关于c++ - 如何为通过共享指针在不同模块之间传递的模板类分配内存,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/35092103/