2

I'm a beginner programmer, and I was wondering...

I need a unique ID for my object in a game

so I thought I define a global class that holds the global id, and when I call the constructor of an object it will be incremented, while calling the destructor of that object it decrements it.

Is it true that for every constructor call there will be a corresponding destructor?

class id_holder{
private:
    int id_count;

public:
    id_holder() {id_count = 0 ; std::cout<< "unique_id: I'm constructed"<<std::endl;}

    ~id_holder(){std::cout<<"unique_id: I'm destructed"<<std::endl;}

    void increment_id(){id_count ++;};
    void decrement_id(){id_count --;};

    int get_id() {return id_count;};

};

class BaseObject {
protected:
    void increment_id();
    void decrement_id();
public:
    virtual ~BaseObject(){decrement_id();}
    BaseObject();
    BaseObject(V2 pos, ObjectType idobj, double s, Color col);
};

BaseObject::BaseObject(){   
    //stuff
    increment_id();
    unique_id = bObj_id_counter.get_id();
}


BaseObject::BaseObject(V2 pos, ObjectType idobj, double s, Color col){
    //stuff
    increment_id();
    unique_id = bObj_id_counter.get_id(); 
}

void BaseObject::increment_id(){
    bObj_id_counter.increment_id();
}

void BaseObject::decrement_id(){
    bObj_id_counter.decrement_id();
}

side question... is it possible to compare the memory adresses (this should be a unique id enough) of the objects instead of using an id_holder?

4
  • The main problem I see with using memory addresses is that your "unique" ID's are not going to be unique between runs of your program. That might cause problems with networking, storage, etc, Commented Mar 22, 2012 at 20:43
  • 1
    If you're using Boost, or wouldn't mind using it, the UUID library solves this problem. Commented Mar 22, 2012 at 20:44
  • 2
    Is there any specific reason that the main unique ID has to be decremented ever? If it only increments, it can only give out unique numbers until its limit is reached - if you make it a long long, that will be...never, even if you're doing it 1000 times per frame at 100fps for years. In fact, if you decrement it like this, object 7 may decrement it while objects 8...10 are still there, and the next object will be counted as 10, even though there is already a 10 Commented Mar 22, 2012 at 20:48
  • Where is bObj_id_counter? You never said. Commented Mar 22, 2012 at 20:52

5 Answers 5

4

Is it true that for every constructor call there will be a corresponding destructor?

Yes, unless you allocate objects using operator new and don't destroy them, or, that your application ends abnormally.

Your solution should work well, unless you use it concurrently. You should provide exclusive access to that global object if you do so, though i don't think this is the case.

is it possible to compare the memory adresses (this should be a unique id enough) of the objects instead of using an id_holder

You could do it, every object will have a unique memory address. It's a kind of dirty solution, depending on what is your objective.

Sign up to request clarification or add additional context in comments.

4 Comments

@MooingDuck Could you give me an example of an object created on the stack with dynamic lifespan? I don't quite get what you're telling me. I meant objects allocated via operator new, basically.
Oh placement new, i thought about it. You're right, thanks, i'll fix that :D.
Alright, this is a nitpick, but you don't delete stuff constructed with placement new, you "destroy" it. (You already got my +1, just going for perfection)
Hahaha, ALRIGHT sir, i'll fix that too :D
3

No, that is not necessarily true. In every well behaved program there is a destructor call for every constructor call. But if, for example, objects constructed with new aren't deleted, there will be no destructor call.

Also, in your example you didn't implement a copy constructor which means, that you might account for more destructor calls than constructor calls.

Further, you didn't implement an assignment operator, either. If the default one is used, the object being assigned to will have the same unique_id as the one being assigned from. (Assuming, unique_id is a memeber of BaseObject which you didn't declare in your code snippet.) So that would make your unique_id quite "un-unique". ;-)

1 Comment

I think you got it, I forgot the copy constructor, now it works as expected!
1

This is typically called "reference counting" and it has a wide range of uses. Yes it is possible to do, but yours won't quite work correctly just yet.

  1. If you want a global unique identifier for all your objects, you would need to have one id held statically, and then use the curiously recurring template pattern to ensure that this id is global only for specific types. Then each type gets a copy of the global id during construction.
  2. You need to increment the counter in the copy-constructor
  3. Since increment_id() and decrement_id() are called by the id_holder class, there is no reason to call them again in your BaseObject constructor/destructor.
  4. It will not work in the presence of multithreaded access, you need to find a way to atomically set the "reference count".

This is a relatively elegant solution that allows for you to do things like constant-time lookups for an item given its ID. For example:

Warning pseudo-code (not compiled or tested)

template <class T>
class counter
{
   static size_t global_id_;
   size_t id_;

public:
    counter() : id_(global_id_++) {}
    counter(const counter&) : id_(global_id++) {}
    ~counter() {}

    counter& operator=(const counter&)
    {
      // left as an exercise for the reader
      // as what to do in this case is highly 
      // dependent upon the application
    }
};

class counted_object : public counter<counted_object>
{
   // stuff
};

Now you could hold a lookup table of counted_objects stored in a vector for very quick lookups:

std::vector<counted_object*> lookup;
counted_object* o416 = lookup[416];

1 Comment

neat! I'll might implement that, I think it could be really useful, now what I need is just a unique id!
0
  1. Yes, there will be a destructor for every class and the compiler will give you a default if you don't provide one. Look at C++ default destructor

    1. Although you can use memory addresses, it's not recommended. Can get quite obfuscating, espc. if using shared ptrs and such.

Comments

0

Yes, it is, and yes you can compare the addresses. Only problem is if you have memory leaks and the objects don't get deleted.

Every object has a unique address. Even if you create an empty class or struct, it's size will not be zero(by language design). Be careful when passing the this reference from a constructor when working with multiple threads, as your object might not be fully constructed.

EDIT: You can use this to your advantage to discover memory leaks. Assuming you delete every object you have created by the time the program exits (like a clean-up method), just see if you have any objects left.

EDIT2: Here's a very good article on object counting in C++

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.