【问题标题】:Static factory methods and static objects memory leaks静态工厂方法和静态对象内存泄漏
【发布时间】:2012-06-23 16:06:05
【问题描述】:

我有一个带有静态工厂构造函数的类,它返回一个指向所创建对象的指针。

我必须将对象声明为命名空间内的静态对象,但我不知道如何正确删除它

class Foo
{
   public:
   Foo(int, int* );
   virtual ~Foo();
   static Foo* MyFooInitializer(int n )
   {
      int *p = new int[n];
      for (int i=0; i<n; i++)
         p[i]=i;

      Foo *ret = new Foo(n,p);
      delete p;
      return ret;
   }
   int someFooFunction(int a);
}

然后在我的命名空间中我有一个static inline 函数

namespace MyNamespace
{

    static inline void  myfunction()
    {
        static Foo  *foo1 = Foo::MyFooInitializer(10); 
        int y = somevalue();
        int x = foo1->someFooFunction(int y);
    } 
}

我显然这里有内存泄漏,因为该对象从未被删除。

重要的事实是我需要将 foo1 声明为静态,因为一旦创建,它在所有程序中必须是同一个对象并且必须是唯一的(它跟踪一些变量)。

可能这是一个设计问题,但我不知道在我的程序退出时如何删除它,或者当我明确要删除它以重新初始化它时。

解决方案:

我这样修改了MyFooInitializer的正文:

   static Foo* MyFooInitializer(int n )
   {
      int *p = new int[n];
      for (int i=0; i<n; i++)
         p[i]=i;

      static Foo ret = Foo(n,p);
      delete[] p;
      return &ret;
   }

这允许我在程序终止时正确释放所有内存。 Valgrind 说所有的堆内存都被释放了!

【问题讨论】:

  • 你没有在任何地方使用过new(),为什么你认为你有内存泄漏?
  • @Als 我敢打赌MyFooInitializer 有一个新的。
  • @LuchianGrigore:它很可能是一个本地的 static 实例,它在整个程序生命周期中始终保持活动状态。如果不是,只需将 new 替换为一个,然后就不会泄漏:)
  • 函数中的“g”是什么?为什么要初始化“p”然后删除它?

标签: c++ memory-leaks new-operator static-methods delete-operator


【解决方案1】:

这里不需要在堆上分配那个 Foo:

static Foo* MyFooInitializer(int x) {
    static Foo some_foo(x);
    return &some_foo;
}

该代码没有泄漏,当您的程序结束时 Foo 将被销毁。

请注意,如果MyFooInitializer 返回的指针实际上指向某个继承自Foo 的类,那么您只需对静态变量使用派生类型:

static Foo* MyFooInitializer(int x) {
    static SomeFooDerived some_foo(x);
    return &some_foo;
}

编辑:既然你提供了实际的函数体,我的回答是有效的。你会这样做:

static Foo* MyFooInitializer(int n ) {
   // Don't know what this p is, anyway...
   int *p = new int[n];
   for (int i=0; i<n; i++)
      p[i]=i;

   static Foo ret(n,g); // what is g?
   delete[] p; // smart pointer plx
   return &ret;
}

【讨论】:

  • 于是我们来到了单例模式。
  • +1 For “没有必要在堆上分配那个Foo”。这是非常正确的。
  • 是的,它基本上是一个单例。无需在每次调用 MyFooInitializer 时都在堆上进行分配,因为您已经知道该对象的生命周期将持续到应用程序结束。
  • 提防静态去初始化惨败!如果静态对象 A 在销毁过程中访问静态对象 B,请确保 B 在 A 之前构造。
  • 我们实际上不知道他是否可以通过单例做到这一点。也许直到运行时才知道从类工厂返回的参数甚至类型。
【解决方案2】:

怎么样

static inline void  myfunction()
{
    static std::unique_ptr<Foo> foo1(Foo::MyFooInitializer(10));
    int y = somevalue();
    int x = foo1->someFooFunction(int y);
} 

【讨论】:

  • 我没有找到 unique_ptr(我正在编写符合 C++ O3 的)。我正在用 g++-4.4 编写我的代码,它必须在 g++ >= 4.0 和 windows 上编译,似乎 unique_ptr 不在标准头文件 中。我在哪里可以找到它? (我不能使用提升)。
【解决方案3】:

如果您绝对需要动态创建 foo1,则编写一个额外的类并按值将其设为静态/全局。然后使用它的析构函数来删除对象。

class MasterControlClass
{
    public:
    Foo* foo1;
    MasterControl(){foo1 = NULL;}
    ~MasterControl(){delete(foo1), foo1 = NULL;}
};



static inline void myfunction()
{
     static MasterControlClass mcp;
     mcp.foo1 = Foo::MyFooInitializer(10); 
}

这样,当您的程序关闭时,mcp 析构函数将被调用并进行清理。 如果要重新初始化,则必须在每次分配时删除 foo1,只需添加

if(mcp.foo1)
{
    delete mcp.foo1;
    mcp.foo1= NULL;
}

或者更好的是,将其移至 mcp 的方法中。

【讨论】:

    猜你喜欢
    • 2013-02-10
    • 1970-01-01
    • 2012-08-08
    • 2013-11-06
    • 2011-02-26
    • 1970-01-01
    • 2011-10-08
    • 2011-12-01
    相关资源
    最近更新 更多