首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >全局C++对象初始化

全局C++对象初始化
EN

Stack Overflow用户
提问于 2011-08-04 10:42:34
回答 3查看 7.6K关注 0票数 2

许多C++程序员遭受了与全局C++对象初始化/清理的激烈冲突。最后,我找到了一个很好的解决这个问题的方法,我已经用了(而且很高兴)很多年了。我的问题是:这个解决方案完全符合C++标准,还是“平台/实现依赖的”?

问题

一般来说,全局对象有两个主要问题:

  • 不可预测的建造/破坏顺序。如果这些对象相互依赖,这就会被咬一口。

  • ,构造/销毁代码是在主程序入口点外部的CRT初始化/清理过程中执行的。无法用initialization.

/ try/catch包装这段代码,也无法执行任何初步的catch

克服这些问题的一种方法是,而不是使用全局对象的。相反,可以使用静态/全局指针到这些对象。在程序初始化期间,这些对象被动态分配或实例化为入口点函数(main)中的自动变量,它们的指针存储在这些指针中。这样,您就可以完全控制“全局”对象的生存期。

然而,这种方法也有一些缺点。这与这样一个事实有关:不仅这些对象的创建/销毁是不同的,而且它们的access也不同。通常,全局对象驻留在由加载程序分配的数据部分中,并且在构建时它的虚拟地址是已知的。使用全局指针会导致以下缺点:

  • 对象访问速度有点慢,额外的指针取消引用。在运行时,编译器将生成取消全局pointer.
  • Weaker优化的代码,而不是假设对象位于指定的地址。编译器可能没有意识到指针总是指向同一个对象。如果在heap:
    • Worse性能上分配了实际对象,则为
    • (堆分配为"heavy")

)。

    • Memory fragmentation

内存不足的

    • Chance of exception

如果在堆栈上分配了实际的对象( main):

  • Stack大小的自动变量通常是有限的),则为
    • 。在某些情况下,“胖”对象的消耗是suboptimal.

解决方案

我找到的解决方案是重写对象的new/delete操作程序。

代码语言:javascript
复制
// 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
    // ...

}

诀窍是在全局内存中分配足够的空间(通过声明一个具有足够大小的全局数组),然后对所需的对象使用虚拟内存分配,这将“分配”这个全局内存。

  • Semantically我们动态地分配对象。因此,我们可以完全控制它的lifetime.
  • Actually,对象驻留在全局内存中。因此,与“指针式”方法相关的所有缺点都不适用于我们的情况。
  • 对象在程序中随处可见。一个调用MyObject::Instance()来获取对它的引用。而且,BTW,此函数调用很容易由编译器内联。

所以这个方法看起来一切都很好。我只是好奇,从C++标准的角度来看,这是否合法。

EN

回答 3

Stack Overflow用户

回答已采纳

发布于 2011-08-04 11:13:25

我认为您没有正式的保证您的解决方案在每个兼容的实现上都能工作,因为C++标准并不保证静态分配的char数组与任何大小相同的对象所需要的对齐。

票数 2
EN

Stack Overflow用户

发布于 2011-08-04 11:12:22

我看到了两个问题,一个是合法性问题,另一个是可用性问题。

第一个问题是对齐:不能保证MyObject::s_pMyPlaceholder能够适当地对齐以容纳MyObject

第二个问题是,您将自己限制在MyObject类型的单个对象上。创建第二个,您已经覆盖了第一个没有警告。

我建议使用boost::optional来延迟对象的初始化。

票数 3
EN

Stack Overflow用户

发布于 2011-08-04 11:37:39

3.7.3.1 (分配职能,basic.stc.dynamic.allocation) (ISO/IEC 14882/2003):

2/ .返回的指针应适当地对齐,以便将其转换为任何完整对象类型的指针,然后用于访问所分配存储中的对象或数组(直到通过调用相应的去分配函数显式地释放存储)。

我的怀疑是,您不能保证s_MyPlaceHolder[0]的地址正确地对齐。

我没有看到(在单个线程环境中)有什么不好的地方:

代码语言:javascript
复制
#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:

代码语言:javascript
复制
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;
    }
};

用法:

代码语言:javascript
复制
int main()
{ 
    MyObject::InstanceKeeper k = MyObject::instance_keeper();

    MyObject::instance().do_something();
    ...

}

您甚至可以将InstanceKeeper对象传递给函数,它具有与std::auto_ptr相同的行为。

您可能存在的任何性能问题都是过早优化的情况。

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/6939989

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档