2014-09-14 2 views
4

Я хочу создать класс менеджера, который будет управлять всеми созданными объектами одного типа. Естественно, этот класс должен также создавать эти объекты. Поэтому клиенту не разрешается создавать объекты самостоятельно, но вместо этого он должен всегда использовать класс менеджера для его выполнения. Кроме того, клиенту разрешено определять его собственные классы, которые будут управляться одним Менеджером.Создание объектов только в классе Менеджера

template<class Type> 
class Manager{ 
    //... 
    Type* createInstance(){ 
    Type* ptr = new Type(); 
    //do sommething 
    return ptr; 
    } 
}; 

Проблема: как ограничить создание экземпляров только классом менеджера?

Одним из возможных вариантов были бы объявить конструктор как частные и класс Manager, как друг класс:

class A{ 
    friend class Manager<A>; 
private: 
    A(){} 
    ~A(){} 
}; 

Поскольку клиент могут определить собственные классы он мог бы просто объявить конструктор общественность и менеджер будет все еще работают без проблем. Но клиент сможет создавать экземпляры этих классов без участия класса-менеджера, что приводит к неправильному управлению/управлению ресурсами.

Есть ли способ преодолеть эту проблему?

+0

Если пользователю разрешено определять свои собственные классы, которыми может управлять ваш, нет способа запретить ему создавать экземпляры своих классов. Вы просто делаете это неправильно. Кроме того, почему бы не использовать интеллектуальные указатели? – Nelfeal

+0

Typo/Синтаксис: не должно быть 'friend class Manager ;'? – Christophe

ответ

2

Менеджеры такого рода, как правило, плохая картина. Следующий пример кода показывает, почему.

Это та же проблема, что и все деструкторы должны быть виртуальными. Я использую 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 по своей сути не являются потокобезопасными.

+0

Ну, я должен был предоставить больше информации, особенно о деструкторе. В вашем случае вы совершенно правы! Но фактический менеджер не удаляет необработанный указатель. Он сохраняет все экземпляры и предоставляет ключ для них. Метод удаления не принимает необработанный указатель, а вместо этого ключ или сам объект с вызовом по ссылке. Затем он проверяет, сохраняет ли он этот объект, а затем удаляет его. Таким образом, ваша проблема не возникнет. – Stan

+0

Вы абсолютно уверены в этом. Помните, что если вы создаете что-то через Менеджер (B); и попробуйте удалить его с помощью Менеджера (A), как это все равно произойдет с актом от B * до A *, тогда у менеджера (A) не будет соответствующего ключа, и система не сработает. Кроме того, менеджер (B) никогда не будет просить уничтожить объект, и поэтому вы пропустите. – dgnuff

+0

По построению объекты A и B получат уникальные идентификаторы, которые они возвращают с помощью виртуальной функции getID(). Поэтому менеджер может проверить, отвечает ли он за удаление этого объекта. Если он не тот, он может просто вернуть какое-то значение ошибки, как простой bool. В это время клиент будет отвечать за правильное удаление через менеджера. – Stan

Смежные вопросы