質問

I've seen a number of posts regarding C++ factories, but so far I haven't seen a solution that solves my problem. (Though I may be missing something.)

Example console app:

#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();
}

I need to make sure that only ResourceManager can instantiate Entity, Component and any classes that derive from them.

I've looked and adding ResourceManager as a friend class to Identity, and making the constructors private or protected, with no success. (This could be a blind alley, or just an implementation problem on my end.)

Any suggestions?

Update and Edit

Replaced code with compilable example. Although the constructor for Identity() is protected, I can still directly instantiate derived classes.

役に立ちましたか?

解決

Following should work:

friend class ResourceManager; should be in each derivated classes. (friend is not inherited).

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;
};

Note that since ResourceManager owns the resource I change std::shared_ptr to std::unique_ptr. I fixed ResourceManager::Get with invalid id.

他のヒント

Have you tried a protected construtor?

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()
{
}

But you need to repeat this pattern in every derived class. Thus making ResourceManager a friend and making the constructor private or protected.

ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top