加载外部 class c++

Loading external class c++

我正在尝试加载在 .dll 文件中定义的 class。但是,在 dll 中定义 class 有两种稍微不同的方法。我不确定哪种方法更合法,我也不知道为什么第二种方法也有效。这是一个简单的例子:

方法一:main.cpp:

#include <iostream>
#include <windows.h>
#include <memory>
#include "bar.h"

using namespace std;

typedef bar* (* MYDLLPROC)();

int main()
{
    unique_ptr<bar> theresult;
    auto thelib = LoadLibrary(TEXT("foo.dll"));

    if (thelib != NULL) {
        MYDLLPROC theprocs = (MYDLLPROC)GetProcAddress(thelib, "Myfoo");
        cout << "load successfully" << endl;
        theresult.reset(theprocs());
        theresult->printmsg();
    } else {
        cout << "cannot load the dll" << endl;
    }

    return 1;
}

barbar.h中定义为纯虚class:

class bar {
public:
    virtual ~bar() {};
    virtual void printmsg() = 0;
};

foo.dll源文件中:

#include <iostream>
#include <windows.h>
#include "bar.h"

using namespace std;

class foo: public bar {
public:
    foo() { cout << "foo is instantiated" << endl; }
    ~foo() {}
    void printmsg() final { cout << "msg from foo print" << endl; }
};

extern "C" __declspec(dllexport) foo* __cdecl Myfoo()
{
    return new foo();
}

在第一种方法中,纯虚拟 class bar 被用作接口,并且它的成员函数被 foo 中的成员函数覆盖是有意义的dll 已加载。

然而,我发现 foo 不必从 bar 派生,只要 foo 有一个 Vtable,一切仍然有效:

第二种方法,除了foo的定义外,其他都是一样的:

#include <iostream>
#include <windows.h>

using namespace std;

class foo {
public:
    foo() { cout << "foo is instantiated" << endl; }
    virtual ~foo() {}
    virtual void printmsg() final { cout << "msg from foo print" << endl; }
};

extern "C" __declspec(dllexport) foo* __cdecl Myfoo()
{
    return new foo();
}

谁能告诉我为什么第二种方法有效?我有点困惑,因为 foobar 没有关系,但是 bar 中的成员函数仍然可以被覆盖。

第一个示例非常脆弱,因为它隐含地依赖于 foobar 指针可互换。

第二个示例已损坏,因为函数 Myfoo returns 指向 class foo 的指针与 bar 无关,取消引用它会导致未定义的行为.

函数签名必须与 dll 中实现的函数匹配。您现在拥有的基本上是从 foo *bar *reinterpret_cast

因此,您将返回 foo* 的函数转换为返回 bar* 的函数,然后调用它。

最终结果是您有一个指向 foo 的指针,它是一个指向不相关类型 bar 的指针。以任何方式使用它都会导致 undefined behavior.

它似乎在这种特定情况下有效,因为 printmsg 虚函数在两个 vtable 中的位置相同,因此在 foo 的实例上调用 bar::printmsg 只需调用“vtable 中的第 N 个条目”。如果您在 printmsg 之前将另一个虚拟成员添加到 foo,那么它可能会被调用(或者程序可能会崩溃)。