From what I understand I'm able to "disable" copying and assigning to my objects by defining private copy constructor and assignment operator:
class MyClass
{
private:
MyClass(const MyClass& srcMyClass);
MyClass& operator=(const MyClass& srcMyClass);
}
But what's the usage of this?
Is it considered a bad practice?
I would appreciate if you could describe the situation, in which it would be reasonable / useful to "disable" assignment and copy constructor in this way.
It's useful when it doesn't make sense for your object to be copied. It is definitely not considered bad practice.
For instance, if you have a class that represents a network connection, it's not meaningful to copy that object. Another time you may want a class to be noncopyable is if you had a class representing one player in a multiplayer game. Both these classes represent things that can't be copied in the real world, or that don't make sense to copy (a person, a connection).
Also, if you are trying to implement a Singleton, it's standard procedure to make the objects non-copyable.
Generally speaking any class that manages a resource should be none-copyable or have specialized copy semantics. The converse is true as well: any class that is non-copyable or needs specialized copy semantics is managing a resource. "Manage a resource" in the C++ lingua in practice means responsible for some space in memory, or for a connection to a network or a database, or a handle to a file, or an undo transaction, and so on.
Resource management captures quite a lot of examples. These are responsibilities that take a prefix operation, a suffix operation and possibly some action in between. Memory management, for example, involves acquiring a handle to a memory address which we'll manage, perhaps mess around with that memory, and finally release the handle (because if you love something, let it be free).
template<typename T>
struct memory {
memory(T const& val = T()) : p(new T(val)) { }
~memory() { delete p }
T& operator*() const { return *p; }
private:
T* p;
};
// ...
{
memory<int> m0;
*m0 = 3;
std::cout << *m0 << '\n';
}
This memory class is almost correct: it automatically acquires the underlying memory space and automatically releases it, even if an exception propagates some time after it acquired its resource. But consider this scenario:
{
memory<double> m1(3.14);
memory<double> m2(m1); // m2.p == m1.p (do you hear the bomb ticking?)
}
Because we didn't provide specialized copy semantics for memory, the compiler provides its own copy constructor and copy assignment. These do the wrong thing: m2 = m1 means m2.p = m1.p, such that the two pointers point at the same address. It's wrong because when m2 goes out of scope it frees its resource like a good responsible object, and when m1 then goes out of scope it too frees its resource, that same resource m2 has already freed, completing a double-delete -- a notorious undefined behaviour scenario. Moreover, in C++ it's extremely easy to make copies of an object without even noticing: a function taking its parameter by value, returning its parameter by value, or taking its parameter by reference but then calling another function which itself takes (or returns) its parameter by value ... It's easier to just assume that things will try to get copied.
All this to say that when a class' raison d'être is managing a resource then you immediately should know that you need to handle copying. You should decide
you support copying, whereas you decide what copying means: safe sharing of the resource, performing a deep copy of the underlying resource so there is no sharing whatsoever, or combining the two approaches as in copy-on-write or lazy copy. Whatever path you choose you will need to provide a specialized copy constructor and a copy assignment operator.
or you don't support any sort of copying of the resource, in which case you disable the copy constructor and the copy assignment operator.
I'd go so far and say that resource management is the only case where you disable copying or provide specialized copy semantics. This is just another perspective on The Rule of Three.
It's a pretty common practice. There are are lots of examples where copying isn't appropriate.
Let's say your object represents an open server-side socket (i.e. an incoming network connection); what would be the semantics of making a copy of that object?
when you are allowed to create instance of object only after checking like in the case of singleton u need private constructors. when constructor is called the object instance will be called and then there is no point in checking if there is another instance already. so what we do is call a member function of class from main and inside that member function check if another instance is already in the memory. if not constructor is called. else aborted.
check singleton classes or other protected classes where data of object has to be kept secured and should not be allowed to copy.
also check this : Singleton Class in C++
When you are trying to implement a singleton pattern it's perfectly acceptable to use a private constructor as it's meant to be only instantiated inside itself and from nowhere else.
Once invoked, the constructor can't be revoked. So the constructor is invoked only after checking if the singleton condition satisfied.
Related
I made a class for a function's argument to delegate its validation and also for function overloading purposes.
Throwing from constructor guarantees that the object will either be constructed in a valid state or will not be constructed at all. Hence, there is no need to introduce any checking member functions like explicit operator bool() const.
// just for exposition
const auto &certString = open_cert();
add_certificate(cert_pem{certString.cbegin(), certString.cend()}); // this will either throw
// or add a valid certificate.
// cert_pem is a temporary
However, there are issues which I don't see a appealing solution for:
Argument-validation class might itself be made non-persistent - to be used only for validation as a temporary object. But what about classes that are allowed to be persistent? That is living after function invocation:
// just for exposition
const auto &certString = open_cert();
cert_pem cert{certString.cbegin(), certString.cend()}; // allowed to throw
cert_pem moved = std::move(cert); // cert invalidated
cert_pem cert_invalid = std::move(cert); // is not allowed to throw
add_certificate(cert_invalid); // we lost the whole purpoce
I can see several ways to treat this without introducing state-checking (thus declaring a class stateful) functions:
Declare object "unusable" after move. - A really simple recipe for disaster
Declare move constructor and assignment operator deleted. Allow only copy - Resources might be very expensive to copy. Or even not possible if using a PIMPL idiom.
Use heap allocation when need an object to be persistent - this looks like most obvious. But has an unnecessary penalty on performance. Especially when some class has as members several such objects - there will be several memory allocations upon construction.
Here is a code example for 2):
/**
* Class that contains PEM certificate byte array.
* To be used as an argument. Ensures that input certificate is valid, otherwise throws on construction.
*/
class cert_pem final
{
public:
template <typename IterT>
cert_pem(IterT begin, IterT end)
: value_(begin, end)
{
validate(value_);
}
const std::vector<uint8_t>& Value() const noexcept(false)
{
return value_;
}
cert_pem (const cert_pem &) = default;
cert_pem & operator=(const cert_pem &) = default;
cert_pem (cert_pem &&) = delete;
cert_pem & operator=(cert_pem &&) = delete;
private:
/**
* \throws std::invalid_argument
*/
static void Validate(const std::vector<uint8_t>& value) noexcept(false);
static void ValidateNotEmpty(const std::vector<uint8_t>& value) noexcept(false);
private:
std::vector<uint8_t> value_;
};
Is there another way to handle this problem without these shortcomings? Or will I have to choose one of the above?
I think that with argument-validating classes a good way would be to not allow it to be persistent - only temporary object is allowed. But I am not sure if it is possible in C++.
You are trying to maintain two invariants at once, and their semantics are in conflict. The first invariant is the validity of the certificate. The second is for memory management.
For the first invariant, you decided that there can be no invalid constructed object, but for the second, you decided that the object can be either valid or unspecified†. This is only possible because the deallocation has a check somewhere.
There is no way around this: you either add a check for the first or you decouple the invariants. One way of decoupling them is to follow the design of std::lock_guard
cert c = open_cert(); // c is guaranteed to not have memory leaks and is movable
{
cert_guard cg{c}; // cg is guaranteed to be valid, but cg is non-movable
}
But wait, you might ask, how do you transfer the validity to another cert_guard?
Well, you can't.
That is the semantics you chose for the first invariant: it is valid exactly during the lifetime of the object. That is the entire point.
† Unspecified and invalid as far as the certificate is concerned.
The question aims to design a type such that:
an object of the type always satisfies a given invariant
an object of the type is "usable" as a non-temporary
The question then makes a leap from (2) to ask that the type be movable. But it need not be: copy and move operations could be defined as deleted. The question fails to motivate why the move operations are necessary. If that is a need, it comes from an unstated requirement. A non-movable class can be emplaced in a map, returned from a function, and used in many other ways. It admittedly can be more painful to use, but it can be used.
So that's one option that's not listed: define copy and move operations as deleted.
Otherwise, let's assume we do want:
an object of the type always satisfies a given invariant
the type is movable
This is not in conflict. Every copyable class is movable, and copying is a valid strategy here. Remember that move operations allow a "potentially smarter" copy, by allowing the source to be mutated. There are still two C++ objects, and it is still a logical copy, but with an assumption that the source won't be needed anymore in its current state (so you can steal from it!). There is no difference in the C++ interface, only in the totally unchecked documented behavior of the type after a move operation.
Defining move operations as deleted gives you a copyable class. This is your second option listed. Assigning from an xvalue (cert_pem moved = std::move(cert)) will still compile, but will not invalidate the source. It will still be considered movable by the language. The trade-off is as you note, copies can be expensive. Note that PIMPL authors can give their types copy operations, that's a choice they make about what the interface of the type should be, and the idiom doesn't prevent it.
The third choice is a version of the second. By putting values behind a shared_ptr, one can make an expensive-to-copy type cheap to copy. But we still rely on copy as the strategy for move.
The first choice amounts to weakening the invariant in (1). A moved-from object satisfying a different set of invariants than a normal object is very typical in C++. It is annoying, but in many cases it is the best we can do. When only one object can exist satisfying the invariant (think: non-null unique_ptr) the moved-from object must violate it.
The accepted answer amounts to my first option combined with delayed construction: define copy and move operations as deleted. Creating the guard can throw if the object was moved-from. The guard is just the type maintaining the invariant, and it is non-movable. We can delay its construction because such types are difficult to manage. We do that by keeping an object that knows enough about how to construct it. This strategy exists in other forms (emplace functions and piecewise_construct constructors to construct objects in their eventual place, factory functions to construct the object at will, etc.).
However, the description in the accepted answer leaves a bit to be desired, in my opinion. The desire is to maintain the invariant while being movable (this is assumed). Being movable doesn't require that the moved-from object satisfy an invariant or be unspecified. That's a choice the author of the type makes, and what choices are available is exactly the question, by my reading of it. Although the example given only implicated memory, and the first answer mentioned memory, my reading of the question was more general: maintaining invariants in movable classes.
Knowing that all copyable classes are movable, that move is a "smart" copy, and that there are two objects in and after a move operation will help in understanding why there's such limited options here. One has to leave that source object in some state.
My advice is to embrace the radioactive moved-from object. That's the approach in the standard library, and defaulted move operations will obey that more often than not. For such types, there must be some "empty" state for moved-from objects, so all types are effectively optional and a default constructor can also be defined to get an object in that empty state.
The rule of 5 states that if a class has a user-declared destructor, copy constructor, copy assignment constructor, move constructor, or move assignment constructor, then it must have the other 4.
But today it dawned on me: when do you ever need a user-defined destructor, copy constructor, copy assignment constructor, move constructor, or move assignment constructor?
In my understanding, implicit constructors / destructors work just fine for aggregate data structures. However, classes which manage a resource need user-defined constructors / destructors.
However, can't all resource managing classes be converted into an aggregate data structure using a smart pointer?
Example:
// RAII Class which allocates memory on the heap.
class ResourceManager {
Resource* resource;
ResourceManager() {resource = new Resource;}
// In this class you need all the destructors/ copy ctor/ move ctor etc...
// I haven't written them as they are trivial to implement
};
vs
class ResourceManager {
std::unique_ptr<Resource> resource;
};
Now example 2 behaves exactly the same as example 1, but all the implicit constructors work.
Of course, you can't copy ResourceManager, but if you want a different behavior, you can use a different smart pointer.
The point is that you don't need user-defined constructors when smart pointers already have those so implicit constructors work.
The only reason I would see to have user-defined constructors would be when:
you can't use smart pointers in some low-level code (I highly doubt this is ever the case).
you are implementing the smart pointers themselves.
However, in normal code I don't see any reason to use user-defined constructors.
Am I missing something here?
The full name of the rule is the rule of 3/5/0.
It doesn't say "always provide all five". It says that you have to either provide the three, the five, or none of them.
Indeed, more often than not the smartest move is to not provide any of the five. But you can't do that if you're writing your own container, smart pointer, or a RAII wrapper around some resource.
However, in normal code I don't see any reason to use user-defined constructors.
User provided constructor allows also to maintain some invariant, so orthogonal with rule of 5.
As for example a
struct clampInt
{
int min;
int max;
int value;
};
doesn't ensure that min < max. So encapsulate data might provide this guaranty.
aggregate doesn't fit for all cases.
when do you ever need a user-defined destructor, copy constructor, copy assignment constructor, move constructor, or move assignment constructor?
Now about rule of 5/3/0.
Indeed rule of 0 should be preferred.
Available smart-pointers (I include container) are for pointers, collections or Lockables.
But resources are not necessary pointers (might be handle hidden in an int, internal hidden static variables (XXX_Init()/XXX_Close())), or might requires more advanced treatment (as for database, an auto commit at end of scope or rollback in case of exceptions) so you have to write your own RAII object.
You might also want to write RAII object which doesn't really own resource, as a TimerLogger for example (write elapsed time used by a "scope").
Another moment when you generally have to write destructor is for abstract class, as you need virtual destructor (and possible polymorphic copy is done by a virtual clone).
The full rule is, as noted, the Rule of 0/3/5; implement 0 of them usually, and if you implement any, implement 3 or 5 of them.
You have to implement the copy/move and destruction operations in a few cases.
Self reference. Sometimes parts of an object refer to other parts of the object. When you copy them, they'll naively refer to the other object you copied from.
Smart pointers. There are reasons to implement more smart pointers.
More generally than smart pointers, resource owning types, like vectors or optional or variants. All of these are vocabulary types that let their users not care about them.
More general than 1, objects whose identity matters. Objects which have external registration, for example, have to reregister the new copy with the register store, and when destroyed have to deregister themselves.
Cases where you have to be careful or fancy due to concurrency. As an example, if you have a mutex_guarded<T> template and you want them to be copyable, default copy doesn't work as the wrapper has a mutex, and mutexes cannot be copied. In other cases, you might need to guarantee the order of some operations, do compare and sets, or even track or record the "native thread" of the object to detect when it has crossed thread boundaries.
Having good encapsulated concepts that already follow the rule of five ensures indeed that you have to worry less about it. That said if you find yourselves in a situation where you have to write some custom logic, it still holds. Some things that come to mind:
Your own smart pointer types
Observers that have to unregister
Wrappers for C-libraries
Next to that, I find that once you have enough composition, it's no longer clear what the behavior of a class will be. Are assignment operators available? Can we copy construct the class? Therefore enforcing the rule of five, even with = default in it, in combination with -Wdefaulted-function-deleted as error helps in understanding the code.
To look closer at your examples:
// RAII Class which allocates memory on the heap.
class ResourceManager {
Resource* resource;
ResourceManager() {resource = new Resource;}
// In this class you need all the destructors/ copy ctor/ move ctor etc...
// I haven't written them as they are trivial to implement
};
This code could indeed nicely be converted to:
class ResourceManager {
std::unique_ptr<Resource> resource;
};
However, now imagine:
class ResourceManager {
ResourcePool &pool;
Resource *resource;
ResourceManager(ResourcePool &pool) : pool{pool}, resource{pool.createResource()} {}
~ResourceManager() { pool.destroyResource(resource);
};
Again, this could be done with a unique_ptr if you give it a custom destructor.
Though, if your class now stores a lot of resources, are you willing to pay the extra cost in memory?
What if you first need to take a lock before you can return the resource to the pool to be recycled? Will you take this lock only once and return all resources or 1000 times when you return them 1-by-1?
I think your reasoning is correct, having good smart pointer types makes the rule of 5 less relevant. However, as indicated in this answer, there are always cases to be discovered where you'll need it. So calling it out-dated might be a bit too far, it's a bit like knowing how to iterate using for (auto it = v.begin(); it != v.end(); ++it) instead of for (auto e : v). You no longer use the first variant, up to the point, you need to call 'erase' where this suddenly does become relevant again.
The rule is often misunderstood because it is often found oversimplified.
The simplified version goes like this: if you need to write at least one of (3/5) special methods then you need to write all of the (3/5).
The actual, useful rule: A class that is responsible with manual ownership of a resource should: deal exclusively with managing the ownership/lifetime of the resource; in order to do this correctly it must implement all 3/5 special members. Else (if your class doesn't have manual ownership of a resource) you must leave all special members implicit or defaulted (rule of zero).
The simplified versions uses this rhetoric: if you find yourself in need to write one of the (3/5) then most likely your class manually manages the ownership of a resource so you need to implement all (3/5).
Example 1: if your class manages the acquisition/release of a system resource then it must implement all 3/5.
Example 2: if your class manages the lifetime of a memory region then it must implement all 3/5.
Example 3: in your destructor you do some logging. The reason you write a destructor is not to manage a resource you own so you don't need to write the other special members.
In conclusion: in user code you should follow the rule of zero: don't manual manage resources. Use RAII wrappers that already implement this for you (like smart pointers, standard containers, std::string, etc.)
However if you find yourself in need to manually manage a resource then write a RAII class that is responsible exclusively with the resource lifetime management. This class should implement all (3/5) special members.
A good read on this: https://en.cppreference.com/w/cpp/language/rule_of_three
There is a scenario where when I get a reply from an API via a callback and I need to move the objects in reply to my class member variable to proceed processing. Will the following code achieve the objective:
class Obj {
public:
Obj (unit32_t& x, std::string& x, int& data)
{}
... lot of variable .. no pointers
};
class A
{
public:
A(Obj& r):pvtmemVarTyOfObj(0,"",0) {}
private:
Obj pvtmemVarTyOfObj ;
};
void A::GetResponse(Obj&& resp)
{
//pvtmemVarTyOfObj = std::forward<Obj>(std::move(resp));
pvtmemVarTyOfObj = std::move(resp);
}
Will this code actually move resp to pvtmemVarTyOfObj member variable? Just to ensure that no-copy should be done as it will lead to huge processing cost?
Just use std::move(resp) here. You know for sure that resp is a rvalue reference, hence you always want to move.
If Obj was a template parameter, you would need to use std::forward<Obj>(resp). Because in this case Obj&& would be an universal reference, which could be an rvalue or lvalue reference and std::forward deduces whether resp can be moved from or not.
Whether in the end a copy or move is performed depends on the assignment operators of pvtmemVarTyOfObj's class. If it does only contain plain data and no allocated resources (e.g. via pointers), move can't provide any benefits.
The move constructor, and the move assignment operator, only do what they are
programmed to do, by you the programmer. Technically, they can be designed to
do anything, but that is not advisable.
Rather, they should be designed to make the target object indistinguishable from
the source object, while leaving the source object in a valid state.
If you do not provide a move operation yourself, one will be synthesized by means
of member-wise move.
Move for fundamental types such as int, just do a copy.
Move for many library-types, such as std::vector, std::string and
std::unique_ptr, are designed, by the library-implementor, to transfer
ownership of resources, such as memory.
This means, that for a stack-allocated POD, there just is no way to avoid
a copy. If the operation is performance-critical, and a copy is just unacceptable,
the solution could be to not have a stack-allocated POD in the first place, for
example by sticking the object in a std::unique_ptr.
Another option could be to only ever have the target object, and let others 'access'
that object through pointers or references.
I'm trying to understand the purpose of overloading some operators in C++.
Conceptually, an assignment statement can be easily implemented via:
Destruction of the old object followed by copy construction of the new object
Copy construction of the new object, followed by a swap with the old object, followed by destruction of the old object
In fact, often, the copy-and-swap implementation is the implementation of assignment in real code.
Why, then, does C++ allow the programmer to overload the assignment operator, instead of just performing the above?
Was it intended to allow a scenario in which assignment is faster than destruction + construction?
If so, when does that happen? And if not, then what use case was it intended to support?
1) Reference counting
Suppose you have a resource that is ref-counted and it is wrapped in objects.
void operator=(const MyObject& v) {
this->resource = refCount(v->resource);
}
// Here the internal resource will be copied and not the objects.
MyObject A = B;
2) Or you just want to copy the fields without fancy semantics.
void operator=(const MyObject& v) {
this->someField = v->someField;
}
// So this statement should generate just one function call and not a fancy
// collection of temporaries that require construction destruction.
MyObject A = B;
In both cases the code runs much faster. In the second case the effect is similar.
3) Also what about types...
Use the operator to handle assigning other types to your type.
void operator=(const MyObject& v) {
this->someField = v->someField;
}
void operator=(int x) {
this->value = x;
}
void operator=(float y) {
this->floatValue = y;
}
first, note that "Destruction of the old object followed by copy construction of the new object" is not exception safe.
but re "Copy construction of the new object, followed by a swap with the old object, followed by destruction of the old object", that's the swap idiom for implementing an assignment operator, and it's exception safe if done correctly.
in some cases a custom assignment operator can be faster than the swap idiom. for example, direct arrays of POD type can't really be swapped except by way of lower level assignments. so there for the swap idiom you can expect an overhead proportional to the array size.
however, historically there wasn't much focus on swapping and exception safety.
bjarne wanted exceptions originally (if i recall correctly), but they didn't get into the language until 1989 or thereabouts. so the original c++ way of programming was more focused on assignments. to the degree that a failing constructor signalled its failure by assigning 0 to this… i think, that in those days your question would not have made sense. it was just assignments all over.
typewise, some objects have identity, and others have value. it makes sense to assign to value objects, but for identity objects one typically wants to limit the ways that the object can be modified. while this doesn't require the ability to customize copy assignment (only to make it unavailable), with that ability one doesn't need any other language support.
and i think likewise for any other specific reasons one can think of: probably no such reason really requires the general ability, but the general ability is sufficient to cover it all, so it lowers the overall language complexity.
a good source to get more definitive answer than my hunches, recollections and gut feelings, is bjarne's "the design and evolution of c++" book.
probably the question has a definitive answer there.
Destruction of the old object, followed by copy construction of
the new, will not usually work. And the swap idiom is
guaranteed not to work unless the class provides a special swap
function—std::swap uses assignment in its unspecialized
implementation, and using it directly in the assignment operator
will lead to endless recursion.
And of course, the user may want to do something special, e.g.
make the assignment operator private, for example.
And finally, what is almost certainly an overruling reason: the
default assignment operator has to be compatible with C.
Actually, after seeing juanchopanza's answer (which was deleted), I think I ended up figuring it out myself.
Copy-assignment operators allow classes like basic_string to avoid allocating resources unnecessarily when they can re-use them (in this case, memory).
So when you assign to a basic_string, an overloaded copy assignment operator would avoid allocating memory, and would just copy the string data directly to the existing buffer.
If the object had to be destroyed and constructed again, the buffer would have to be reallocated, which would be potentially much more costly for a small string.
(Note that vector could benefit from this too, but only if it knew that the elements' copy constructors would never throw exceptions. Otherwise it would need to maintain its exception safety and actually perform a copy-and-swap.)
It allows you to use assignment of other types as well.
You could have a class Person with an assignment operator that assigns an ID.
But besides that, you don't always want to copy all the members as they are.
The default assignment only does a shallow copy.
For example, if the class contains pointers, or locks, you dont always want to copy them from the other object.
Usually when you have pointers you want to use a deep copy, and maybe create a copy of the object that the pointers are pointed to.
And if you have locks, you want them to be specific to the object, and you don't want to copy their state from the other object.
It is actually a common practice to provide your own copy constructor and assignment operator if your class holds pointers as members.
I have used it often as a conversion constructor but with already existing objects. i.e assigning member variable type, etc to an object.
Just wanted opinions on a design question. If you have a C++ class than owns other objects, would you use smart pointers to achieve this?
class Example {
public:
// ...
private:
boost::scoped_ptr<Owned> data;
};
The 'Owned' object can't be stored by value because it may change through the lifetime of the object.
My view of it is that on the one side, you make it clear that the object is owned and ensure its deletion, but on the flipside, you could easily just have a regular pointer and delete it in the destructor. Is this overkill?
Follow up: Just wanted to say thanks for all your answers. Thanks for the heads-up about auto_ptr leaving the other object with a NULL pointer when the whole object is copied, I have used auto_ptr extensively but had not thought of that yet. I make basically all my classes boost::noncopyable unless I have a good reason, so there's nothing to worry about there. And thanks also for the information on memory leaks in exceptions, that's good to know too. I try not to write things which could cause exceptions in the constructor anyway - there are better ways of doing that - so that shouldn't be a problem.
I just had another question though. What I wanted when I asked this question was to know whether anyone actually did this, and you all seem to mention that it's a good idea theoretically, but no one's said they actually do it. Which surprises me! Certainly one object owning a pointer to another is not a new idea, I would have expected you all would have done it before at some point. What's going on?
scoped_ptr is very good for this purpose. But one has to understand its semantics. You can group smart pointers using two major properties:
Copyable: A smart pointer can be copied: The copy and the original share ownership.
Movable: A smart pointer can be moved: The move-result will have ownership, the original won't own anymore.
That's rather common terminology. For smart pointers, there is a specific terminology which better marks those properties:
Transfer of Ownership: A smart pointer is Movable
Share of Ownership: A smart pointer is copyable. If a smart pointer is already copyable, it's easy to support transfer-of-ownership semantic: That then is just an atomic copy & reset-of-original operation, restricting that to smart pointers of certain kinds (e.g only temporary smart pointers).
Let's group the available smart pointers, using (C)opyable, and (M)ovable, (N)either:
boost::scoped_ptr: N
std::auto_ptr: M
boost::shared_ptr: C
auto_ptr has one big problem, in that it realizes the Movable concept using a copy constructor. That is because When auto_ptr was accepted into C++, there wasn't yet a way to natively support move semantics using a move constructor, as opposed to the new C++ Standard. That is, you can do the following with auto_ptr, and it works:
auto_ptr<int> a(new int), b;
// oops, after this, a is reset. But a copy was desired!
// it does the copy&reset-of-original, but it's not restricted to only temporary
// auto_ptrs (so, not to ones that are returned from functions, for example).
b = a;
Anyway, as we see, in your case you won't be able to transfer the ownership to another object: Your object will in effect be non-copyable. And in the next C++ Standard, it will be non-movable if you stay with scoped_ptr.
For implementing your class with scoped_ptr, watch that you either have one of these two points satisfied:
Write an destructor (even if it's empty) in the .cpp file of your class, or
Make Owned a completely defines class.
Otherwise, when you would create an object of Example, the compiler would implicitly define a destructor for you, which would call scoped_ptr's destructor:
~Example() { ptr.~scoped_ptr<Owned>(); }
That would then make scoped_ptr call boost::checked_delete, which would complain about Owned being incomplete, in case you haven't done any of the above two points. If you have defined your own dtor in the .cpp file, the implicit call to the destructor of scoped_ptr would be made from the .cpp file, in which you could place the definition of your Owned class.
You have that same problem with auto_ptr, but you have one more problem: Providing auto_ptr with an incomplete type is undefined behavior currently (maybe it will be fixed for the next C++ version). So, when you use auto_ptr, you have to make Owned a complete type within your header file.
shared_ptr doesn't have that problem, because it uses a polymorphic deleter, which makes an indirect call to the delete. So the deleting function is not instantiated at the time the destructor is instantiated, but at the time the deleter is created in shared_ptr's constructor.
It's a good idea. It helps simplify your code, and ensure that when you do change the Owned object during the lifetime of the object, the previous one gets destroyed properly.
You have to remember that scoped_ptr is noncopyable, though, which makes your class noncopyable by default until/unless you add your own copy constructor, etc. (Of course, using the default copy constructor in the case of raw pointers would be a no-no too!)
If your class has more than one pointer field, then use of scoped_ptr actually improves exception safety in one case:
class C
{
Owned * o1;
Owned * o2;
public:
C() : o1(new Owned), o2(new Owned) {}
~C() { delete o1; delete o2;}
};
Now, imagine that during construction of a C the second "new Owned" throws an exception (out-of-memory, for example). o1 will be leaked, because C::~C() (the destructor) won't get called, because the object has not been completely constructed yet. The destructor of any completely constructed member field does get called though. So, using a scoped_ptr instead of a plain pointer will allow o1 to be properly destroyed.
It's not overkill at all, it's a good idea.
It does require your class clients to know about boost, though. This may or may not be an issue. For portability you could consider std::auto_ptr which does (in this case) the same job. As it's private, you don't have to worry about other people attempting to copy it.
Using the scoped_ptr is a good idea.
Keeping and manually destroying the pointer is not as simple as you think. Especially if there is more than one RAW pointer in your code. If exception safety and not leaking memory is a priority then you need a lot of extra code to get it correct.
For a start you have to make sure you correctly define all four default methods. This is because the compiler generated version of these methods are fine for normal objects (including smart pointers) but in the normal case will lead to problems with pointer handling (Look for the Shallow Copy Problem).
Default Constructor
Copy Constructor
Assignment Operator
Destructor
If you use the scoped_ptr then you don't need to worry about any of those.
Now if you have more than one RAW pointer in your class (or other parts of your constructor can throw) . You have to EXPLICITLY deal with exceptions during construction and destruction.
class MyClass
{
public:
MyClass();
MyClass(MyClass const& copy);
MyClass& operator=(MyClass const& copy);
~MyClass();
private
Data* d1;
Data* d2;
};
MyClass::MyClass()
:d1(NULL),d2(NULL)
{
// This is the most trivial case I can think off
// But even it looks ugly. Remember the destructor is NOT called
// unless the constructor completes (without exceptions) but if an
// exception is thrown then all fully constructed object will be
// destroyed via there destructor. But pointers don't have destructors.
try
{
d1 = new Data;
d2 = new Data;
}
catch(...)
{
delete d1;
delete d2;
throw;
}
}
Look how much easier a scopted_ptr is.
Scoped pointers are good for exactly this because they ensure that the objects get deleted without you having to worry about it as a programmer. I think that this is a good use of scoped ptr.
I find that a good design strategy in general is to avoid freeing memory manually as much as possible and let your tools (in this case smart pointers) do it for you. Manual deletion is bad for one main reason as I can see it, and that is that code becomes difficult to maintain very quickly. The logic for allocation and deallocation of memory is often separate in the code, and that leads to the complementary lines not being maintained together.
I don't think this is overkill, this documents the semantics of the member much better than having a raw pointer and is less error prone.
Why an overkill? boost::scoped_ptr is very easy to optimize, and I bet the resulting machine code would be the same as if you manually delete the pointer in the destructor.
scoped_ptr is good - just use it :)