1

I want references to created objects to be automatically added to a vector (often multiple vectors), when the objects are created. To give some context, this code will be used in a game for sets of game objects (Drawable, Collidable, Enemies, etc) hence the need for multiple vectors.

An example of what I am trying to achieve is shown here:

#include <iostream>
#include <memory>
#include <vector>
class BaseClass :public std::enable_shared_from_this<BaseClass>
{ //for example "drawable"
public:
    BaseClass()
    {
        std::cout << "Base Created"<<std::endl;
        BaseList.push_back(shared_from_this());//I want to put a reference to this object in a vector
    }
    ~BaseClass()
    {
        std::cout << "Base Deleted"<<std::endl;
    }
    static std::vector<std::shared_ptr<BaseClass>> BaseList;

};
class DerivedClass :public BaseClass
{//for example "enemy"
public:
    static std::vector<std::shared_ptr<BaseClass>> DerivedList; //shared_ptr of baseclass can point to derivedclass
    DerivedClass()
    {
        std::cout << "Derived Created" << std::endl;
        DerivedList.push_back(shared_from_this());//reference to this object in a vector in addition to the other vector
    }
    ~DerivedClass()
    {
        std::cout << "Derived Deleted" << std::endl;
    }
};
std::vector<std::shared_ptr<BaseClass>> BaseClass::BaseList;
std::vector<std::shared_ptr<BaseClass>> DerivedClass::DerivedList;
int main()
{

    std::shared_ptr<BaseClass> C = std::make_shared<BaseClass>();
    std::shared_ptr<BaseClass> D = std::make_shared<DerivedClass>();

    BaseClass::BaseList.clear(); //C should be deleted, D should not since it is still in DerivedList
    DerivedClass::DerivedList.clear(); //now D should be deleted
    return 0;
}

In this code, the use of shared_from_this() does not work correctly, since it is in a constructor (As Shown Here). Previously I have overcome this issue using separate static functions, for example:

void BaseClass::makeOne()
{
    std::shared_ptr<BaseClass> P(new BaseClass());
    BaseClass::BaseList.push_back(P);
}

void DerivedClass::makeOne()
{
    std::shared_ptr<BaseClass> P(new DerivedClass());
    BaseList.push_back(P);
    DerivedList.push_back(P);
}

however in a context where multiple classes are derived from a single base class, and each derived class may also be added to other vectors, code duplication becomes an issue (BaseList.push_back(P) should be called for every object that inherits BaseClass, and hence must be written in every X::MakeOne() where X inherits BaseClass ).

I have also overcome this issue by simply using raw pointers (std::vector<BaseClass*>) however this loses the benefit of simple memory management and reference counting when objects are referenced in multiple places. Is there a better option for memory management in this situation?

jcg
  • 69
  • 6
  • Why bother having a `DerivedList` if it doesn't contain `std::shared_ptr`? Hint: you can have `shared_ptr`s of different types sharing ownership of the same object, see [static_pointer_cast](https://en.cppreference.com/w/cpp/memory/shared_ptr/pointer_cast) – Caleth Jun 27 '18 at 15:51
  • If you have a separate static container for every type then can't you just store them as **values**? – Galik Jun 27 '18 at 15:58
  • @Galik No, for example `DerivedClass` instances are stored in both `BaseList` and `DerivedList` – jcg Jun 27 '18 at 16:08
  • @Caleth The name `BaseClass` was an example, if you consider it as `GameObject` for example, the vectors could become `DrawableList` and `CollidableList`, which makes it clearer why the type of the vector should be the base class rather than any one of the many derived classes which may arise in context – jcg Jun 27 '18 at 16:12
  • 1
    From your example: `std::vector> BaseClass::BaseList; std::vector> DerivedClass::DerivedList;` The second one will be easier to use if it is `std::vector> DerivedClass::DerivedList`, you won't have to litter it's usage with casts to `DerivedClass` – Caleth Jun 27 '18 at 16:19
  • Agreed, however in a case such as mine, casts might be unavoidable when `drawableList` could contain both `Player` and `Enemy` under the base class `GameObject` – jcg Jun 27 '18 at 16:25

2 Answers2

1

I think this can be resolved with using a Factory/Provider.

class SomeBaseClass // Could also be an interface or similar
{
};

// Instead of having SomeBaseClassFactory instance, all the methods could also be static
class SomeBaseClassFactory
{
public:
    std::vector<std::shared_ptr<SomeBaseClass>> someBaseClassList;

    std::shared_ptr<SomeBaseClass> GenerateObject(/* Parameters */)
    {
        std::shared_ptr<SomeBaseClass> someBaseClass = std::make_shared<SomeBaseClass>(/* Parameters */);
        RegisterObject(someBaseClass);
        return someBaseClass;
    }

protected:
    void RegisterObject(std::shared_ptr<SomeBaseClass> objectToRegister)
    {
        someBaseClassList.push_back(objectToRegister);
    }
};

class SomeDerivedClass : public SomeBaseClass
{
};

class SomeDerivedClassFactory : public SomeBaseClassFactory
{
public:
    std::shared_ptr<SomeDerivedClass> GenerateObject(/* Parameters */)
    {
        std::shared_ptr<SomeDerivedClass> someDerivedClass = std::make_shared<SomeDerivedClass>(/* Parameters */);
        RegisterObject(someDerivedClass);
        return someDerivedClass;
    }
};
Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770
Merx
  • 21
  • 4
1

You can define factory for your objects like this:

#include <iostream>
#include <memory>
#include <vector>

class Factory
{
public:
    template<class T, class ... Args>
    static std::shared_ptr<T> Make(Args... args)
    {
        auto instance = std::shared_ptr<T>(new T(args...));
        T::List.push_back(instance);
        return instance;
    }
};

class BaseClass
{ 
    friend class Factory;  
protected:    
    BaseClass()
    {
        std::cout << "Base Created"<<std::endl;
    }

public:
    virtual ~BaseClass()
    {
        std::cout << "Base Deleted"<<std::endl;
    }

    static std::vector<std::shared_ptr<BaseClass>> List;

};
class DerivedClass : public BaseClass
{//for example "enemy"
    friend class Factory;  

    DerivedClass()
    {
        std::cout << "Derived Created" << std::endl;
    }
public:
    ~DerivedClass()
    {
        std::cout << "Derived Deleted" << std::endl;
    }

    static std::vector<std::shared_ptr<BaseClass>> List; //shared_ptr of baseclass can point to derivedclass
};

std::vector<std::shared_ptr<BaseClass>> BaseClass::List;
std::vector<std::shared_ptr<BaseClass>> DerivedClass::List;

int main()
{
    auto base = Factory::Make<BaseClass>();
    auto derived = Factory::Make<DerivedClass>();
}
Amadeusz
  • 1,488
  • 14
  • 21
  • This does not allow for adding the pointer to the vectors required by both `BaseClass` and `DerivedClass`, where `DerivedClass` should also be placed into the vector that `BaseClass` is. Vectors may not always necessarily be static members of the class that is placed into them (although my question does not clarify this, will edit to correct this), hence using a template in the fashion `T::List.push_back()` is not really feasable. – jcg Jun 27 '18 at 16:32