7

Is there a way through which we can get all the objects of a class in C++? Like in Python we can do:

class_name.objects.all()

to get all the objects of a class. What's its analog in C++, if it exists?

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
itsrishre
  • 103
  • 1
  • 2
  • 8

5 Answers5

14

You can do this yourself, but make sure you know what you're doing.

How:

There's nothing within C++ that already does this, but it's pretty easy to do this yourself. The key is to recognize that a class can have static member variables and functions (i.e. functions that belong to the whole class, rather than to individual objects of the class).

So you can use some kind of table or other data structure to store a reference to each object. Like so:

class A {

public:
  //constructor assigns this object an id based on the static value curID,
  //which is common to the class (i.e. the next class to call the constructor 
  //will be assigned an id thats 1 more than this one
  //also, constructor adds the pointer to this object to a static map of ids 
  //to objects. This way, the map can be queried for the pointer to an object 
  //that has a particular id
  A() { 
    id = curID++;
    objects[id] = this;
  }
 
  //copy constructor ensures an object copied from another does not 
  //take the id of the other object, and gets added to the map
  A(const A&) {
    id = curID++; //don't want have the same ID as the object we are copying from
    objects[id] = this;
    x = A.x;
    y = A.y;
  }

  //destructor removes the pointer to this object from the map
  ~A() {
    objects.erase(id);
  }

  //function to get the map that stores all the objects
  static map<int, A*>& GetMapOfObjects() { 
    return objects;
  }

private:
  //the following variable is **static**, which means it does not
  //belong to a single object but to the whole class. Here, it is
  //used to generate a unique ID for every new object that's 
  //instantiated. If you have a lot of objects (e.g. more than
  //32,767), consider using a long int
  static int curID;  

  //this variable is also static, and is map that stores a pointer
  //to each object. This way, you can access the pointer to a
  //particular object using its ID. Depending on what you need, you
  //could use other structures than a map
  static map<int, A*> objects; 


  //this is a (non-static) member variable, i.e. unique to each object.
  //Its value is determined in the constructor, making use of curID.
  int id;

  //these are some other member variables, depending on what your object actually is
  double x;
  double y; 
}

Note: The above design is very basic and not complete, but just meant to give you an idea of how to implement what you're asking for using static members/functions. For example, for operations that you want to perform on all the objects, for example, it may be better to implement a static function that iterates through the map of elements, rather than getting the map and then doing the iterations "outside".

Why:

I've never used this method myself, but one potential use case I can think of is e.g. in a graphics or game application, where you may want to only draw objects that are in scope and change certain drawing-related properties of all of them at once, e.g. color or size. I'm working on an application that might eventually need something like this (sort of a visual debugger). I'm sure people can provide more examples in the comments.

Why not:

The picture gets complicated when inheritance is involved.

  • If you have a class B that derives from A (i.e. B "is an" A), then who should keep track of objects of B? A static member of objects in A, or a similar one in B, or both?
  • Let's say both. Then what happens if a static function that applies to all objects in A calls a virtual member function on each object? If the virtual function has been overridden in the derived class, then that function will be called instead for all objects being tracked in class A that are actually B objects. What happens if you then call that function again in another static function in B?
HolyBlackCat
  • 78,603
  • 9
  • 131
  • 207
maditya
  • 8,626
  • 2
  • 28
  • 28
  • You forgot assignment operator. – SigTerm Jul 07 '13 at 06:07
  • @MartinJames Agreed. A mutex could be used to control access to the map but has the usual downsides (overhead, added complexity of implementation and interface, etc) – maditya Jul 07 '13 at 08:01
  • @MartinJames: You can put container manipulation into separate subroutine and protect it using whatever mechanism is available. OR you could maintain multiple maps-one map per thread id. – SigTerm Jul 07 '13 at 08:01
  • The assignment operator is a mistake, I removed it. – HolyBlackCat Aug 20 '23 at 14:44
5

There is no way that I know of but you can implement one with static members

#include <iostream>
#include <vector>
class MyClass{
private:
 static std::vector<MyClass*> objList;
public:
 MyClass() {
  objList.push_back(this);
 }
 static std::vector<MyClass*> getAllObjects(){
  return objList;
 }
};

std::vector<MyClass*> MyClass::objList;

main(){
 MyClass m,a;
 for (int i=0;i<MyClass::getAllObjects().size();i++){
  std::cout<<MyClass::getAllObjects()[i]<<std::endl;
 }
}
4

No, unless you implement this mechanism yourself. By default, it is not provided by the C++ language.

