我正在将一个类移植到C++,并且需要在创建类的第一个实例之前执行一些初始化代码;在main()
获得控制之前执行代码很适合我。如何在C++中执行?
初始答案
您可以在namespace
作用域中使用对象的构造函数。
namespace {
struct Init
{
Init()
{
// Initialization code here.
}
} init_;
} // namespace
注意,这有一些限制,尤其是在Windows上。在Windows上,ctor是在保持加载程序锁的情况下调用的,因此您不能执行任何需要加载DLL之类的操作。这包括初始化WinSock,因为它可以尝试加载外部DLL。
更新
根据一些消息来源,您可以使用QueueUserAPC
来绕过这个限制。这种技术也有局限性,尽管有所不同。我使用过它,我的实验表明,只有当您将Visual Studio及其C库用作DLL时,即MSVCRT.DLL、MSVCR100.DLL等(/MD
或/MDd
开关)
更新2
这里有一个链接到类似的问题(实际上是我的),其中有一个重要的部分:
经过一些测试,如果我从DllMain()对APC进行排队,APC方法似乎可以工作,但如果我从类的静态全局实例的ctor对APC进行队列,则它不工作。
struct Init
{
Init()
{
/* Your Initialization Code */
}
} x;
int main()
{
}
全局类和静态类是在main()开始执行之前构造的。
class hello {
hello () { std::cout << "hello" << std::endl; }
};
hello hi;
int main(){
std::cout << "hello again" << std::endl;
return 0;
}
输出将始终是
hello
hello again
因为hello对象实例是在main启动之前创建的,因为hi是类hello 的全局实例
我的答案解决了您真正的问题——在类的第一个实例之前执行一次性初始化,而不是在main之前执行。
只需使用一个静态变量来确保一次性init代码只执行一次。如果您需要线程安全,请使用同步,尽管它会影响性能。
class MyClass {
MyClass() {
// Perform the one-time initialization.
static bool passed = false;
if (!passed) {
performOneTimeInitialization();
passed = true;
}
// Continue with normal construction.
}
};
实际问题的附加解决方案(使用工厂方法):
namespace
{
struct Initializer
{
Initializer()
{
/* initializing code goes here */
}
}
}
MyClass CreateMyClass()
{
static Initializer init;
return new MyClass();
}
初始化代码在您第一次调用CreateMyClass时执行。使用C++11编译器,它甚至应该是线程安全的。如果您没有,并且需要线程安全性,可以检查boost::call_once。
您实际上在这里问了两个不同的问题,其中一个描述了您想要解决的问题:
在创建类的第一个实例之前,我[…]需要执行一些初始化代码;
第二个是关于你认为如何解决这个问题:
在main()获得控制之前执行代码很适合我。如何在C++中做到这一点?
对于第二个问题,您已经多次得到一个答案(全局变量)。这可能是一个可行的解决方案,尽管我更喜欢一个私有静态类成员来限制全局变量的可见性:
class X {
const static bool initialized;
};
//X.cpp:
namespace {
bool preInitialization() {
//your pre-main code here
return true;
}
}
bool X::initialized = preInitialization();
对于第一个问题,您可以将初始化代码的执行延迟到类的第一个变量初始化时:
class X {
X();
};
//X.cpp:
namespace {
bool preInitialization() {
//your pre-main code here
return true;
}
}
X::X() {
static bool b = preInitialization();
}
这样,您的代码在X的第一个构造函数调用期间执行。这可能有一些缺点,例如,如果X的成员只能在执行初始化代码后初始化,或者在存在多个构造函数的情况下初始化。您可以通过将该方法推送到一个空基类中来改进它:
class PreInit {
static bool preInitialization() {
//your pre-main code here
return true;
}
public:
PreInit() {
static bool b =preInitialization();
}
};
class X : private PreInit {
//normal class implementation
};
这样,您甚至可以在第一次构造多个类的任何对象之前执行代码,只需从PreInit类派生所有有问题的类即可。按照SRP,将预初始化考虑到自己的类中只是一个更好的设计。