Assigning A to B without B destroying all the allocations...

Started by
9 comments, last by King Mir 11 years, 2 months ago
So i have the situation, where i have a class that contains some allocated data (in this case an opengl vertex buffer allocated on the GPU). I have a grid of these objects, and i want to move them all to a bigger grid.
This means doing something along the lines of
bigGrid[x]=smallGrid[x]
that is,
1.A=B
2.~B()
Which is a problem since i have set my vertex buffer deallocate itself upon destruction. How would you recommend me to overcome this problem? Should i use the newish move semantics here?

Thanks

Oh and the original grid is destroyed after copying all of them. So im basically just moving them.

o3o

Advertisement
C++11 move assignment would work. Assuming your class implemented the move assignment operator you could invoke it with bigGrid[x] = std::move(smallGrid[x]); Another possibility is to go through and do bigGrid[x].swap(smallGrid[x]) if you implement an appropriate swap() function. How you would implement these would, of course, depend on what your class looks like.

The problem is that this copying happens in a template class, so i cant really use a swap...

I assume move will work for any type i pass in properly, but will i need to implement the operators required for moving in only the vertex buffer or both the vertex buffer and the class containing it?

I read that in some situations on some compilers i would only need to implement it in the vertex buffer but that "some" also means im not going to do that. If this is true, it would be quite inconvenient and i might forget to implement the move for some class in a hierarchy of classes with a vertexbuffer somewhere, resulting in a bug :/

Reference counting might be something i can trust but i think that would require allocating the reference count variable which isnt nice.

o3o

Never mind, i see you can make an assignment operator without making the parameter const :P

o3o

To use move semantics you need a move assignment operator on the class being moved, not the buffer.

You can also use a buffer of unique_ptr, but this will mean that the class is allocated seperately on the heap, instead of as a continious block in the buffer.

You can also use a buffer of unique_ptr, but this will mean that the class is allocated seperately on the heap, instead of as a continious block in the buffer.

Just want to point out that, depending on what you're doing and what else is in your class, this could be perfectly fine. You'll still pay the indirection, but in terms of cache performance it might be better than if you were to have a buffer allocated as part of your class (it sounds like its heap allocated anyhow, but its not entirely clear without the source).

There are two downsides I can think of with this approach.

  1. The lesser issue: You can't share the data. If you needed to share it, then use shared_ptr -- you pay the cost of the reference count, but its per-shared-object, not per-sharing-object; that is, the cost is amortized across the classes sharing the object, rather than scaling up with them.
  2. The bigger issue: You essentially lose the ability to ever take any copy without destroying the original, including passing by value.

Enabling move semantics is also fine, this is exactly the situation it was intended for.

throw table_exception("(? ???)? ? ???");

You can still pass copies of the object, just not compies of the unique_ptr. That's a non-issue.

How do you propose that you can take a copy of an object that contains a unique_ptr? You have to move (as in std::move) the unique_ptr's ownership to the copy, thus the unique_ptr in the original object no longer owns what it was pointing to, or even has its address.

The original object is not destructed, but it no longer holds that reference. Its incomplete and probably worthless.

[Edit] See my follow-up below.

throw table_exception("(? ???)? ? ???");

I read your post to imply that you can't pass a copy of the contained object. But for the container, you can explicitly deep-copy it in a wrapper class (which you often want to have anyway). Also, you generally want to avoid coping big containers anyway.

To follow up, you can't just copy a unique_ptr because its it doesn't support copy-construction or copy-assign -- to move the contents of one unique_ptr into another, you explicitly release the source ptr and explicitly reset the destination ptr to the source. Because this is not provided for, your own objects that contain unique_ptr objects cannot be trivially copied either.

To clarify the point I was trying to get across, to make your own unique_ptr-containing objects copyable you have to implement your copy constructor and copy-assignment operator to explicitly exchange ownership of those unique_ptr resources. However, doing that hides the ownership semantics from client code in a non-obvious way. A preferable solution would be for your own objects to either expose an explicit release/reset interface (like unique_ptr) themselves. There might also be ways to implement the object which neatly side-steps the transfer of ownership (such as by adding a level of indirection, or some kind of "wrapper" as King Mir might be suggesting), but I honestly have a hard time thinking of any use-cases that wouldn't simply be better off using shared_ptr in the first place.

throw table_exception("(? ???)? ? ???");

This topic is closed to new replies.

Advertisement