延迟C++静态对象构建-Linux上的GCC
Deferring C++ static object construction - GCC on Linux
假设我有一个名为MyClass
的C++类。
想象一下,我无法访问MyClass
的源代码。。。它包含在库中,并且仅向我提供用于CCD_ 3的库和头文件。
想象一下,类本身需要环境预配置。。。例如在调用类的构造函数之前,我需要进行一些设置。该类通常用于以下用途:
void func() {
doGlobalSetup();
MyClass myInstance(1,2,3);
myInstance.doSomething();
...
}
现在我有这样的情况,我们需要创建类的全局实例,例如:
MyClass myInstance(1,2,3);
int main(int argc, char *argv[]) {
doGlobalSetup();
myInstance.doSomething();
}
问题是,在这个故事中,MyClass
的实例是在调用doGlobalSetup()
之前创建的。它在调用main()
之前被实例化。我想做的是将myInstance()
的创建推迟到稍后,或者能够在类实例化之前以某种方式运行doGlobalSetup()
。
这是对实际故事的简化。。。所以让我们假设:
- 我无法更改
MyClass
的内部结构 - 必须有一个
MyClass
类型的名为myInstance
的实例变量(我不能将逻辑更改为MyClass *pMyInstance
)
非常感谢您的阅读。
由于您已经将问题约束为无法使用new
,因此您应该能够一如既往地创建对象并将其复制到全局实例。例如:
MyClass createMyClass()
{
doGlobalSetup();
return MyClass(1, 2, 3);
}
MyClass myInstance = createMyClass();
int main()
{
myInstance.doSomething();
return 0;
}
它适合您的需求吗?
namespace
{
int doStaticGlobalSetup()
{
doGlobalSetup();
return 0;
}
}
MyClass myInstance(doStaticGlobalSetup() + 1,2,3);
int main() {
myInstance.doSomething();
return 0;
}
如果您必须将任何构造函数调用推迟到全局初始化完成之后,并且希望确保不会发生静态顺序初始化失败,那么有一种方法:使myInstance
引用未初始化的内存块,并在全局初始化后使用placement new在其中创建对象。
#include <iostream>
#include <type_traits>
struct foo
{
foo() { std::cout << "createdn"; }
void meow() { std::cout << "usedn"; }
~foo() { std::cout << "destroyedn"; }
};
void doGlobalSetup() { std::cout << "Global setupn"; }
//Actual implementation
namespace {
typename std::aligned_storage<sizeof(foo), alignof(foo)>::type bar;
}
foo& instance = reinterpret_cast<foo&>(bar);
//Allows automatic creation and destruction
struct initializer
{
initializer()
{
if (!initialized)
new (&instance) foo();
initialized = true;
}
~initializer()
{
if(initialized)
instance.~foo();
initialized = false;
}
private:
static bool initialized;
};
bool initializer::initialized = false;
int main()
{
doGlobalSetup();
initializer _;
instance.meow();
}
在函数中使用静态变量。
MyClass &myInstance() {
doGlobalSetup();
static MyClass myInstance(1,2,3);
return myInstance;
}
void func() {
myInstance().doSomething();
}
您可能已经得到了想要的答案。但为了覆盖整个范围:如果出于某种原因,您想确保代码中的其他地方不会意外地独立于全局变量构建MyClass,并且在进行全局设置之前,您需要通过链接来解决这个问题。
如果您在Linux上,您可以LD_PRELOAD一个共享对象,该对象只包含MyClass构造函数的符号。在它中,您相应地声明设置函数,并让动态链接器为您完成任务。然后,在构造函数内部,调用setup函数,然后执行dlsym("...", RTLD_NEXT)
以获得指向原始构造函数的指针,然后调用它,将获得的参数传递给它。当然,您需要维护并检查一个静态标志,以确定是否已经执行了设置。
同样,这对你来说可能有些过头了,但我发布它只是为了以防万一有人需要(并且能够使用)这种解决方案。
附言:这就是当你依赖全球状态时所得到的!:)
首先,请记住,给定像doGlobalSetup
这样的库初始化函数,如果创建全局实例,则库将不起作用的可能性明显为非零。
否则,使用逗号运算符创建初始值设定项是非常容易的:
bool do_my_setup = (doGlobalSetup(), true);
MyClass myInstance(1,2,3);
GCC编译器环境中有一个名为constructor
的函数属性功能。这使我们能够标记函数定义,使其能够在调用main之前自动调用,最重要的是在调用任何类构造函数之前自动调用。
回到最初的问题定义。。。如果doGlobalSetup()
函数修改自:
void doGlobalSetup() { ... }
至
__attribute__((constructor)) void doGlobalSetup() { ... }
那么它的调用将发生在main被调用之前,也将发生在任何静态类实例构造函数被调用之前。对该函数的显式调用也将从main()中删除,因为它的工作是隐式执行的。
- 在VS代码中交叉编译Windows与Linux上的MinGW的SDL程序
- C/C++ Linux 上的多线程服务器/客户端崩溃
- Linux 上的黑屏,但 Windows 上不是
- 从Raspberry Pi / Linux上的Python脚本运行和停止C++程序
- 使用eBPF拦截Linux上的所有传出/传入流量
- 如何在 Oracle Linux 上的 Eclipse 中编译和链接 boost 库
- C++ Linux 上的插件 ABI 问题
- 如何在 Linux 上的 C/C++ 中使用 ipv6 udp 套接字进行多播?
- Linux 上的 .NET Core - 元帅结构
- Linux上的PTHREAD_CANCEL()导致异常/Coredump,为什么
- 该命令在QProcess中启动,不会返回Linux上的响应
- 在RHEL 7上编译MySqlClient,并在RHEL 6上运行(Linux上的G )
- 跨平台套接字发送,Linux 上的缓冲区常量无效* Windows上的常量字符*,最佳处理方式
- Linux 上的键码表
- 如何检测 CDRom 是否是 Linux 上的 DVD
- Linux 上的 FreeImage 返回 -1 格式
- 在 Linux 上的 Visual Studio 中链接两个项目
- QT 5.8(Ubuntu 16.04)Linux上的应用程序部署
- 除了 Linux 上的源代码和编译 (GCC) 之外,有没有办法在 Windows 中托管 IDE
- 写入 Linux 上的共享内存时出现周期性延迟峰值