Я видел несколько сообщений о фабриках 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()
защищен, я все же могу напрямую создавать производные классы.
Следующее должно работать:
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
с недопустимым идентификатором.
~Identity
, я получаю неразрешенный внешний вид при сборке. Если я добавлю это, то смогу напрямую создать экземпляр Entity
и Component
с помощью auto& e = Entity()
. Предложения?
~Identity
и все же позволить объявить его виртуально чистым.
Вы пробовали защищенный конструктор?
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
.