释放由动态链接库创建的对象。

我的 DLL 是使用 LoadLibrary 加载的 - 它们仍然可以共享运行时吗?或者我实际上必须确保对象在分配它们的相同 DLL 中被删除吗?

在我的示例中,我有一个在 DLL 模块中实现的共同基类,然后由 Lua 对象镜像,Lua 确定了它的寿命。因此,当 Lua 垃圾收集器决定这样做时,我需要释放对象,我无法预测将从哪个 DLL 进行垃圾收集。

我正在考虑每个 DLL 都有以下函数的副本:

int ReleaseObject(lua_State* L)
{
    // object pointer is stored in field [0]
    lua_rawgeti(L, -1, 0);
    delete (Object*) lua_touserdata(L, -1);
    return 0;
}

然后将指向此函数的指针放置在元表的 __gc 字段中,并且该元表将用于在该 DLL 中定义的所有派生类的所有实例。这足以保证安全删除吗?

点赞
用户3019690
用户3019690

考虑在dll的客户端使用std::unique_ptrstd::shared_ptr(使用自定义删除器调用ReleaseObject),这些可以正确处理跨dll边界的删除。

另外,最好将dll接口完全设置为extern "C",这样可以使用不同的C++运行库编译dll,名称仍然不会混淆。 与从dll继承基类实现相比,更好的方法是导出一个函数对BaseImpl* CreateBaseImpl();void DeleteBaseImpl(BaseImpl*);,并让这些返回指向所需实现的指针。然后将该指针提供给您选择的智能指针。使BaseImpl的公共接口仅使用原始类型(句柄、数字、指针等,C数组-很好;stl容器-不好:创建了cpp运行时依赖)。

我猜它可能像这样:

// 使用LUA的代码-不关心实现的跨dll特性
...
{
    auto p = new ConcreteImpl(); // 将p传递给LUA,方式如常
    ...
    // 当LUA想要删除它时:
    delete p;
    p = nullptr;
    // 可能要告诉LUA它已经被删除
}
...

// BaseImpl.h - Dll接口头文件

class BaseImpl;

extern "C" BaseImpl * CreateBaseImpl();
extern "C" void DeleteBaseImpl(BaseImpl *p);

// 客户端代码

#include "BaseImpl.h"

#include <type_traits>
#include <memory>

#include <Windows.h>

class ConcreteImpl { // 可能是不需要继承的
{
    using namespace std;
    ...
    ConcreteImpl()
    :   m_ptrDll( ::LoadLibraryW(L"BaseImpl.dll"), &::FreeLibrary )
    ,   m_ptrBase( nullptr, [](BaseImpl *){} )
    {
        Assert( m_ptrDll, "LoadLibraryW() failed err=0x%x", ::GetLastError() );

        auto pfnCreate = reinterpret_cast<decltype(&CreateBaseImpl)>(
            ::GetProcAddress(m_ptrDll.get(), "CreateBaseImpl") );

        auto pfnDelete = reinterpret_cast<decltype(&DeleteBaseImpl)>(
            ::GetProcAddress(m_ptrDll.get(), "DeleteBaseImpl") );

        Assert( pfnCreate && pfnDelete,
                "GetProcAddress() failed err=0x%x", ::GetLastError() );

        // 使用移动构造函数分配新值
        m_ptrBase = decltype(m_ptrBase)( pfnCreate(), pfnDelete );

        Assert( m_ptrBase, "CreateBaseImpl返回nullptr" );
    }
    ...
    // 在此处使用m_ptrBase的方法
    ...
    unique_ptr<
        remove_pointer<HMODULE>::type,
        decltype(&::Freelibrary)
    > m_ptrDll;

    unique_ptr<BaseImpl, decltype(&DeleteBaseImpl)> m_ptrBase;
};
2013-11-28 03:17:16