从 DLL 中删除指针
Deleting pointer from DLL
ALL,
我有以下问题:
class Base
{
public:
Base();
virtual ~Base();
};
class Derived1 : public Base
{
public:
Derived1();
virtual Derived1();
};
class Derived2 : public Base
{
public:
Derived2();
virtual Derived2();
};
DLL 中定义的基类,它是静态链接的,每个子类都在其自己的 DLL 中定义,该 DLL 按需加载。
class Foo
{
public:
~Foo();
void CreateObject();
void SetPointer(Base *base) { m_p = base; };
private:
void *m_p;
}
Foo::~Foo()
{
delete m_p;
m_p = NULL;
}
extern "C" void CreateObject()
{
Base *base = NULL;
Foo *foo = new Foo();
foo->CreateObject( base );
foo->SetBase( base );
delete foo;
}
void Foo::CreateObject(Base *m_p)
{
if( <cond1> )
m_p = new Derived1();
if( <cond2> )
m_p = new Derived2();
}
运行此代码,我遇到内存泄漏。通过调试器运行,我看到从未调用 Derived1 类的析构函数。
我该如何解决它?析构函数是虚拟的,应该调用。唯一的问题是内存分配发生在 DLL 内部,但析构函数在主应用程序内部调用。
是否可以修复内存泄漏,或者我将不得不重新考虑设计?
谢谢。
,您正在尝试实现一个插件系统,因此这个答案将尝试勾勒出您如何构建这样的东西以及想到的一些陷阱。
C++和 DLL 有一些与之相关的有趣挑战。 例如,必须格外小心:
- 在 DLL 之间传递的对象的分配和解除分配("新建"和"删除")
- 在 DLL 之间引发和捕获异常
- 跨 DLL 边界传递 STL 对象
- 函数重载
我在这里经验最多的方法是仔细定义从 DLL 导出的函数,然后标记 extern "C",避免重载它们,并且永远不会从这些函数中抛出异常。
尽管 MSVC 确实支持可导出类,但我建议避免使用它们,因为您可能会很快遇到上面列出的问题区域。
无论如何,您可以相对安全地依赖的一件事是在 DLL 之间共享接口类。 接口类是仅包含纯虚拟方法的类。 所以例如:
class Plugin
{
public:
virtual void DoFoo() = 0;
virtual void DoBar() = 0;
};
我们将插件的声明放在一个头文件中,该文件可以包含在应用程序以及插件 DLL 的实现中。
请注意,尚未导出任何内容。 我们只打算导出 C 样式的函数。 按照惯例,我们会说我们的插件DLL必须提供"CreatePlugin"函数。 下面是一个示例:
class FirstPlugin : public Plugin
{
public:
virtual void DoFoo() { std::cout << "FirstPlugin says FOO!n"; }
virtual void DoBar() { std::cout << "FirstPlugin says BAR!n"; }
};
extern "C"
{
__declspec(dllexport) Plugin* CreatePlugin()
{
return new FirstPlugin();
}
}
加载 dll 的应用程序可以执行此操作:
typedef Plugin* (*CreatePluginFn)();
HMODULE module = LoadLibrary("first.dll");
CreatePluginFn createPlugin = (CreatePluginFn)GetProcAddress(module, "CreatePlugin");
Plugin* plugin = createPlugin();
plugin->DoFoo();
plugin->DoBar();
我省略了必要的免费图书馆调用。 更有趣的是我们如何处理释放我们创建的插件。 应用程序不一定知道 CreatePlugin 如何分配插件实例,因此应用程序"删除插件"并不安全。 相反,我们需要告诉插件 DLL 本身我们已经完成了它。
最明显的方法是在插件中添加一个"销毁"方法:
class Plugin
{
public:
virtual void Destroy() = 0;
virtual void DoFoo() = 0;
virtual void DoBar() = 0;
};
可能的实现方式是:
class FirstPlugin : public Plugin
{
public:
virtual void Destroy() { delete this; }
virtual void DoFoo() { std::cout << "FirstPlugin says FOO!n"; }
virtual void DoBar() { std::cout << "FirstPlugin says BAR!n"; }
};
所以现在调用者做:
plugin->Destroy();
plugin = NULL; // we mustn't use plugin after we're destroyed it!
我认为这涵盖了基础知识。 事实证明,当您构建大量这样的代码时,存在常见的模式,例如:
- 使用引用计数而不是大锤子销毁方法
- 界面发现(比如问插件"你也可以做Foo吗?
- 在DLL中支持许多多个"创建插件"样式的函数
这些(例如COM)的现有解决方案可能很有趣。
在一个 DLL 中分配并在另一个 DLL 中解除分配是可以工作的事情,但很棘手。 但是,我认为即使所有代码都在同一个 DLL 中,您也会看到此问题:
在类 Foo 中,指向 Base 的指针存储在 void* 中。 这会导致编译器"忘记"m_p是 Base* 实例,因此它只是释放内存而不调用析构函数。
将m_p作为基础*应该可以解决此问题。
- 使用函数引用指向节点的指针删除链表中的节点?
- 如何通过指向元组的共享指针删除对象
- 函数内的 C++ 指针删除
- 一个对象的两个指针.删除了一个指针,对象仍然存在
- 如何通过存储在 std::list 中的指针删除对象?
- C++中的智能指针删除
- 将C 中的每个指针删除作为阵列的指针安全吗?
- 使用 C 字符串和指针.删除除小写和空格以外的任何字符
- 在特定情况下,指针删除和铸造之间的关系不清楚
- 提升属性树:使用指向节点及其父节点的指针删除节点
- 警告C4150在尝试包装本机C 类时,指向不完整类型的指针删除
- C++ Valgrind 双指针删除,用于防止内存泄漏
- C 通过指向其基类的指针删除派生对象
- 将指针删除到数组
- 当通过其中一个指针删除对象时,C++将所有指针设置为null
- 智能指针删除器和"using"名称为"pointer"关键字
- 通过指向其基的指针删除 POD 对象是否安全
- 模板化的出列无效指针:删除类时失败
- 使用"this"指针删除
- (C++) list.error 方法和使用指针删除内存