c++ - 在共享库中隐藏派生类的符号
问题描述
我将编写一个共享库,我 在 Internet 上找到了有关设置符号可见性的注释。一般指导是隐藏库的客户端不需要的所有内容,从而减少库的大小和加载时间。除非涉及到使用类层次结构,否则我很清楚。
但是,让我们从头开始。
假设:
- GCC 工具链用于 Linux 操作系统
- 库和客户端应用程序使用相同的工具链构建
- 该库将用于使用相同工具链构建的 Linux 发行版
-fvisibility=hidden
和-fvisibility-inlines-hidden
compile 选项用于构建库
我准备了一些案例来检查编译器切换到库的影响。
情况1
客户端代码:
#include "Foo.hpp"
int main()
{
auto s = make();
delete s;
}
库头:
struct Foo{};
Foo* make() __attribute__((visibility("default")));
图书馆来源:
#include "Foo.hpp"
Foo *make() { return new Foo; }
在这种情况下,一切编译和链接都没有错误,甚至只make
导出函数。这对我来说很清楚。
案例2
我添加了Foo
析构函数定义。
客户端代码与案例 2 相同。
库头:
struct Foo {
~Foo();
};
Foo* make() __attribute__((visibility("default")));
图书馆来源:
#include "Foo.hpp"
Foo::~Foo() = default;
Foo *make() { return new Foo; }
在这种情况下,在将客户端应用程序与库链接期间,链接器抱怨未定义对 的引用Foo::~Foo()
,这对我来说也很清楚。未导出析构函数符号,客户端应用程序需要它。
案例3
客户端应用程序和库源与案例 2 相同。但是,在库头中我导出 Foo 类:
struct __attribute__((visibility("default"))) Foo {
~Foo();
};
Foo* make() __attribute__((visibility("default")));
这里没有惊喜。由于库导出了客户端应用程序所需的所有符号,因此代码编译、链接和运行不会出错。
但是(案例 4)
当我第一次尝试编写这个库时,我没有导出类,而是将析构函数设为虚拟:
struct Foo {
virtual ~Foo();
};
Foo* make() __attribute__((visibility("default")));
令人惊讶的是……编译、链接和运行的代码没有任何错误。
走得更远(案例5)
最初我的库定义了一个类层次结构,Foo
其余的基类在哪里,并定义了纯虚拟接口:
struct __attribute__((visibility("default"))) Foo {
virtual ~Foo();
virtual void foo() = 0;
};
Foo* make() __attribute__((visibility("default")));
该make
函数产生派生类的实例,返回一个指向基类的指针:
#include "Foo.hpp"
#include <cstdio>
Foo::~Foo() = default;
struct Bar: public Foo
{
void foo() override { puts("Bar::foo"); }
};
Foo* make() { return new Bar; }
应用程序使用接口:
#include "Foo.hpp"
int main()
{
auto s = make();
s->foo();
delete s;
}
一切都编译,链接,运行没有错误。应用程序打印Bar::foo
,这表明Bar::foo
调用了覆盖,甚至Bar
根本没有导出类。
问题
- (案例 4)为什么链接器没有向析构函数抱怨缺少符号?这是一种未定义的行为吗?我不会那样做,只是想明白。
- (案例5)是否可以只为基类和工厂函数导出符号,工厂返回隐藏符号的派生类的实例?
解决方案
为什么链接器没有向析构函数抱怨缺少符号?
这是因为客户端代码从存储在函数中创建的对象中的 vtable 加载析构函数的地址make
。~Foo
链接器在链接客户端代码时不需要知道显式地址。
只为基类和工厂函数导出符号是否可以,工厂返回隐藏符号的派生类的实例?
只要您的客户端代码只在这些派生类中调用重载的虚拟方法,就可以了。原因与虚拟相同~Foo
- 地址将从对象的 vtable 中获取。
推荐阅读
- r - 删除特定变量的 5% 百分位数
- node.js - Node.js 和 Gulp 帮助
- python - 从 tkinter 按钮获取随机文本变量 - python
- scala - Scala中的嵌套字符串插值
- javascript - AngularJS范围变量设置不正确
- amazon-web-services - 代码部署时创建虚拟主机
- php - Bootstrap 模式中的 WordPress 自定义联系表单未显示验证和提交响应
- php - 从 html 页面调用 phar 中包含的函数
- mongodb - 将旧的 Mongo 数据库引用转换为 DBRefs
- java - 是否可以在编译时以外的运行时进行验证检查?