Is it okay to implement reference counting through composition?
Most common re-usable reference counted objects use private inheritance to implement re-use. I'm not a huge fan of private inheritance, and I'm curious if this is an acceptable way of handling things:
class ReferenceCounter {
std::size_t * referenceCount;
public:
ReferenceCounter()
: referenceCount(NULL) {};
ReferenceCounter(ReferenceCounter& other)
: referenceCount(other.referenceCount) {
if (!referenceCount) {
referenceCount = new std::size_t(1);
other.referenceCount = referenceCount;
} else {
++(*referenceCount);
}
};
ReferenceCounter& operator=(const ReferenceCounter& other) {
ReferenceCounter temp(other);
swap(temp);
return *this;
};
void swap(Referen开发者_如何学CceCounter& other) {
std::swap(referenceCount, other.referenceCount);
};
~ReferenceCounter() {
if (referenceCount) {
if (!*referenceCount)
delete referenceCount;
else
--(*referenceCount);
}
};
operator bool() const {
return referenceCount && (*referenceCount != 0);
};
};
class SomeClientClass {
HANDLE someHandleThingy;
ReferenceCounter objectsStillActive;
public:
SomeClientClass() {
someHandleThingy = RegCreateKeyEx(...);
}
~SomeClientClass() {
if (objectsStillActive)
return;
RegCloseKey(someHandleThingy);
};
};
or are there subtle problems with this I'm not seeing?
EDIT
I'm not super duper concerned with this particular implementation (it probably has bugs -- I'm going to spend some time looking at shared_ptr's innards before using something like this in production code) -- I'm just concerned if in general there is a specific reason reusable reference counting goodies always seem to be implemented using inheritance rather than composition.You have to remember to copy the counter when copying the handle. You might prefer not to pass operating system types into templates, but I think safety here requires inheritance. (Not inheritance from HANDLE
, though.)
HANDLE
might also be something of a special case because it's POD. Essentially you have a pointer of type besides T*
.
I see the motivation that you want something besides delete
to happen when the count goes to zero. An adaptation of smart_ptr
would probably work, and you might not be that far from just that.
I don't think this has any merit. Reference counting only makes sense for shared objects. The goal is to save on heap allocations and/or copying of these among other things. You, in fact, implemented sort of copy counter. But even that is not useful since it does not provide any interface to query the counter value. Might I suggest revisiting boost::intrusive
?
You are in fact implementing reference counting for the HANDLE
outside of the HANDLE
class... which is damn close to a shared_ptr
.
Using composition for implementing reference counting is fine, though it would be better if your reference counting object ReferenceCounter
was to own the class HANDLE
instance... Much safer in usage, and cheaper to reuse since you only implement the deletion routine once instead of doing it in all of your constructors (arg).
The single valid reason for using private inheritance is Empty Base Optimization
, all the other cases can be dealt with composition which is much better in terms of coupling, so it's unlikely that they have good reasons to do so, more probably they did it out of misguidance or laziness.
精彩评论