Менеджеры такого рода, как правило, плохая картина. Следующий пример кода показывает, почему.
Это та же проблема, что и все деструкторы должны быть виртуальными. Я использую Manager для создания B, который происходит от A, назначает объект в указатель A *. Если он будет уничтожен, как таковой, он проходит через диспетчер :: уничтожить(), который, вероятно, не закончится хорошо, так как он был создан с помощью диспетчера :: Марка()
#include <iostream>
using namespace std;
template<class Type>
class Manager
{
public:
Manager(char *type)
{
mytype = type;
}
Type *make()
{
Type *ptr = new Type();
cout << "in Manager<" << mytype << ">::make() ptr->mytype is " << ptr->mytype << endl;
return ptr;
}
void destroy(Type *ptr)
{
cout << "in Manager<" << mytype << ">::destroy() ptr->mytype is " << ptr->mytype << endl;
delete ptr;
}
private:
char *mytype;
};
class A
{
friend class Manager<A>;
protected:
A()
{
mytype = "A";
cout << "in A()" << endl;
}
virtual ~A()
{
cout << "in ~A() mytype is " << mytype << endl;
}
char *mytype;
};
class B : public A
{
friend class Manager<B>;
protected:
B()
{
mytype = "B";
cout << "in B()" << endl;
}
virtual ~B()
{
cout << "in ~B() mytype is " << mytype << endl;
}
};
int main()
{
Manager<A> ma("A");
Manager<B> mb("B");
B *b = mb.make();
A *a = b;
ma.destroy(a); // This line calls Manager<A>::destroy. It should call Manager<B>::destroy.
return 0;
}
, который производит следующее в качестве вывода:
in A()
in B()
in Manager<B>::make() ptr->mytype is B
in Manager<A>::destroy() ptr->mytype is B -- Oops - wrong destroy.
in ~B() mytype is B
in ~A() mytype is B
Это, в свою очередь, означает, что вы не можете использовать наследование для его полной возможности, что на самом деле портит цель использования языка OO.
В зависимости от того, почему, по вашему мнению, вам нужен менеджер, возможно, лучшее решение.
Если это для управления памятью (маловероятно, учитывая исходный фрагмент кода), стоит обратить внимание на отмену новых и удаление.
Если он предназначен для отслеживания всех экземпляров для целей обработки (например, объекты в игре, которые обновляются каждый гаджет игры), тогда, когда это немного умственная передача, лучшим решением является интеграция менеджера в сам класс , как набор статических функций-членов и переменных. Менеджеры такого рода почти неизменно одиночные, поэтому статические члены/функции ставят вас в одно и то же место семантически.
Таким образом, вы могли бы:
static set<A *> s_collection;
static void Heartbeat() // in class A
{
// lock s_collection here
for (auto it = s_collection.begin, end = s_collection.end() it != end; ++it)
{
// process *it
}
// unlock s_collection here
}
, а затем во время A :: A() вставить это в s_collection, а также во время A :: ~ A() вы стереть это.
Просто будьте осторожны, чтобы использовать подходящие примитивы синхронизации, если вы многопоточны, поскольку большинство контейнеров stl по своей сути не являются потокобезопасными.
Если пользователю разрешено определять свои собственные классы, которыми может управлять ваш, нет способа запретить ему создавать экземпляры своих классов. Вы просто делаете это неправильно. Кроме того, почему бы не использовать интеллектуальные указатели? – Nelfeal
Typo/Синтаксис: не должно быть 'friend class Manager ;'? – Christophe