0

有人发布了关于这种模式的问题,但没有在我脑海中提出问题的解决方案,所以发布了我的查询......

在上面的示例中,如果所有实例都由 s_prototypes 变量初始化,并且下一次如果其中的任何对象将被克隆方法中的新对象替换,那么现有对象会发生什么?它会造成内存泄漏吗?

据我从上面的例子中了解到,两个陈述让我感到困惑

class Stooge {
public:
   virtual Stooge* clone() = 0;
   virtual void slap_stick() = 0;
};

class Factory {
public:
   static Stooge* make_stooge( int choice );
private:
   static Stooge* s_prototypes[4];
};

int main() {
   vector roles;
   int             choice;

   while (true) {
      cout << "Larry(1) Moe(2) Curly(3) Go(0): ";
      cin >> choice;
      if (choice == 0)
         break;
      roles.push_back(
         Factory::make_stooge( choice ) );
   }

   for (int i=0; i < roles.size(); ++i)
      roles[i]->slap_stick();
   for (int i=0; i < roles.size(); ++i)
      delete roles[i];
}

class Larry : public Stooge {
public:
   Stooge*   clone() { return new Larry; }
   void slap_stick() {
      cout << "Larry: poke eyes\n"; }
};
class Moe : public Stooge {
public:
   Stooge*   clone() { return new Moe; }
   void slap_stick() {
      cout << "Moe: slap head\n"; }
};
class Curly : public Stooge {
public:
   Stooge*   clone() { return new Curly; }
   void slap_stick() {
      cout << "Curly: suffer abuse\n"; }
};

Stooge* Factory::s_prototypes[] = {
   0, new Larry, new Moe, new Curly
};
Stooge* Factory::make_stooge( int choice ) {
   return s_prototypes[choice]->clone();
}

Output
Larry(1) Moe(2) Curly(3) Go(0): 2
Larry(1) Moe(2) Curly(3) Go(0): 1
Larry(1) Moe(2) Curly(3) Go(0): 3
Larry(1) Moe(2) Curly(3) Go(0): 0
Moe: slap head
Larry: poke eyes
Curly: suffer abuse

但是当我们通过 make_stooge 方法调用 clone 方法时,它会返回新对象,如果返回新对象并将其替换为现有对象,则现有对象将在此处创建内存泄漏(因为 new 运算符完全创建单独的对象,而不是在此处返回现有对象) ……

所以这件事让我对这个例子感到困惑......

4

2 回答 2

1

您的代码中没有内存泄漏,因为新创建的对象被小心地存储在vector(here roles) 中。

然后从该向量中使用它们,并在程序结束之前销毁它们:

   ...
   for (int i=0; i < roles.size(); ++i)
      delete roles[i];
}

但是return 0;在退出 main 之前会更好......

为要派生的类创建一个虚拟析构函数是一种很好的做法:如果任何一个Larry等具有非平凡的析构函数,当您通过基类指针删除它们时将不会使用它:

class Stooge {
public:
   virtual Stooge* clone() = 0;
   virtual void slap_stick() = 0;
   virtual ~Stooge() {}
};

对于 4 个静态对象s_prototypes,它们在程序启动时在 main 被调用之前被静态初始化,并且永远不会被修改。他们的clone方法被调用来创建新对象,但s_prototype仍会指向旧对象——这就是为什么存储和删除新创建的对象很重要的原因。

这 4 个对象确实存在内存泄漏,因为它们永远不会被明确销毁。通常认为以这种方式泄漏静态对象是可以接受的,因为它们的生命周期会一直延伸到程序结束,即所有内存都被释放。如果调用析构函数很重要,您还应该为它们创建静态实例:

static Larry _larry;
static Moe _moe;
static Curly _curly;
Stooge* Factory::s_prototypes[] = {
   0, &_larry, &_moe, &_curly
};

如果您通过以下方式明确销毁:

   virtual ~Stooge() {
       cout << "Delete " << this << endl;
   }

并稍微改变程序的结尾:

   for (int i=0; i < roles.size(); ++i)
      delete roles[i];
   cout << "END" << endl;
   return 0;
}

输出将如下所示:

...
Delete 001ADB78
END
Delete 00F350A0
Delete 00F3509C
Delete 00F35098

清楚地表明静态对象在程序结束后被销毁。

于 2017-01-06T07:03:38.953 回答
1

好吧,你是对的,其中的指针s_prototypes永远不会被删除,但至少这些对象无论如何都打算在程序的整个运行时持续存在,所以除非需要在它们的析构函数中执行特定的操作,否则它不是如果它们在程序终止之前没有被删除,那么世界末日。这远没有可能持续泄漏内存的代码那么糟糕,这将导致程序使用的内存不断增加。

如果您愿意,可以将它们替换为静态实例,如下所示:

#include <iostream>
#include <vector>
using namespace std;

class Stooge {
public:
   virtual Stooge* clone() = 0;
   virtual void slap_stick() = 0;
   virtual ~Stooge() = default;
};

class Larry : public Stooge {
public:
   Stooge*   clone() { return new Larry; }
   void slap_stick() {
      cout << "Larry: poke eyes\n"; }
};
class Moe : public Stooge {
public:
   Stooge*   clone() { return new Moe; }
   void slap_stick() {
      cout << "Moe: slap head\n"; }
};
class Curly : public Stooge {
public:
   Stooge*   clone() { return new Curly; }
   void slap_stick() {
      cout << "Curly: suffer abuse\n"; }
};

class Factory {
public:
   static Stooge* make_stooge( int choice );
private:
   static Larry larry;
   static Curly curly;
   static Moe moe;
};

int main() {
   vector<Stooge*> roles;
   int             choice;

   while (true) {
      cout << "Larry(1) Moe(2) Curly(3) Go(0): ";
      cin >> choice;
      if (choice == 0)
         break;
      roles.push_back(
         Factory::make_stooge( choice ) );
   }

   for (int i=0; i < roles.size(); ++i)
      roles[i]->slap_stick();
   for (int i=0; i < roles.size(); ++i)
      delete roles[i];
}

Stooge* Factory::make_stooge( int choice ) {
   switch(choice) {
   case 1:
      return larry.clone();
   case 2:
      return curly.clone();
   case 3:
      return moe.clone();
   default:
      return nullptr;
   }
}
于 2017-01-06T07:13:20.963 回答