C ++ (своего рода) фабрика

0

Я видел несколько сообщений о фабриках C++, но до сих пор я не видел решения, которые решают мою проблему. (Хотя, возможно, я что-то пропустил.)

Пример консольного приложения:

#include <memory>
#include <map>
#include <iostream>

using namespace std;

class ResourceManager;

/// abstract base class
class Identity
{
public:
    int Id() const { return _id; }

    /// make this an abstract class
    virtual ~Identity() = 0 {}

protected:
    Identity() { _id = _nextId++; }

private:
    int _id;
    static int _nextId;
};

int Identity::_nextId = int();

/// derived classes
class Component : public Identity
{
    friend class ResourceManager;
public:
    ~Component() { }
};

class Entity : public Identity
{
    friend class ResourceManager;
public:
    ~Entity() { }
};


class ResourceManager
{
public:
    template<typename T>
    T& Create()
    {
        auto ptr = std::make_shared<T>();
        auto id = ptr->Id();

        _resources[id] = std::move(ptr);
        return *dynamic_pointer_cast<T>(_resources[id]);
    }

private:
    std::map<int, std::shared_ptr<Identity>> _resources;
};



int main(int argc, char *argv[])
{
    cout << "Factory test" << endl;

    ResourceManager r;

    auto& e = r.Create<Entity>();
    cout << "e.id = " << e.Id() << endl;

    Entity e2;
    cout << "e2.id = " << e2.Id() << endl;

    Component c;
    cout << "c.id = " << c.Id() << endl;

    std::getchar();
}

Мне нужно убедиться, что только ResourceManager может создавать экземпляр Entity, Component и любые классы, которые производятся от них.

Я посмотрел и добавил ResourceManager в качестве класса друзей в Identity, и сделал конструкторы частными или защищенными, без каких-либо успехов. (Это может быть тупик или просто проблема с реализацией на моем конце.)

Какие-либо предложения?

Обновление и редактирование

Замененный код с компилируемым примером. Хотя конструктор для Identity() защищен, я все же могу напрямую создавать производные классы.

  • 1
    Что именно означает «нет успеха»? ошибка компиляции? ошибка выполнения? носовые демоны?
  • 0
    Дружба не передается по наследству.
Показать ещё 4 комментария
Теги:
c++11
factory
visual-studio-2012

2 ответа

1
Лучший ответ

Следующее должно работать:

friend class ResourceManager; должно быть в каждом дифференцированном классе. (друг не наследуется).

class ResourceManager;

/// abstract base class
class Identity
{
public:
    int Id() const { return _id; }

    /// make this an abstract class
    virtual ~Identity() = 0;

    // Forbid any copy
    Identity(const Identity&) = delete;
    Identity(const Identity&&) = delete;
    Identity& operator = (const Identity&) = delete;
    Identity& operator = (Identity&&) = delete;

protected:
    Identity() { _id = _nextId++; }

private:
    int _id;
    static int _nextId;
};

// empty destructor
Identity::~Identity() {}

int Identity::_nextId = 0;

/// derived classes
class Component : public Identity
{
    friend class ResourceManager;
public:
    ~Component() { }
protected:
    Component() = default;
};

class Entity : public Identity
{
    friend class ResourceManager;
public:
    ~Entity() { }
protected:
    Entity() = default;
};


class ResourceManager
{
public:
    template<typename T>
    T& Create()
    {
         std::unique_ptr<T> ptr(new T);
         T& res = *ptr;

        _resources[ptr->Id()] = std::move(ptr);
        return res;
    }

    /// TODO: need to make sure that resource ID is actually of type T
    /// and that _resources contains ID.

    template<typename T>
    T* Get(int id)
    {
        auto it = _resources.find(id);
        if (it == _resources.end()) {
            return nullptr;
        }
        return dynamic_cast<T*>(it->second.get());
    }

private:
    std::map<int, std::unique_ptr<Identity>> _resources;
};

Обратите внимание, что поскольку ResourceManager владеет ресурсом, я меняю std::shared_ptr на std::unique_ptr. Я исправил ResourceManager::Get с недопустимым идентификатором.

  • 0
    Спасибо за подробный ответ. К сожалению, у этого есть несколько проблем: не объявляя реализацию для ~Identity , я получаю неразрешенный внешний вид при сборке. Если я добавлю это, то смогу напрямую создать экземпляр Entity и Component с помощью auto& e = Entity() . Предложения?
  • 0
    @DavidLively: исправлено. Вы можете реализовать ~Identity и все же позволить объявить его виртуально чистым.
Показать ещё 2 комментария
1

Вы пробовали защищенный конструктор?

class Identity
{
     friend class ResourceManager;

public:
     int Id() { return _id; } 

     virtual ~Identity() = 0;

protected:
    Identity() {
         _id = _nextId++;        
    }

private:
    static int _nextId;
    // do not forget to put "int Identity::_nextId = 0;" in a source file
};

Identity::~Identity()
{
}

Но вам нужно повторить этот шаблон в каждом производном классе. Таким образом, сделать ResourceManager другом и сделать конструктор private или protected.

  • 0
    Отличный ответ. Я принял Джарода из-за довольно полной реализации.

Ещё вопросы

Сообщество Overcoder
Наверх
Меню