【问题标题】:Global Static Dynamically allocated objects C++全局静态动态分配对象 C++
【发布时间】:2012-05-29 15:41:19
【问题描述】:

我对动态分配的静态全局对象有一个非常具体的问题。在我的项目中,我几乎没有需要在整个应用程序生命周期中跨线程从不同位置访问的对象。我想在应用程序初始化时创建并在应用程序退出时销毁。所以我尝试了以下,

头文件:MyObjectFactory.h

class MyObjectFactory{
   public:
      static MyObject* GetMyObject();
};

源文件:MyObjectFactory.cpp

static MyObject* gMyObject = 0;

MyObject* MyObjectFactory::GetMyObject(){
    if(gMyObject == 0)
    {
        gMyObject = new MyObject();
    }
    return gMyObject;
}

这段代码似乎可以工作,但我想清除一些东西。

  1. 对象只会被创建一次,然后会返回对对象的引用。 (我想要这个,因为 MyObject 封装了一些像文本文件这样的系统资源)
  2. MyObject 在应用程序退出时被销毁。
  3. 对象会在哪里创建堆(当我使用 new 时)或全局内存(当我使用静态时)?还是我违反了任何 OOP 原则?
  4. 可以从多线程调用 MyObjectFactory::GetMyObject() 吗?
  5. 这是实现有点类似于 Singleton 的好方法吗?

请告诉我您的意见。

非常感谢!

【问题讨论】:

  • MyObject* 是指针,不是引用...
  • “静态”或“动态”,不能同时拥有。
  • 是否有特定要求使用new 创建对象?否则,只需将其设为全局 MyObject theObject;

标签: c++ static heap-memory global


【解决方案1】:

以最小的麻烦实现正确销毁和正确初始化的标准方法使用块局部静态,如下所示:

foo.hpp:

struct Foo
{
    static Foo & get();
    // ...
};

foo.cpp:

#include "foo.hpp"

Foo & Foo::get()
{
    static Foo impl;
    return impl;
}

现在您可以在代码中的任何位置说Foo::get()。没有指针,没有动态分配,也没有任何东西被泄露。一个真正的静态单例。

【讨论】:

  • 根据 OP 问题 #4,您可以添加与线程安全相关的内容以及 C++11 和 C++03 的不同之处。
  • @K-ballo:初始化是线程安全的,其他都是 OP 的问题。 (但这与单例无关。这只是一般编程。)
  • 复杂Foo的构造是线程安全的吗? C++03 中没有竞争条件吗?
  • 正因为在 C++03 中没有线程的概念,所以不能保证 static 对象的初始化是线程安全的。一个线程可以调用get(),然后另一个线程调用impl 的初始化仍在进行中。
  • @MikeSeymour: ...除非其他对象 also 在其自己的构造函数中调用 Foo::get() :-)
【解决方案2】:

对象只会被创建一次,然后会返回对对象的引用。 (我想要这个,因为 MyObject 封装了一些像文本文件这样的系统资源)

  • MyObject* 是指针类型,而不是引用类型。 gMyObject 是指向MyObject 的指针类型变量。

MyObject 在应用程序退出时被销毁。

  • 没有,没有人在你的指针上调用delete,所以你有泄漏。

对象会在哪里创建 Heap(当我使用 new 时)或全局内存(当我使用 static 时)?还是我违反了任何 OOP 原则?

  • 如果您使用new,则在'heap' 中创建对象。 static 仅适用于指向您的对象的指针,而不适用于对象本身。

可以从多线程调用 MyObjectFactory::GetMyObject() 吗?

  • 不是,如果您有并发线程并且对象尚未构造,则可能会导致多次初始化。

这是实现有点类似于 Singleton 的好方法吗?

  • 不是。或许是这样,但单例通常是实现某事的糟糕方式(我只说通常)。

【讨论】:

  • 更准确地说,MyObject* 是一种类型,但我现在很学究。
  • 我很惭愧继续我的迂腐,请原谅。但是gMyObject 是一个指针。我可以提议gMyObject is a variable of type Pointer to MyObject。我心情不好。不理我。或者gMyObject is a pointer variable.
【解决方案3】:

对象只会被创建一次,然后会返回对对象的引用。

如果您的程序是单线程的,是的(尽管在您的示例中它返回的是指针而不是引用)。否则存在两个线程创建对象的单独副本的危险;或者实际上发生了其他任何事情,因为在这种情况下行为是未定义的。

MyObject 在应用程序退出时被销毁。

不,用new 创建的对象只会被delete 销毁,永远不会自动销毁,所以这个对象被泄露了。这是否是一个问题取决于它使用的所有资源是否都被系统自动回收。

对象会在哪里创建 Heap(当我使用 new 时)或全局内存(当我使用 static 时)?

对象是从空闲存储区(又名堆)分配的;指向它的指针是静态的。

可以从多线程调用 MyObjectFactory::GetMyObject() 吗?

仅当您可以确保在他们中的任何一个调用它之前已经创建了对象。

这是实现有点类似于 Singleton 的好方法吗?

在 C++ 中没有很好的方法来实现这一点。最好的方法是完全避免全局可访问的对象;在某处创建它,并将引用传递给任何需要它的东西。

如果你真的想要一个全局对象,那么有几个选项,每个选项都有自己的死亡陷阱:

  • 一个简单的全局对象。如果您有不止一个,并且它们之间存在依赖关系,请注意初始化顺序惨败。
  • 一个包含静态对象的函数,返回对此的引用。这保证在您使用它时会被初始化,并且在符合 C++11 的实现上是线程安全的(在许多早期的实现上也是如此)。但是,在程序关闭期间,它可能会在其他仍在尝试访问它的静态对象之前被破坏 - 您可以通过动态分配和泄漏对象来避免这种情况,就像您的方法一样。确保线程安全的构造也可能会产生一些运行时开销。
  • 一个延迟分配的动态对象(就像您拥有的那样),只要您确保在启动多个线程之前对其进行了初始化,或者添加线程安全(这并不完全简单,并且会增加运行时开销)。

【讨论】:

    猜你喜欢
    • 2016-02-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-03-12
    • 2020-12-05
    • 1970-01-01
    • 1970-01-01
    • 2011-03-24
    相关资源
    最近更新 更多