许多C++程序员遭受了与全局C++对象初始化/清理的激烈冲突。最后,我找到了一个很好的解决这个问题的方法,我已经用了(而且很高兴)很多年了。我的问题是:这个解决方案完全符合C++标准,还是“平台/实现依赖的”?
问题
一般来说,全局对象有两个主要问题:
,
/ try/catch包装这段代码,也无法执行任何初步的catch
克服这些问题的一种方法是,而不是使用全局对象的。相反,可以使用静态/全局指针到这些对象。在程序初始化期间,这些对象被动态分配或实例化为入口点函数(main)中的自动变量,它们的指针存储在这些指针中。这样,您就可以完全控制“全局”对象的生存期。
然而,这种方法也有一些缺点。这与这样一个事实有关:不仅这些对象的创建/销毁是不同的,而且它们的access也不同。通常,全局对象驻留在由加载程序分配的数据部分中,并且在构建时它的虚拟地址是已知的。使用全局指针会导致以下缺点:
)。
内存不足的
如果在堆栈上分配了实际的对象( main):
。
解决方案
我找到的解决方案是重写对象的new/delete操作程序。
// class definition
class MyObject
{
// some members
// ...
static char s_pMyPlaceholder[];
public:
// methods
// ...
static MyObject& Instance()
{
return *(MyObject*) s_pMyPlaceholder;
}
void* operator new (size_t) { return s_pMyPlaceholder; }
void operator delete (void*) {}
};
// object placeholder instantiated
char MyObject::s_pMyPlaceholder[sizeof(MyObject)];
void main()
{
// global initialization
std::auto_ptr<MyObject> pMyObj(new MyObject);
// run the program
// ...
}诀窍是在全局内存中分配足够的空间(通过声明一个具有足够大小的全局数组),然后对所需的对象使用虚拟内存分配,这将“分配”这个全局内存。
MyObject::Instance()来获取对它的引用。而且,BTW,此函数调用很容易由编译器内联。。
所以这个方法看起来一切都很好。我只是好奇,从C++标准的角度来看,这是否合法。
发布于 2011-08-04 11:13:25
我认为您没有正式的保证您的解决方案在每个兼容的实现上都能工作,因为C++标准并不保证静态分配的char数组与任何大小相同的对象所需要的对齐。
发布于 2011-08-04 11:12:22
我看到了两个问题,一个是合法性问题,另一个是可用性问题。
第一个问题是对齐:不能保证MyObject::s_pMyPlaceholder能够适当地对齐以容纳MyObject。
第二个问题是,您将自己限制在MyObject类型的单个对象上。创建第二个,您已经覆盖了第一个没有警告。
我建议使用boost::optional来延迟对象的初始化。
发布于 2011-08-04 11:37:39
3.7.3.1 (分配职能,basic.stc.dynamic.allocation) (ISO/IEC 14882/2003):
2/ .返回的指针应适当地对齐,以便将其转换为任何完整对象类型的指针,然后用于访问所分配存储中的对象或数组(直到通过调用相应的去分配函数显式地释放存储)。
我的怀疑是,您不能保证s_MyPlaceHolder[0]的地址正确地对齐。
我没有看到(在单个线程环境中)有什么不好的地方:
#include <cstdlib>
class MyObject
{
static MyObject* instance;
static void release_instance() { delete instance; }
public:
static MyObject& get_instance()
{
if (!instance)
{
instance = new MyObject();
std::atexit(&release_instance);
}
return *instance;
}
};除了单子和全局通常是一个糟糕的想法(他们倾向于将您的代码与这些对象的存在结合在一起,这会加强代码各部分之间的耦合)。
由于您对控制对象的生存期感兴趣,所以可以使用RAII:
class MyObject
{
MyObject() { ... }
~MyObject() { ... }
// Never defined
MyObject(const MyObject&);
void operator=(const MyObject&);
static MyObject* instance = 0;
static void create_instance()
{
if (instance) throw std::logic_error("Instance already created");
else instance = new MyObject();
}
static void release_instance() { delete instance; }
public:
struct InstanceKeeper
{
InstanceKeeper(InstanceKeeper& x) : must_clean(x.must_clean)
{ x.must_clean = false; }
~InstanceKeeper() { if (must_clean) release_instance(); }
private:
friend class MyObject;
InstanceKeeper() : must_clean(true) { create_instance(); }
bool must_clean;
};
friend struct InstanceKeeper;
static InstanceKeeper instance_keeper() { return InstanceKeeper(); }
static MyObject& instance()
{
if (!instance) throw std::logic_error("Instance not created");
return *instance;
}
};用法:
int main()
{
MyObject::InstanceKeeper k = MyObject::instance_keeper();
MyObject::instance().do_something();
...
}您甚至可以将InstanceKeeper对象传递给函数,它具有与std::auto_ptr相同的行为。
您可能存在的任何性能问题都是过早优化的情况。
https://stackoverflow.com/questions/6939989
复制相似问题