Question

This question could also have been entitled "How to do reference-counting without ATL". Some similar questions have been asked here and here, but in the former a different question was answered and in both cases, ATL is involved. My question is more general to C++ and not so much about COM.

Suppose we have an IUnknown "interface", like so:

class IUnknown
{
public:
    virtual ULONG AddRef() = 0;
    virtual ULONG Release() = 0;
    virtual ULONG QueryInterface(void * iid, void **ppv) = 0;
};

...and let's throw in a few other interfaces that are part of a fictional SDK:

class IAnimal : public IUnknown
{
public:
    virtual IAnimal** GetParents() = 0;
};

class IMammal : public IAnimal
{
public:
    virtual ULONG Reproduce() = 0;
};

Since I am going to be implementing several animals and mammals, I would rather not copy-paste the AddRef() and Release() implementations in every class, so I wrote UnknownBase:

class UnknownBase : public IUnknown
{
public:
    UnknownBase()
    {
        _referenceCount = 0;
    }
    ULONG AddRef()
    {
        return ++_referenceCount;
    }
    ULONG Release()
    {
        ULONG result = --_referenceCount;
        if (result == 0)
        {
            delete this;
        }
        return result;
    }
private:
    ULONG _referenceCount;
};

...so that I may use it to, say, implement a Cat:

class Cat : public IMammal, UnknownBase
{
public:
    ULONG QueryInterface(void *, void**);

    IAnimal** GetParents();
    ULONG Reproduce();
};

ULONG Cat::QueryInterface(void * iid, void **ppv)
{
    // TODO: implement
    return E_NOTIMPL;
}

IAnimal** Cat::GetParents()
{
    // TODO: implement
    return NULL;
}

ULONG Cat::Reproduce()
{
    // TODO: implement
    return 0;
}

...however, the compiler disagrees:

c:\path\to\farm.cpp(42): error C2259: 'Cat' : cannot instantiate abstract class
          due to following members:
          'ULONG IUnknown::AddRef(void)' : is abstract
          c:\path\to\iunknown.h(8) : see declaration of 'IUnknown::AddRef'
          'ULONG IUnknown::Release(void)' : is abstract
          c:\path\to\iunknown.h(9) : see declaration of 'IUnknown::Release'

What am I missing?

Was it helpful?

Solution

This does not require changing the interface definitions:

template<class I>
class UnknownBase : public I
{
    ...
}

class Cat : public UnknownBase<IMammal>
{
    ...
}

OTHER TIPS

You must inherit using virtual base classes. Change:

class IAnimal : public IUnknown --> class IAnimal : public virtual IUnknown; and

class UnknownBase : public IUnknown --> class UnknownBase : public virtual IUnknown

You can use a class template that allows you to specify the base class as a template argument. This will eliminate the need to implement a final override function in the derived class.

You also need to make sure that you declare the member functions with the correct calling convention, return type and parameter types.

template<typename Base>
class UnknownImpl : public Base
{
public:

    UnknownImpl() : _referenceCount(0)
    {
    }

    ULONG STDMETHODCALLTYPE AddRef()
    {
        return InterlockedIncrement(&_referenceCount);
    }

    ULONG STDMETHODCALLTYPE Release()
    {
        ULONG result = InterlockedDecrement(&_referenceCount);
        if (result == 0)
        {
            delete this;
        }
        return result;
    }

private:

    ULONG _referenceCount;
};


class Cat : public UnknownImpl<IMammal>
{
public:
    HRESULT STDMETHODCALLTYPE QueryInterface(REFIID, void**);

    IAnimal** GetParents();
    ULONG Reproduce();
};
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top