9

I have a C++ class that implements reference-counting and I want all users of this class to inherit from this class only virtually so that no object ends up with more than one reference counter.

I'd like some way to assert this requirment either during compile time or at least during runtime.

Is there a way to achieve that?

sharptooth
  • 167,383
  • 100
  • 513
  • 979

3 Answers3

9

Something like this?

struct RefCounter {
    template <typename T>
    RefCounter(T *) {
        BOOST_STATIC_ASSERT(boost::is_virtual_base_of<RefCounter, T>);
    }
};

struct GoodClass : virtual RefCounter {
    GoodClass() : RefCounter(this) {}
};

struct BadClass : RefCounter {
    BadClass() : RefCounter(this) {}
};

It's a shame about needing to pass this to the constructor, though, to capture the derived type. And of course a wilfully obtuse user could subvert it by passing something other than this.

Steve Jessop
  • 273,490
  • 39
  • 460
  • 699
  • You could use CRTP instead of `this`, but I think your solution is better. – sbi Dec 19 '11 at 10:05
  • @sbi: I considered that, but then `RefCounter` and `RefCounter` would be different classes, and so even if they are virtual you could still end up with multiple refcounter base class subobjects. – Steve Jessop Dec 19 '11 at 10:07
  • Will this work if a class inherits from two bases and one base inherits from the `RefCounter` virtually and the other inherits non-virtually? – sharptooth Dec 19 '11 at 10:09
  • @sharptooth: the "other" class should trigger the assert, so you wouldn't even get as far as the class that inherits from both of them. – Steve Jessop Dec 19 '11 at 10:10
  • The solution looks great, except one thing - it requires to actually change each class that already inherits from `RefCounter` and that's quite a lot of work. – sharptooth Dec 19 '11 at 10:12
  • @sharptooth: I think there is a problem, though, with a class that has two bases: `GoodClass` and `RefCounter`. Then `RefCounter` is a virtual base of the class, but is *also* a non-virtual base. – Steve Jessop Dec 19 '11 at 10:15
5

I think wrapping the class would be the simplest option. Rather than directly inheriting from RefCounter create an intermediary class.

struct RefCounterVirtPrivate_
{
    int count;

    RefCounterVirt()
        : count( 0 )
    { }
};

struct RefCounter : public virtual RefCounterVirtPrivate_
{
};

struct A : public RefCounter { };
struct B : public RefCounter { };
struct C : public A, public B { };

Then everything can inherit from RefCounter without any need to care about virtual inheritence. You don't even have to change any existing code -- virtual inheritence of RefCounter itself should be harmless.

This of course doesn't guarantee people don't inherit from RefCounterVirtPrivate_ directly, but that is why I've given it an obvious name. It's harder to accidentally do this than forget a virtual keyword.

edA-qa mort-ora-y
  • 30,295
  • 39
  • 137
  • 267
0

It should be possible with some trickery, but consider the implications: you are setting the policy that the object needs to immediately self-destruct if its reference counter ever reaches zero.

Depending on your application, you might want to leave the exact time when it calls delete this; to the implementation of the object, i.e. only have add_ref() and release() abstract functions in the base class (which makes the one concrete implementation show up in all interface vtables with proper thunking) and place the burden of maintaining the reference count on the concrete class.

Simon Richter
  • 28,572
  • 1
  • 42
  • 64
  • Actually this class does `delete this` itself at all times, so I could do the checks in `addref()`/`release()`. So the trickery is welcome. – sharptooth Dec 19 '11 at 10:04