【问题标题】:Memory managed C++ singletons内存管理的 C++ 单例
【发布时间】:2012-06-28 01:28:43
【问题描述】:

我现在正在编写游戏引擎。对我来说最难的部分是内存管理。我一直在进行尽可能多的优化(因为每一帧都很重要,对吗?)并意识到解决这个问题的最佳方法是使用堆栈和池分配器进行资源管理。问题是,我不知道如何创建一个由这些分配器管理内存的单例类。

我的单例是管理器,所以它们实际上相当大,它们的内存管理对于我的游戏的启动速度很重要。我基本上希望能够调用我的分配器的alloc() 函数,它返回类型void*,并在这个分配的空间中创建我的单例。

我一直在考虑在每个单例类中放置一个名为instantiatedstatic boolean,如果instantiated 为真,则让构造函数返回NULL。这行得通吗?

【问题讨论】:

  • 你需要多少个单身人士?一、五个不同的类,好几个类,也许你真的想要一个“multiton”
  • 您确定需要所有优化吗?即使是引擎,您仍然应该先进行分析。

标签: c++ memory memory-management


【解决方案1】:

这就是为什么每个人都认为单身人士很糟糕的原因。他们糟透了。这只是它们糟糕的一个方面,它会用它来搞垮你的整个应用程序。

为了解决您的问题:不要使用 Suckletons。

【讨论】:

  • 那么您会推荐什么作为合适的替代品?我希望每个经理只有一个实例。我应该让我的内核初始化它们并强制库的用户通过内核访问它们吗?
  • @steelclaw:如果你只想有一个......那么就只创建一个。
  • @steelclaw: 如果你不想让其他人直接实例化A类,让你的“内核”类成为A的friend class,并声明A的所有构造函数都是私有的。这样只有一个“ kernel" 对象将能够实例化 A 类。
  • @steelclaw:更准确地说,您已经将内核对象分解得超出了可能。您的“经理”基本上只是内核对象的各个部分。您应该将它们的所有接口放在一个对象中。如果结果太多,则认为你的内核做的太多了。
  • @everyone:我想从你所说的来看,我应该避免将单例放在一起,要么将它们与内核结合起来,要么阻止它们以具有多个的方式修改内核经理很危险。我认为我将采用的方法是将管理器作为普通对象分配在我的堆栈上,并将指向它们的指针传递给内核。谢谢大家。
【解决方案2】:

好的,我将通过分享我自己的经验来回答这个问题。通常我希望一个类拥有它自己的所有功能,然后意识到这可能需要一个工厂方法来为其他代码提供我的对象的一个​​实例,该实例是单数的。实例实际上应该只定义一次以维护状态等。因此,这就是我为没有将所有初始化包装到构造中的类所做的。我不想让我现有的类因单例而混乱,所以我创建了一个工厂包装类来帮助我解决这个问题。您可以使用 Myer 的单例模式,它很优雅,并通过让静态局部变量的实现(依赖于静态局部初始化的编译器实现,这并不总是一个好主意)来处理锁定,但如果您喜欢,问题就来了我,希望您的对象默认构造,以便您可以轻松地在它们上使用 STL 向量等,然后在它们上调用某种类型的初始化,可能将参数传递给此方法,以使它们更重。

class X
{
  public:


  bool isInitialized () { return _isInitialized; } ;  // or some method like 
                                                      // establishCxn to have it
                                                      // bootstrapped 
  void initialize();
  X() {} // constructor default, not initialized, light weight object

  private:
    bool _isInitialzied;

};

// set initialization to false 
X::X(): _isInitialized(false) {}

void X::intialize()
{
  if (isInitiazlied()) { return; }

   .... init code ....

  _isInitialized = true
}

// now we go ahead and put a factory wrapper on top of this to help manage the 
// singletoness nature.  This could be templatized but we don't as we want the 
// flexibility to override our own getinstance to call a specific initialize for the 
// object it is holding

class XFactory
{
public:

static X* getInstance();

private:
static boost::mutex _lock;

// light weight static object of yourself ( early instantiation ) to put on the stack 
// to avoid thread issues, static method member thread saftey, and DCLP pattern 
// threading races that can stem from compiling re-ordering of items

static XFactory _instance; 

// making this pointer volatile so that the compiler knows not to reorder our
// instructions for it ( depends on compiler implementation but it is a safe guard )
X* volatile _Xitem;  
X* getXitem () { return _Xitem; }

void createInstance();
void doCleanUp();

// stop instantiation or unwanted copies
XClientFactory();
~XClientFactory();
XClientFactory(XClientFactory const&);
XClientFactory& operator=(XClientFactory const&);

};

// on construction we create and set the pointer to a new light weight version of the 
// object we are interested in construction
XFactory::XFactory() : _Xitem( new X; )
{
}

// note our factory method is returning a pointer to X not itself (XFactory)
X* XFactory::getInstance()
{
// DCLP pattern, first thread might have initialized X while others
// were waiting for the lock in this way your double check locking is 
// on initializing the X container in the factory and not the factory object itself.  
// Worst case your initialization could happen more than once but it should check the 
// boolean before it starts (see initialization method above) and sets the boolean at 
// the end of it. Also your init should be such that a re-initialization will not put 
// the object in a bad state.  The most important thing to note is that we
// moved the double check locking to the contained object's initialization method
// instead of the factory object

  if (! XFactory::_instance.getXitem()->isInitialized() )
  {
    boost::unique_lock<boost::mutex> guard(XFactory::_lock);
    if (! XFactory::_instance.getXitem()->isInitialized() )
    {
      XFactory::_instance.getXitem()->initialize();
    }
  }
  return XFactory::_instance.getXitem();
}


// XFactory private static instance on the stack will get cleaned up and we need to 
// call the delete on the pointer we created for the _Xitem

XFactory::~XFactory()
{
  doCleanUp();
}
XFactory::doCleanUp()
{
  delete _Xitem; // 
}

就是这样,让我知道你的想法。我最近还与单身人士摔跤,所有的坑都掉了。此外,我们还没有使用 0x 编译器,这将确保 Myers 单例将以线程安全的方式实现,仅使用局部静态变量

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-01-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-09-06
    • 2018-12-17
    相关资源
    最近更新 更多