2

假设我有一个这样定义的抽象基类:

接口.hpp

#ifndef INTERFACE_HPP
#define INTERFACE_HPP 1

class interface{
    public:
        virtual void func() = 0;
};

#endif // INTERFACE_HPP

然后我将一个翻译单元编译test.cpp成一个共享对象test.so

测试.cpp

#include "interface.hpp"
#include <iostream>

class test_interface: public interface{
    public:
        void func(){std::cout << "test_interface::func() called\n";}
};

extern "C"
interface &get_interface(){
    static test_interface test;
    return test;
}

如果我在可执行文件中打开该共享对象并尝试get_interface像这样调用:

#include <dlfcn.h>
#include "interface.hpp"

int main(){
    void *handle = dlopen("test.so", RTLD_LAZY);
    void *func = dlsym(handle, "get_interface");

    interface &i = reinterpret_cast<interface &(*)()>(func)();
    i.func(); // print "test_interface::func() called"

    dlclose(handle);
}

(假装我做了错误检查)

行为是否明确定义?还是我假设这将永远有效,从而踩到自己的脚趾?

请记住,我只会使用 clang 和 gcc

4

1 回答 1

1

一个问题是你想protected: ~interface()阻止客户删除interface.

第二个实际问题是,如果您修改interface,请记住仅在类末尾添加方法,并且不要添加新的虚拟覆盖(具有相同名称的函数)。(实际上,我已经看到覆盖聚集在一起,即使它们没有聚集在头文件中)。

如果您想要的不仅仅是一个接口(例如,您的接口继承自其他 2 个接口),请使用virtual继承。virtual根据我的经验,事后添加新父母也被证明是有问题的。

这些都不是由 C++ 标准定义的,它与二进制接口和代码运行时加载的主题无关。但是,以上是我使用类似技术的经验(诚然,使用指针而不是引用,并使用 MSVC 代替 gcc/clang)。

您确实必须跟踪您使用的编译器上的 ABI 是什么。如果您通过std这样的接口传递结构,请注意它们有时会更改布局(std::string例如,在 gcc 中,从引用计数变为不计数,或者std::list获得 O(1) size),并且它们之间的布局不太可能兼容编译器(好吧,标准库,不同的编译器默认使用不同的编译器)。

于 2015-07-14T15:48:33.190 回答