Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How bad is to use integer pointers as unique ids? C++11

I got a class that when instantiated needs to obtain a few unique ids to work. Initially I thought using an static function that assigns and increments. I don't need them to be consecutive, only unique.

class A {
    int id_1;
    int id_2;
    int id_3;
public:
    static int last_id=0;
    static int get_id(){ return A::last_id++; }
    ...
    A(){ id_1 = A::get_id(); id_2 = A::get_id(); id_3 = A::get_id(); } 
};

Now, I' thinking in going multithreading. I think the static function will be a bottleneck, since I'm constructing a few hundred thousand instances of these objects at the start. I don't destroy any instance until the end of the program, so after initialization they are fixed. Anyway they are not computed at compile time because the quantity depends of command-line arguments.

An alternative I was thinking of was using memory addresses, they are unique in a single computer at least.

Something like:

class A {
    int* id_1;
    int* id_2;
    int* id_3;
public:
    static int last_id=0;
    static int get_id(){ return A::last_id++; }
    ...
    A(){ id_1 = new int(0); id_2 = new int(0); id_3 = new int(0); } 
    ~A() { delete id_1; delete id_2; delete id_3(); }
};

Then I would read the identifiers as the address of the pointers.

Question: Does this make any sense to use pointers like this?

like image 336
dvicino Avatar asked Jul 30 '14 23:07

dvicino


Video Answer


2 Answers

You're really not that far off with your original solution. Don't prematurely optimize! Incrementing an int is very cheap! My only suggestion would be to use std::atomic<int> instead of int.

class A {
    int id_1;
    int id_2;
    int id_3;

    static int get_id() {
        static std::atomic<int> next_id(1);
        return ++next_id;
    }

public:
    A() :
        id_1(get_id()),
        id_2(get_id()),
        id_3(get_id())
    { }

    // deal with copying by disabling
    A(const A&) = delete;
    A& operator=(const A&) = delete;

    // move is okay
    A(A&&) noexcept = default;
    A& operator=(A&&) noexcept = default;
};

Assuming you don't create more than 2^31/3 instances of A, you don't have to worry about overflow.

like image 137
Travis Gockel Avatar answered Sep 22 '22 23:09

Travis Gockel


I've used something like the following as a quick hack in C before - when I needed some unique values that were only unique over the lifetime of the process.

constants.h

extern const void * id_1;
extern const void * id_2;

constants.c

const void * id_1 = &id_1;
const void * id_2 = &id_2;

No worry about cleanup etc since they're extern'd globals.

In C++ for a class instance you could use the same idea, but localized to the instance:

class A {
    void* id_1;
    void* id_2;
    void* id_3;
public:
    A(){ id_1 = &id_1; id_2 = &id_2; id_3 = &id_3; } 
};

Note that the ids will only be unique while the instance exists - but you've said that they're allocated only destroyed at application exit - so you should be OK.

Note: I do consider this a hack, and with C++11s std::atomic the solution provided by Travis Glockel above is as simple and more robust. However without C++11 implementing atomic or adding an atomic library is messy.

like image 32
Michael Anderson Avatar answered Sep 19 '22 23:09

Michael Anderson