C++模板和标头分配
我最近遇到了在一个 DLL(或 *.so - 可移植代码)中进行内存分配和在另一个 DLL 中进行释放的问题。到目前为止我遇到的错误是:
- 它不起作用 - 在调试时断言()失败。
- 如果一个 DLL 与标准 C 库静态链接,而另一个 DLL 与其动态链接,则该方法不起作用。
- 如果一个 DLL 进行分配,然后该 DLL 被卸载并且另一个 DLL 尝试释放该内存,则它不起作用。
基本上,我决定应该遵循的规则是不要在一个 DLL 中进行分配并在另一个 DLL 中释放它(最好将其保留在一个 cpp 文件中)。这通常也意味着我不应该在可能由多个 DLL 共享的头文件中进行分配。这意味着我不应该在模板中进行分配(因为它们都在标头中),这是一个很大的限制。
当我确实需要在模板中创建新对象时,我现在要做的就是为其分配内存(一个 cpp 文件),然后才使用放置 new 运算符运行其 c'tor。
// header
class MyBase
{
public:
static void* allocate(std::size_t i_size);
};
template <typename T>
class MyClass: MyBase
{
public:
T* createT();
};
temlpate <typename T>
T* MyClass<T>::createT()
{
void* pMem = MyBase::allocate( sizeof(T) );
return new (pMem) T;
}
// Cpp file
void* MyBase::allocate(std::size_t i_size)
{
return malloc( i_size );
}
虽然这有效,但有点难看。这意味着编写模板代码时不使用new。
另一个含义是,如果您不知道模板是使用此技术编写的,您应该只在头文件(包括其他模板)中使用它的 const 方法(这是假设 const 方法不分配或释放内存)。这包括STL。事实上,我遇到这种情况的地方之一是在一个向量中,该向量由一个动态库(在 HP-UX 上)调整大小,然后卸载,而不是由另一个动态库调用其 d'tor。
是否有一些广为人知的解决方案,我只是错过了,或者这只是一个被忽视的问题?
I recently encountered problems with memory allocations made in one DLL (or *.so - portable code) and deallocation done in another DLL. The errors I encountered so far are:
- It just doesn't work - fails an assert() on debug.
- It doesn't work if one DLL was statically linked with the standard C library and the other DLL dynamically linked with it.
- It doesn't work if one DLL does an allocation then the DLL is unloaded and another DLL tries to deallocate this memory.
Basically the rule I decided I should follow is not to make allocations in one DLL and release it in another (and preferably keep it within one cpp file). This usually also means I shouldn't do allocations in a header file that may be shared by more than one DLLs. This means I shouldn't do allocations in tempaltes (since they are all in header) and this is quite a big limitation.
When I do need to create a new object in a template what I do now is allocate the memory for it a cpp file and only then run its c'tor with placement new operator.
// header
class MyBase
{
public:
static void* allocate(std::size_t i_size);
};
template <typename T>
class MyClass: MyBase
{
public:
T* createT();
};
temlpate <typename T>
T* MyClass<T>::createT()
{
void* pMem = MyBase::allocate( sizeof(T) );
return new (pMem) T;
}
// Cpp file
void* MyBase::allocate(std::size_t i_size)
{
return malloc( i_size );
}
While this works, this is a bit ugly. It means writing template code without using new.
Another implication is that if you don't know that a template was written using this technique you should only use const methods of it in a header file (including other templates) (this is assuming const methods don't allocate or deallocate memory). This includes STL. In fact, one of the places I encountered this was in a vector that was resized by one dynamic library (on HP-UX) then unloaded than its d'tor was called by another dynamic library.
Is there some widely known solution for this that I'm just missing or is it just an overlooked problem?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
不,一个不暗示另一个。
如果您的分配和取消分配函数是标头中的模板,那也没关系;只需确保将任何给定对象对这些函数的使用限制为一个 TU1。
封装您的对象,以便 DLL 1 中的代码对 DLL 2 中的对象调用这些函数是无效/禁止/未定义的。与用户签订合同,在注释中写下对象的所有权仍保留在原始分配中上下文,然后继续到项目的下一部分,而不必再担心这个问题。
所有 TU 都可以使用这些功能并不相关;毕竟,您总是可以尝试
删除
这些东西!1 - 翻译单元。大致相当于一个预处理的
.cpp
文件。No, the one does not imply the other.
If your allocation and de-allocation functions are templates in a header, that's still fine; just ensure that you restrict use of these functions for any given object to one TU1.
Encapsulate your objects such that it would be invalid/prohibited/undefined for code in a DLL 1 to call these functions on objects from DLL 2. Make it a contract to the user, write it in comments that ownership of objects remains with the original allocating context, then move on to the next part of your project without ever having to worry about this again.
That the functions are available to all TUs is not relevant; after all, you can always attempt
delete
on these things!1 - Translation Unit. Roughly equivalent to one pre-processed
.cpp
file.您必须将两个 DLL 动态链接到同一个动态 CRT 库。直到所有引用它的 DLL 都被卸载后,CRT 才会被卸载,并且当使用相同的 CRT DLL 时,在一个 DLL 中分配内存并在另一个 DLL 中释放内存是安全的。
You must dynamically link both DLLs to the same dynamic CRT library. The CRT will not be unloaded until all DLLs that reference it have been unloaded, and when the same CRT DLL is used, then it is safe to allocate memory in one and free it in another.