C++ 如何在许多类中重用接口实现?

C++ 如何在许多类中重用接口实现?,c++,interface,abstract-class,reusability,C++,Interface,Abstract Class,Reusability,这个问题也可以题为“如何在没有ATL的情况下进行引用计数”。已经提出了一些类似的问题,但在前者中回答了不同的问题,在这两种情况下,ATL都参与其中。我的问题对C++更为普遍,而不是关于COM. 假设我们有一个IUnknown“接口”,如下所示: class IUnknown { public: virtual ULONG AddRef() = 0; virtual ULONG Release() = 0; virtual ULONG QueryInterface(void

这个问题也可以题为“如何在没有ATL的情况下进行引用计数”。已经提出了一些类似的问题,但在前者中回答了不同的问题,在这两种情况下,ATL都参与其中。我的问题对C++更为普遍,而不是关于COM.</P> 假设我们有一个
IUnknown
“接口”,如下所示:

class IUnknown
{
public:
    virtual ULONG AddRef() = 0;
    virtual ULONG Release() = 0;
    virtual ULONG QueryInterface(void * iid, void **ppv) = 0;
};
…让我们加入一些其他接口,它们是虚构SDK的一部分:

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

class IMammal : public IAnimal
{
public:
    virtual ULONG Reproduce() = 0;
};
由于我将要实现几个动物和哺乳动物,我不希望在每个类中复制粘贴
AddRef()
Release()
实现,因此我编写了
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;
};
…以便我可以使用它来实现一个
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;
}
…但是,编译器不同意:

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'

我缺少什么?

您可以使用允许您将基类指定为模板参数的类模板。这将消除在派生类中实现最终重写函数的需要

您还需要确保声明的成员函数具有正确的调用约定、返回类型和参数类型

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();
};
模板
类UnknownImpl:公共基
{
公众:
UnknownImpl():\u referenceCount(0)
{
}
ULONG标准方法调用类型AddRef()
{
返回联锁增量(&_referenceCount);
}
ULONG标准方法调用类型发布()
{
ULONG结果=联锁减量(&_referenceCount);
如果(结果==0)
{
删除此项;
}
返回结果;
}
私人:
乌龙参考计数;
};
类别Cat:公共未知NIMPL
{
公众:
HRESULT STDMETHODCALLTYPE查询接口(refid,void**);
IAnimal**GetParents();
ULONG();
};

您可以使用允许您将基类指定为模板参数的类模板。这将消除在派生类中实现最终重写函数的需要

您还需要确保声明的成员函数具有正确的调用约定、返回类型和参数类型

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();
};
模板
类UnknownImpl:公共基
{
公众:
UnknownImpl():\u referenceCount(0)
{
}
ULONG标准方法调用类型AddRef()
{
返回联锁增量(&_referenceCount);
}
ULONG标准方法调用类型发布()
{
ULONG结果=联锁减量(&_referenceCount);
如果(结果==0)
{
删除此项;
}
返回结果;
}
私人:
乌龙参考计数;
};
类别Cat:公共未知NIMPL
{
公众:
HRESULT STDMETHODCALLTYPE查询接口(refid,void**);
IAnimal**GetParents();
ULONG();
};
您必须使用继承。更改:

IAnimal类:公共IUnknown-->IAnimal类:公共虚拟IUnknown;及

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

必须使用继承。更改:

IAnimal类:公共IUnknown-->IAnimal类:公共虚拟IUnknown;及


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

这不需要更改接口定义:

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

class Cat : public UnknownBase<IMammal>
{
    ...
}
模板
类别未知基地:公共I
{
...
}
类别Cat:公共未知数据库
{
...
}

这不需要更改接口定义:

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

class Cat : public UnknownBase<IMammal>
{
    ...
}
模板
类别未知基地:公共I
{
...
}
类别Cat:公共未知数据库
{
...
}

边注-如果你的代码> unNeXBase<代码>只是为了给这两个函数提供一个IMPL,而原来的接口只通过它,你可以考虑将IMPL直接移动到接口本身。AH,这会更容易,但是在这种情况下,我不控制<代码> IUngule或任何其他接口。我只是在执行它们。边注释——如果你的代码> unNeXBase<代码>只是为了给这两个函数提供一个IMPL,而原来的接口只通过它,你可以考虑将IMPL直接移动到接口本身。但在本例中,我不控制
IUnknown
或任何其他接口。我只是在实现它们。只有当我可以控制
IAnimal
的定义时,这才有效,但我不能。只有当我可以控制
IAnimal
的定义时,这才有效,但我不能。感谢提醒我,我可能应该使用
联锁[In | De]crement
。感谢您提醒我,我可能应该使用
联锁[In | De]crement
。这被称为奇怪重复出现的模板模式:CRTP。这被称为奇怪重复出现的模板模式:CRTP。