Thomas said:
You're tearing down a straw man. The intrusive smart pointer in the
FAQ does not not involve any inheritance, let alone "all your classes
inheriting from one class." I described it as "perfectly adequate,"
and it is, at least for non-expert users trying to learn about smart
pointers.
Let me describe why I don't think it is "perfectly adequate". Yes, it
will probably help you avoid leaks but breaks some other fundamental
rules. I'll ignore the possible implementation of operator=() using
swap and I'll assume Fred's constructor never throws.
======== the C++ FAQ writes:
class FredPtr;
class Fred {
public:
Fred() : count_(0) /*...*/ { } // All ctors set count_ to 0 !
...
private:
friend class FredPtr; // A friend class
unsigned count_;
// count_ must be initialized to 0 by all constructors
// count_ is the number of FredPtr objects that point at this
};
class FredPtr {
public:
Fred* operator-> () { return p_; }
Fred& operator* () { return *p_; }
FredPtr(Fred* p) : p_(p) { ++p_->count_; } // p must not be NULL
~FredPtr() { if (--p_->count_ == 0) delete p_; }
FredPtr(const FredPtr& p) : p_(p.p_) { ++p_->count_; }
FredPtr& operator= (const FredPtr& p)
{ // DO NOT CHANGE THE ORDER OF THESE STATEMENTS!
// (This order properly handles self-assignment)
// (This order also properly handles recursion, e.g., if a
Fred contains FredPtrs)
Fred* const old = p_;
p_ = p.p_;
++p_->count_;
if (--old->count_ == 0) delete old;
return *this;
}
private:
Fred* p_; // p_ is never NULL
};
==============================
Now I gave 3 rules. This breaks all 2 of them even as it is.It will
break all 3 if we ever decide to make it a template.
(1) If I'm allowed to separate out the implementation of FredPtr from
its interface then I could have a FredPtr with Fred being incomplete.
Change all the above into a template though (assuming that any class
that uses it has to have an accessible count_, either public or by
making the template its friend). (I'm assuming we don't have export).
That's where the option of having Fred derive from a class (that
provides the count) come in as we could get away with only having that
class complete except that we can't forwardly declare that Fred derives
from it so it would probably require a hack with casting, unless anyone
knows of a better way (other than using non-intrusive reference
counting, which is the solution).
(2) Above implementation calls delete. No option for custom deleter.
(3). There is no equivalent for a "const Fred", i.e. a FredConstPtr or
whatever you might want to call it.
Of course there is an alternative to template - you can use
auto-code-generators. In fact these are often a viable alternative, for
example on embedded systems where templates are not permitted.
I fear you may have missed my point. The OP asked for advice about
"the best way to make regular use of smart pointers." I responded that
Josuttis' book - still the best treatise on the Standard Library - "has
some good examples of using a non-intrusive smart pointer (using code
supplied in the book) that works well with standard containers." I
wasn't advocating Josuttis' smart pointer, I was advocating his
examples of how to use smart pointers with the standard containers,
which is what the OP was seeking advice on. Sorry if you
misunderstood.
I will get my Josuttis out at some point when I'm home and have a look.
It may be good if the FAQ were to a "light" version of shared_ptr for
those that can't use boost for whatever reason. Such a version could be
copied and pasted from one file.
I actually think it would be advantageous for boost themselves to have
a "light" version of some of their libraries (i.e. one file no
dependencies).
Of course, even then you might have a problem relating to any copyright
statements at the top of files - will the company allow you to state
that you got the source from "open-source" (i.e. that the company
themselves do not actually own the copyrigth to this particular file).