问题描述
|
我试图使DLL文件与不同的编译器配置(Debug,Release ..)兼容。为了确保以正确的方式删除对象,每当我获取DLL对象并超出范围时,我设法编写一个使用编译的Delete运算符的指针包装器类。
我对此非常满意,但是当我尝试删除以相同方法/程序分配的内存时,程序崩溃。
以下是在标准发布模式下编译的一些示例代码:
标头
template <typename T>
class API mwCompatibleObject
{
public:
//! Constructor
mwCompatibleObject();
//! Destructor
virtual ~mwCompatibleObject();
};
源代码
template < typename T >
mwCompatibleObject< T >::mwCompatibleObject() {}
template <typename T>
mwCompatibleObject<T>::~mwCompatibleObject() {}
注意:API定义为导出/导入。
现在,我在调试模式应用程序中使用此类,在其中创建实例并立即将其删除。
mwCompatibleObject<double> * obj = new mwCompatibleObject<double>();
delete obj;
执行delete运算符会在mlock.c第376行给出访问冲突。
这是调用堆栈的副本:
ntdll.dll!7721e3be()
[Frames below may be incorrect and/or missing,no symbols loaded for ntdll.dll]
msvcr80d.dll!_unlock(int locknum=4) Line 376 C
msvcr80d.dll!_heap_alloc_dbg(unsigned int nSize=0,int nBlockUse=2968120,const char * szFileName=0x2e2ed26c,int nLine=1638180) Line 477 + 0x7 bytes C++
msvcr80d.dll!_heap_alloc_dbg(unsigned int nSize=0,int nLine=1638180) Line 474 + 0xc bytes C++
00300000()
msvcr80d.dll!malloc(unsigned int nSize=2968120) Line 154 + 0x15 bytes C++
5axutil.dll!100b5d09()
Integrator3.exe!main() Line 54 + 0x34 bytes C++
我无法跳入该行或任何内容,但设法查看了汇编代码,这证明了我的观察到它与析构函数有关。
尝试使DLL兼容时,虚拟函数/析构函数是否存在一般问题?
解决方法
当编译器根据模板的使用情况生成类型时,您将无法导出模板定义。您只需将它们内联到头文件中,或者可以执行类似的操作,但是这需要事先声明模板实例化。
另请注意,在C ++中,更喜欢C中的
new
和delete
而不是malloc
和free
函数,特别是如果您实际上希望调用构造函数和析构函数的话。
编辑:
我将认真考虑将模板内联比任何导出尝试都更好。同样,我最初也没有注意到仅当您的类将是基类或包含虚拟方法时才需要虚拟析构函数。为什么其中没有任何vtable?
template <typename T>
class mwCompatibleObject // no need for export if inlined in header
{
public:
//! Constructor
mwCompatibleObject() {}
//! Destructor (don\'t need virtual unless it\'s a base class or has virtual methods)
~mwCompatibleObject() {}
//! Some public method
void DoSomething(const T& withSomething)
{
// ... yata yata
}
private:
T m_member;
};
进一步编辑:
我刚刚发现,最终定稿后,导出模板支持将从新的C ++标准中完全删除(不建议弃用,删除)。头文件中的内联模板将是编译器将在不久的将来实现并允许的唯一解决方案,因此请立即习惯以这种方式编写它们。请参阅Herb Sutter \关于C ++ 0x的问答