Managing object-to-object relationships, including bidirectional ones, is where destructors become helpful. You can eliminate the problem of dangling pointers like this:
~EntityCharacter() {
// Do some other cleanup...
...
if (seatPtr) {
assert(seatPtr->characterPtr == this); // That's my seat!
seatPtr->characterPtr = NULL;
}
}
~ EntityVehicleSeat() {
// Do some other cleanup...
...
if (characterPtr) {
assert(characterPtr->seatPtr == this); // That's my character!
characterPtr->seatPtr = NULL;
}
}
One problem with this approach is concurrency: if a seat and a character can be deleted simultaneously from different threads, you would need to synchronize deletions.