C ++:在分离的共享库中实现类方法

时间:2012-01-10 16:22:58

标签: c++ class implementation dlopen

我知道我可以在共享库中实现类的一部分,只要在使用时加载符号。

myclass.h
---

class C {
void method();
}

main.cpp
---

#include "myclass.h"
int main() {
    //dynamically load mylib.so using dlopen/dlsym/dlclose
        ...
    C *c = new C();
    c->method();
    delete c;
}

mylib.so compiled separately:
====
mylib.cpp
---

#include "mylib.h"
void C::method() {
...
}

这很好用。

然而,一旦我完成了使用C :: method(),我想卸载它,所以我可以更改,重新编译并重新加载它而无需重新启动主程序

int main() {
    //dynamically load mylib.so using dlopen/dlsym/dlclose
        ...
    C *c = new C();
    c->method();
    delete c;
    // close the lib, remove the handle...
         ....
    char pause;
    cin << pause; // before carrying on change the C::method, recompile it

    //dynamically load *Again* mylib.so using dlopen/dlsym/dlclose
        ...
    C *c = new C();
    c->method();
    delete c; 
}

问题是它在执行第一个dlclose时没有卸载库,可能是因为我的类C的实例存在。 有什么方法可以强迫这个吗?

(在Linux Ubuntu 10.04上使用g ++ 4.2.3)

2 个答案:

答案 0 :(得分:10)

简答

它不会像你这样做。你的方法可能还有其他问题,但还没有让你感到困惑。

为什么它不起作用

程序/库中的未定义符号会在不同时间解析。在大多数系统上,加载程序/库时始终会解析数据引用(全局变量,类vtable等)。首次在某些系统上使用代码引用时会解析(“延迟查找”;至少在Linux和Mac OS X上发生),除非设置了一些特殊选项(dlopen或LD_BIND_NOW环境变量的RTLD_NOW参数)。一旦解决了这些问题,就不会进行新的查找。

如果在完成方法的延迟查找之前dlopen具有RTLD_GLOBAL标志的库,则将使用库中的方法。现在解决了该方法的代码引用;它不会再改变。您的主程序现在正式使用dlopen ed库中的符号,因此dlclose将不再关闭库 - dlclose只会删除您的显式句柄。

简而言之,您应该只希望卸载只能通过显式调用dlsym来使用的库。

该怎么做

可以做的是让您的库提供派生类实现。您将类C定义为抽象基类:

class C
{
public:
    virtual void method();
};

在单独编译的库中,您将定义派生类和创建该派生类的对象的函数:

class D : public C
{
public:
    virtual void method();
};

void D::method()
{
    // ...
}

extern "C" C* createC()
{
    return new D();
}

现在,在您的主程序中,您将使用dlopen加载库,使用createD获取指向dlsym的函数指针并调用它来获取对象。当所有对象都消失后,你可以调用dlclose,重新编译你的库,然后再做一遍:

typedef C* (*creatorFunction)();

int main()
{
    for(;;)
    {
        void *handle = dlopen("mylib.so", 0);
        creatorFunction create = (creatorFunction) dlsym(handle, "createC");

        C *c = (*create)();
        c->method();
        delete c;

        dlclose(handle);

        char pause;
        cin << pause;
    }
    return 0;
}

答案 1 :(得分:-1)

感谢帮助人员,只是通知:

别忘了在C类中将方法初始化为零(此类不在共享库中),并将虚拟销毁器设置为默认值

如果您想知道dlopen的标志是0->它是RTLD_LOCAL