You can implement this mechanism yourself quite easily. Register the class in some kind of table within the constructor, and unregister within the destructor. As long as you follow the rule of three, it'll work fine.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
SigTerm
  • 26,089
  • 6
  • 66
  • 115
  • But anyways, if this is needed, then something's utterly bad with the design. –  Jul 07 '13 at 05:31
  • 2
    @H2CO3 Agreed. And what does that say about the state of Django? : ) – djf Jul 07 '13 at 05:32
  • 2
    @H2CO3: No. You will have to implement this mechanism if you want to be able to locate instance of class by some kind of ID. One scenario I can think of is game engine. – SigTerm Jul 07 '13 at 05:33
  • @SigTerm I doubt that "have to" is so strict. Again, then you can just create a constructor that takes the ID as its argument, etc. This **does** indicate a serious design failure. –  Jul 07 '13 at 05:55
  • @SigTerm (Just like casting the return value of `malloc()` is necessary in C++, sure it is. But it's wrong nevertheless, and the resolution of this contradiction is that if one uses `malloc()` in C++, then there's a serious problem.) –  Jul 07 '13 at 05:56
  • @H2CO3: "But it's wrong" No. In my opinion, you're being perfectionist again. As far as I know, there are no universally "right" or "wrong" technique, only techniques that are more or less suitable to *current situation* only. In my experience, most "wrong" techniques are only wrong until you run into situation that requires them. I'd suggest to replace "wrong" with "I don't know where I would use this" or something like that. Have a nice day. – SigTerm Jul 07 '13 at 06:06
  • @SigTerm But at least don't assume *I* don't know what *I* am talking about and what *I* would use and where... –  Jul 07 '13 at 06:07
  • @H2CO3: Your reply "sounds" unnecessarily emotional. I base my opinion of people (their knowledge, etc) on their replies. When somebody says something is "wrong", normally it means they haven't seen enough "gray area" situations or situations that would require use of the "wrong" technique. Eventually they encounter those situations and change their opinion. – SigTerm Jul 07 '13 at 06:11
  • @SigTerm Then here you are [the opinion of a presumably smart programmer who I assume is right, at least in this particular case.](http://stackoverflow.com/questions/605845/do-i-cast-the-result-of-malloc/605858#605858) - That's only what I was talking about, maybe you misunderstood me and/or I misunderstood you, but there's no need to be personal and claim that I don't know stuff because I have XY opinion of something. That's argumentum ad hominem and makes you "sound" very unprofessional. –  Jul 07 '13 at 06:14
  • @H2CO3: "maybe you misunderstood" I was talking about id generation or registration/unregistration in object's constructor/destructor. I've run into this pattern many times. (continued) – SigTerm Jul 07 '13 at 06:19
  • 1
    @H2CO3: "be personal" I wasn't being personal. It is just that your style of writing reminded me of myself 10 years ago. (opinion) I "see" few problems, and if you fix them, you'll become better programmer, so I thought I'd point them out. Problems: 1. "wrong" without explanation. if you say "wrong", you should be able to explain "why". 2. If you can't explain "why", it is a unbased belief. 3. Absolutism. "This technique is right and this is not", might mean you're under somebody's influence, but haven't really though about it. 4. Criticism is perceived as personal attack. – SigTerm Jul 07 '13 at 06:22
  • @H2CO3: That's just an opinion anyway, and you're free to ignore it. If you perceive me as "attacking" you personally, then that was not my intent. I'm absolutely indifferent to you at the moment, and are not attacking or trying to be personal or whatever. Have a nice day, I'm done here. – SigTerm Jul 07 '13 at 06:24
  • The problem I have with this is that the class becomes auto-thread-unsafe. – Martin James Jul 07 '13 at 07:11
2

Of course there is. Just use Factory pattern to create and destroy all your objects and, in Factory implementation, return a collection of live objects in a Factory function that you will provide.

Tony
  • 1,566
  • 2
  • 15
  • 27
  • I didn't downvote, but IMO I find this over-engineered. A static container holding pointers/references to object would suffice and be a lot simpler than this. Also the problem with requiring a factory to create all your objects is it doesn't meld very well with standard containers (AFAIK). Sounds to me like this solution would be better if this was a Java question. – Borgleader Jul 07 '13 at 05:50
  • I can't believe that modifying every object constructor and destructor or introducing statics is considered as better solution than implementong Factory pattern. – Tony Jul 07 '13 at 05:54
  • @Borgleader overengineered? Look at the solutions above. We don't know that this is the only requirement that OP may have on a set of objects that app will create. Having it all centralized in a Factory is IMHO way better approach. There is nothing preventing Factory having some STL container as internal implementation. – Tony Jul 07 '13 at 06:09
  • Need I remind I'm not the one who downvoted you? I gave you *my* opinion. The person who downvoted you may have had a totally different (and possibly better) reason. As for my comment on standard containers, I wasn't talking about the internals of the factory. – Borgleader Jul 07 '13 at 06:14
  • @Tony: "We don't know that this is the only requirement" KISS principle + YAGNI. Unless other requirements are provided, it IS the only requirement. Factory makes sense if you're returning multiple different "hidden" derived classes based on standard abstract interface. Because there's no such requirement, no need for factory. – SigTerm Jul 07 '13 at 06:31
  • I understand that, I wasn't referring to non downvote by you. Rather, I was debating your claim that static container would be simpler than abstracting it all in a Factory object. It can internally use map if search is required by id, or it may be a vector or a list. Certainly a level of abstraction better than horrible mess above – Tony Jul 07 '13 at 06:32
  • @Tony: "your claim that static container" I did not say that, please check nicknames. Factory will, however, introduce extra class you'll have to take care of, so (IMO) it would be better to kill extra class, unless this class is absolutely necessary (which is not the case in current situation). – SigTerm Jul 07 '13 at 06:34
  • @SigTerm static container was referring to Borgleader comment. Kill extra class on account of modifying every class constructor/destructor - sorry can't agree with that way of simplifying things. – Tony Jul 07 '13 at 06:40
  • @Tony "every class", there is no *every class* the question mentions *a* class, not *all* classes – Borgleader Jul 07 '13 at 06:40
  • 4
    @Tony: My reasoning is based on KISS principle. Static container will take 3..5 lines of code (assuming class already has constructor/destructor). Factory will take extra class, plus you'll probably have to make it singleton, plus you'll have to add extra special object creation mechanism and make sure you use it everywhere, plus you'll have to make classes (constructed by factory) non-default-constructible and non-copyable, otherwise temporary variables won't register, etc. Because of this, I see no reason to use factory. Hidden container covers everything in 3..5 lines of code. – SigTerm Jul 07 '13 at 06:43
  • @SigTerm : my reasoning is based on - abstract it before a simple solution becomes a nightmare to maintain. I doubt that requirement will be limited to a single class. – Tony Jul 07 '13 at 06:58
  • This, (as a developer who writes a lot of multithreaded code), seems like a much safer solution. – Martin James Jul 07 '13 at 07:13
2

As has already been stated C++ does not provide a mechanism to do this automatically. However (again has already been stated in the comments) you can use one of the standard library containers to maintain a list of created objects and then register them in the constructor and unregister them in the destructor. The example below shows one way to do this...

#include <iostream>
#include <memory>
#include <utility>
#include <map>
#include <algorithm>
#include <iterator>
#include <typeinfo>
#include <vector>

class Object
{
    static std::map<const Object*, Object*> objects_;

public:

    Object()
    {
        objects_.insert(std::make_pair(this, this));
    }

    virtual ~Object()
    {
        objects_.erase(this);
    }

    static std::vector<Object*> get_all()
    {
        std::vector<Object*> o;
        o.reserve(objects_.size());
        for (auto obj : objects_)
        {
            o.push_back(obj.second);
        }
        return std::move(o);
    }

    template<class Type>
    static std::vector<Type*> get_bytype()
    {
        std::vector<Type*> o;
        for(auto obj : objects_)
        {
            Type *t = dynamic_cast<Type*>(obj.second);
            if (t != nullptr)
            {
                o.push_back(t);
            }
        };
        return std::move(o);
    }


    void print() const
    {
        std::cout << "I'm a " << typeid(*this).name() << " object @ " << this << std::endl;
    }
};

std::map<const Object*, Object*> Object::objects_;

class Foo : public Object {};
class Bar : public Object {};
int main()
{
    std::unique_ptr<Object> o1 = std::unique_ptr<Object>(new Foo());
    std::unique_ptr<Object> o2 = std::unique_ptr<Object>(new Bar());
    std::unique_ptr<Object> o3 = std::unique_ptr<Object>(new Foo());
    std::unique_ptr<Object> o4 = std::unique_ptr<Object>(new Bar());

    std::vector<Object*> objects = Object::get_all();
    for (auto o : objects)
    {
        o->print();
    }

    std::cout << "-----" << std::endl;

    std::vector<Foo*> foos = Object::get_bytype<Foo>();
    for (auto o : foos)
    {
        o->print();
    }

    std::cout << "-----" << std::endl;

    std::vector<Bar*> bars = Object::get_bytype<Bar>();
    for (auto o : bars)
    {
        o->print();
    }
}

The above example produces the following output

I'm a class Foo object @ 003FED00
I'm a class Bar object @ 003FED30
I'm a class Foo object @ 003FED60
I'm a class Bar object @ 003FED90

I'm a class Foo object @ 003FED00
I'm a class Foo object @ 003FED60

I'm a class Bar object @ 003FED30
I'm a class Bar object @ 003FED90

Captain Obvlious
  • 19,754
  • 5
  • 44
  • 74