путаница с примером объяснения шаблона Prototype в переполнении стека

Кто-то опубликовал вопросы об этом шаблоне, но у меня не было решения вопроса, поднятого у меня в голове, поэтому я отвечал на вопросы, которые у меня есть …

В приведенном выше примере, если все экземпляры инициализируются переменной 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

Но когда мы вызываем метод clone методом make_stooge, он возвращает новый объект. Если он возвращает новый объект и заменяет его существующим, то существующий объект создаст здесь утечку памяти (поскольку новый оператор полностью создает отдельный объект и не возвращает здесь существующий) ….

так что эта вещь путает меня с этим примером ….

0

Решение

Ну, вы правы, что указатели в 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;
}
}
1

Другие решения

В вашем коде нет утечки памяти, потому что вновь созданные объекты тщательно хранятся в vector (Вот roles).

Затем они используются из этого вектора и уничтожаются до завершения программы:

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

Но return 0; перед выходом из основного было бы лучше …

Хорошей практикой является создание виртуального деструктора для классов, предназначенных для получения: если 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

ясно показывает, что статические объекты уничтожаются после завершения программы.

1

По вопросам рекламы [email protected]