简体   繁体   中英

Can pointers be safely and portably used as object IDs?

Suppose I have a bunch of valid pointers to instances of a class A . Can I safely and portably assume that the pointers will stay the same during the whole object lifetime? In other words, can I use a pointer to an object as a unique object ID?

Sure; I don't see why not.

A memory address is pretty much the definition of object identity (taking type out of the equation for a moment).

Not necessarily - it depends how you allocate those instances and what you then do with them. For example, suppose that you create an array of objects and then reallocate that array. There is now no guarantee that the objects will still reside in the same location.

You can only do this if you are sure that no object will be created once an object gets deleted. The new object could be created at the same address of a previously deleted object, and this could be a problem: you don't want two different objects to have the same ID.

While it is generally true that a memory address uniquely identifies an object. There are two important caveats:

  1. When an object is deleted, the address may be reused (very possibly for an object of the same class).

  2. If you are working with different processes, addresses may well be the same in the two processes.

So long as the object stays in memory and you always refer to the same instance (eg. you don't pass it by value and then compare that object to the original or another cached copy) the address will always stay the same, and the pointer is a reasonable ID. This is obviously true -- if the object moved in memory, the pointer wouldn't work any more, and the standard guarantees that pointers to different objects compare to different values. [This is why you can't have zero length objects, so the following object in memory has a different address.]

However, if the object will ever be passed across a network or saved to disk, obviously that won't work any more, and you'll have to compare the object element-by-element, or have it include a unique id value (aka primary key).

ETA: borrible is correct that if you realloc the memory containing the objects, the values will be different, but obviously the pointers won't be valid at all, so your program will be broken whether you expect the pointers to be unique or not. (Conversely, if you use smart points that will track reacllocs -- essentially how C# and .NET work -- the value will not necessarily be the same, but the values of the pointers will still be different between different instances of the object.) In fact, that's the rule -- so long as the pointers are valid, you should be able to compare them, but you can't cache the value and expect it to still work some undetermined time later (in case you destroy the object first), so if you're sure you won't, it's ok, but keep it in mind, often you WILL want to serialise the objects at some point, and then having designed them like that from the start will be better.

One worry (and it is a real worry) is cuckoo pointers.

Suppose you cache a pointer value A.

Then later free that pointer, then later allocate a new object B.

The memory allocator may choose the location that was used for A to allocate B.

Code of the form: if (new_pointer != old_pointer) {...}

may fail to realise that the object has changed.

http://www.nobugs.org/blog/archives/2004/11/20/cuckoo-pointers/

Edit:

Another way to put this, your object while alive will always be at the same address, the object at that address however will not always be the object you expect.

If the pointers to different subclasses aren't c-cast to void*, then yes, no problem with comparing pointers to check if it's the same object.

In the case of storing the address as void* or uint values for ID purposes, there's a few less obvious problems that occur when a class inherits from multiple classes.

Consider class C which inherits from class A and class B. If class C is referenced via a class A pointer it will show equal to referencing the same object via a class B pointer.

C* c = new C();
A* a = C;
B* b = C;
a == b; // true

However, if you cast a C object from a class A pointer to a void pointer you may end up getting the sub object address, rather than the full object's address. The types will keep track of this in a normal case and it's only a worry when void pointers are used.

void* avoid = (void*)a;
void* bvoid = (void*)b;
avoid == bvoid; // false

To guarantee the proper address to the most derived and complete object, use dynamic_cast( myObject ). Remember though, that it must be a polymorphic class and RTTI must be enabled for dynamic_cast to work.

Just going back a sec, someone said that polymorphism involves having multi addresses for one object?

I'm pretty sure, that this is what the virtual function table is for. Before Object1->func1() is called the type and the address of Object1 is used as keys to the vtable and an address for func1() is obtained. Right? Doing a recast, like back to the base class like:

BaseClass* OPtr = reinterpret_cast<BaseClass*>(DerivedObjectPtr);
OPtr->func1();

will make the key pair address + type different and can thus return different function pointers.

That is how I would write the runtime stuff anyway. Dont know about multiple inheritance though, which is an abomination imo. And I am also using object addresses as ID s. I can't really find any fault with it, can anyone else?

If all instances of class A are stored in heap, yes, they'll remain the same for the whole program lifetime. If, instead, instances are created in the stack, then absolutely no, because at the end of the scope they get destroyed.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM