2

In my C++ project, I have an Engine class and an Object class.

My issue lies with how my instances of Object are created. Currently this is done through the use of a CreateObject(parameters) function in the Engine class. This adds a new instance of Object to an std::vector of Object instances.

I want to maintain this list of instances of Object in my Engine class, but without the need for the CreateObject(parameters) function. My reason for this is so that I can create new classes that can inherit from Object but still be added to this list. The reason for this list is so that (in Engine) I can iterate through every Object instance that has been created.

This would ultimately mean that I create my Object instances with something like Object newObject = Object(parameters);, but still have the Engine class maintain a list of all Object instances, without the need for Object to reference the instance of Engine or the list to add itself to this list (as in the instance of Object should not know about the list it is in). Can this be done?

Interminable
  • 1,338
  • 3
  • 21
  • 52
  • This cannot be done without making the instance of `Engine` `static` or singleton (which is a glorified way of saying "static" anyway) or making an object itself maintain a static list of its own instances, and giving `Engine` an access to that list. – Sergey Kalinichenko Aug 02 '13 at 11:05
  • How about keeping instances of Object in static vector of Engine class and use static public function to push Object instances? You would call it in constructor of Object class. – mag_zbc Aug 02 '13 at 11:06
  • For the problem only of storing an instance of an object, maybe have a look at my answer to the question [c++ storing an object into an array of objects within the constructor of that object](http://stackoverflow.com/questions/25838551) which is an attempt to use more modern pointers. – NGI Aug 21 '16 at 21:44

4 Answers4

6

You can define a static collection data member in your Engine class, update it in your Object constructor and destructor:

class Engine
{
    friend class Object;
...
public:
    static std::set< Object* > m_instances;
};

class Object
{
public:
    Object();
    virtual ~Object();
    ...
};

You increment it in constructors, and decrement it in destructors.

Object::Object()
{
    Engine::m_instances.insert(this);
}

Object::~Object()
{
    Engine::m_instances.erase(this);
}
Codie CodeMonkey
  • 7,669
  • 2
  • 29
  • 45
  • 1
    I think you wanted to have m_instances declared as static. – drescherjm Aug 02 '13 at 11:49
  • Is this good practice? Are there any drawbacks to doing things this way? – Interminable Aug 02 '13 at 15:25
  • Also, your answer looks quite similar to Joseph Pla's and mag_zbc's, except you use an `std::set` instead of an `std::vector`. I've never used `std::set` before and know nothing about it beyond the fact that you're clearly using it as a container here (I'll look up what it is later when I have the time). Is there a reason you used it for this scenario over `std::vector`? Or is it just personal preference? – Interminable Aug 02 '13 at 15:35
  • @Interminable, yes, because you may have a lot of instances, and may delete them in a different order than you allocated them. Finding the element to delete is faster in a set. – Codie CodeMonkey Aug 02 '13 at 16:43
  • Alrighty then. One last question - why have you made the destructor virtual? – Interminable Aug 05 '13 at 13:54
  • @Interminable, Let me point you to this post: http://stackoverflow.com/questions/461203/when-to-use-virtual-destructors. I prefer make my destructors virtual as part of my philosophy of defensive programming. Unless I have a specific reason not to have a virtual destructor I do it so that I don't introduce errors due to construction of a derived class instance and destruction of the base class instance. I don't always have control over how my code is eventually used, and the errors caused by this can be subtle and hard to find. – Codie CodeMonkey Aug 05 '13 at 17:50
  • I'm afraid I have again one last question. I've been doing a little reading on `set`. I may be deleting them in a different order than I allocate them, but I will also be iterating through them as often as possible. During these iterations I will call each object's respective `Update()`, as well as checking two `bool` variables to see if it should be drawn or not. My reading suggests it is faster to iterate through a `vector`, and I'll obviously be looping through the list more often than I'll be adding/removing from it. Would you say a `set` is still the way to go in this situation? – Interminable Aug 06 '13 at 07:53
  • Is this thread safe? What if I want to create/destroy objects in multiple threads at the same time? I think, in such case one should guard the access to the list of instances by a lock/mutex. – TimeS Mar 07 '19 at 09:11
0

I think your factory pattern approach is the good way to achieve this. The engine manages all the instances of Objects internally.

But if you want to make external instances, managed by the engine too, you must have to access a instance of the engine; even if that instance is a global variable, even if the Engine class implements a singleton pattern.

The second best approach to do that (The first is what yoa are doint yet, the factory), I think is to implement a singleton in the Engine:

class Object
{
    Object
    {
        Engine::instance().addObject( this ); //Note that engine stores pointers. STL containers cannot store references.
    }

    ~Object
    {
       Engine::instance().releaseObject( this );
    }
};
Manu343726
  • 13,969
  • 4
  • 40
  • 75
0

Like many have mentioned, factory architecture is a nice and clean way to go, otherwise you are going to have to have a global instance, static members or a singleton. However, a static approach would be to make Object a friend of class engine and make the members static:

class Engine{
     friend class Object;

     private:
     static std::vector <Object*> objectList;
};

This will allow Object to access private members of Engine statically. Then in the constructor of Object, add it to the list like so:

Object::Object(int randomparam1, const char* randomparam2)
{
    Engine::objectList.push_back(this);
}
Joseph Pla
  • 1,600
  • 1
  • 10
  • 21
0

Use a static

    vector<Object*> Objects

variable in Engine class and static public function

    Push(Object* obj) { Objects.push_back(obj); }

to push Objects to the list. Then in constructor of your Object class you would call

    Engine::Push(this)
mag_zbc
  • 6,801
  • 14
  • 40
  • 62