Reputation: 473
I have a couple classes in my code, of which I need to copy objects.
However, some of those classes use data which I would like to skip when copying, like owner pointers.
So far, the only way I've found of doing this either to avoid copying entirely and construct a completely new object by hand every time I need to copy one, or wrapping the owner pointer in a private, non-copyable struct, like this:
class MyClass {
// non-copyable owner
struct Owner {
Owner() = default;
~Owner() = default;
Owner(const Owner& o) = delete;
Owner& operator=(const Owner& o) = delete;
SomeOtherClass* pointer = nullptr;
};
Owner owner = Owner();
}
However, doing it this way seems a little verbose.
(Note I would not like to use std::unique_ptr
as I do not wish to deconstruct the owner when the object is deconstructed)
Is there a more compact / efficient / readable way of doing this?
Edit: the invalidating of default
marked constructors etc. stemmed from me not initializing owner with a default value. Silly.
EDIT 2: Perhaps I should clear some things up: The owner pointer should point to the object that owns the MyClass
, as a way for the owned object to refer to its owner. Not the other way around. That is why I wish to not copy the pointer, as an object with a different owner that is supposed to copy this object should not change which object it is owned by.
The accepted answer enables me to do just that. I hope this cleared up some of the confusion.
Upvotes: 1
Views: 168
Reputation: 11317
An owner that does not own the resource? Sounds like you are lying in your code, if I would see this in code, I would think you have a bug.
That said, unique_ptr is really the best way of dealing with it. You could give it a custom destructor to do the closing of the resource (like closing a file handle). From a readable perspective, this will be the easiest to understand instead of having your custom class.
I don't know how you define efficient, however, assuming runtime efficiency, their ain't any effect.
The most compact way of writing will be inheriting from a NonCopyable class, boost has one, however it is very easy to write if you don't want that dependency.
From the readable perspective, I actually would go with a variation of your orginal implementation, which is: follow the rule of 5: specify Ctor, copy Ctor, move ctor, copy assign and move assign in your class.
Upvotes: 0
Reputation: 275330
Copy (and move) have required semantics due to how elision works, and to a lesser degree how containers work.
Well behaved value types shouldn't behave surprisingly differently if a copy or move is elided. They should also behave well when stored in a std::vector
.
Your description seems a lot like how auto_ptr
was hacked into being a unique_ptr
before we had language support for moving. It was a clever idea that turned into a monster of a bad one, and auto_ptr
is one of the few deprecated features of the standard library because of how much of a bad idea it turned out to be.
Split the "state" of your types from its identity. Ownership is an identity feature, for example, while height is a state feature.
Store the state in a struct
within your type.
The identity either store within your type, or in another substruct. The identity substruct should have =delete
copy/move operations.
Provide a way to create a new object with a new identity but the same state. If the object type is Foo, and its state is FooState, then you'd have a Foo(FooState)
explicit constructor.
Have a method that produces a state-wise copy of a Foo
.
Do not permit Foo
to be copied. The semantics of copying a Foo
where the identity changes (is cleared) after a copy are not healthy copy semantics. A Foo
should be non-copyable.
Move semantics are possible; in such a case, you'd report to your owner(s) that your location is changing (through some interface) -- like:
if (owner)
owner->child_moving( &old, this );
for( auto* child:children )
child->parent_moving( &old, this );
which permits parent/children to update their owner/child pointers.
Unless you do that kind of thing, you don't want to pretend to have value semantics; delete your copy/move operations instead of implementing insane ones.
Upvotes: 2
Reputation: 11910
Maybe something like a factory method at least evades the copying part in compile time?
// a method of MyClass
Owner * MyOwner()
{
// check if created
if(uidMap.find(parentObjectUid )==uidMap.end())
{
// create if needed
uidMap.insert(std::pair<size_t,Owner *>(parentObjectUid ,new Owner()));
// you can even use shared/unique ptr instead of new Owner()
// if you want automatic garbage
return uidMap[parentObjectUid ];
}
else
{
return uidMap[parentObjectUid];
}
}
then creating uid may need an unwanted singletone or similar global synchronized production. So as long as MyClass is copied with same uid, both copies will have same single object of Owner.
MyClass a;
MyClass b;
Owner * o = a.MyOwner(); // => creates
Owner * p = a.MyOwner(); // => uses
Owner * q = b.MyOwner(); // => creates another
a = b; // doesn't copy anything Owner
a unique id could be an incremented 64bit integer.
Upvotes: 1
Reputation: 5729
You can use a std::unique_ptr
with a deleter that does nothing.
template<typename T>
struct nop_deleter<T> {void operator()(T*){};};
class MyClass {
std::unique_ptr<SomeType, nop_deleter<SomeType>> owner;
};
The "owned" object will then not be deconstructed. (At this point it's not really owned anymore, but that's just me being pedantic).
If you want it to be a tad more readable you can always alias the type:
template<typename T>
struct nop_deleter<T> {void operator()(T*){};};
template<typename T>
using Owner = std::unique_ptr<T, nop_deleter<T>>;
class MyClass {
Owner<SomeType> owner;
};
Upvotes: 3