Inserting element to container: copy constructor messes up pointers

180 Views Asked by At

My problem is that the default copy constr. messes up my pointers.

Simplified version:

  • I have a A class with the default copy constr.

  • A has 2 member objects: B and C.

  • B has a pointer to C.

Situation:

  • I store A in a std::vector: vector.emplace_back(A(...));

  • emplace_back does this:

    • Creating A (setting up B's pointer to C, etc)

    • Copying A into the vector. (default copy ctr: copies by value)

    • Destroying the "origin" A.

. Result:

  • The storage has an A which has a B which has a pointer to the old A's C. Which doesn't exist anymore.

Simple solution would be:

  • In A there would be pointers to B and C

But this is not so good because why should A have pointers, when it owns its B and C and B and C shares lifetime with A.

Question:

  • Isn't there a better way than having pointers in A?

  • Or isn't there a simplier way than messing with copy constructor implementations?

  • I thought that emplace constructs "in-place", so why does it need a copy ctr?

2

There are 2 best solutions below

0
On BEST ANSWER

Isn't there a better way than having pointers in A?

Yes, there is. Furthermore, I don't see how having pointers in A solves the problem.

A solution is to write a user defined copy constructor for A, that will update the pointer of the copied member.

Or isn't there a simplier way than messing with copy constructor implementations?

No, a copy constructor is (perhaps arguably?) the simplest way to update the member pointer of the member.

I thought that emplace constructs "in-place"

It does.

so why does it need a copy ctr?

It doesn't in general.

It does require either a move or copy constructor in this case, because you pass it a temporary object. The object in the vector is constructed "in-place", by using the move (or copy, if not movable) constructor with the temporary as the argument.

Instead, you could have passed the arguments of the constructor of A direclty to emplace_back so that no temporary is involved.

Note that constructing the object in-place doesn't prevent the element of the vector from being copied, unless you have pre-reserved the memory so that no reallocation happens.

0
On

You can manually write the copy constructor of A to do the right thing.

Another plan, if everything is standard layout, is to use offset pointers. An offset pointer stores the difference in address between the pointer itself and what it points to. It typically uses max(ptrdiff_t) for null.

If written carefully, these permit pointers between to sub structures to be default copyable, as the relative address of the two remains constant after a copy.

However, possibly a better plan is to augment all methods in B to take a pointer-to-C, and pass it in each time instead of storing redundant state.

A lot of what determines the right answer from the above has to do with the meaning of A B and C, and how independant they truly are.