Mic*_*ice 8 c++ pure-virtual template-method-pattern class-visibility
编辑:根据一些评论,简单来说,我的意思是a)更少的代码,b)易于维护,和c)很难出错.
编辑#2:另外,如果它确实简化了实现,那么使用包含而不是私有继承并不令人反感InterfaceImpl.
目前,我知道这样做的唯一方法是让实现者定义抽象方法并将调用委托给目标基类型的方法.例:
#include <iostream>
#include <memory>
class Interface
{
public:
virtual void method1() = 0;
virtual void method2(int x) = 0;
};
class MethodOneImpl
{
private:
void method1(int x)
{ std::cout << "MethodOneImpl::method1() " << x << std::endl; }
public:
void method1() { method1(0); }
};
class MethodTwoImpl
{
public:
void myFunc(int x)
{ std::cout << "MethodTwoImpl::myFunc(x)" << x << std::endl; }
};
class InterfaceImpl : public Interface
, private MethodOneImpl
, private MethodTwoImpl
{
public:
virtual void method1() { MethodOneImpl::method1(); }
virtual void method2(int x) { MethodTwoImpl::myFunc(x); }
};
int main()
{
std::unique_ptr<Interface> inf;
inf.reset(new InterfaceImpl);
inf->method1();
inf->method2(0);
// This should be disallowed!
// std::unique_ptr<MethodOneImpl> moi;
// moi.reset(new InterfaceImpl);
}
Run Code Online (Sandbox Code Playgroud)
起初,我认为这或许可以解决问题:
class InterfaceImpl : public Interface
, private MethodOneImpl
, private MethodTwoImpl
{
public:
using MethodOneImpl::method1;
// Obviously this wouldn't work as the method names don't match.
//using MethodTwoImpl::???
};
Run Code Online (Sandbox Code Playgroud)
第一个using语句将使两个MethodOneImpl::method1方法都是公共的,但它实际上不符合合同Interface,并且它修改了它的可访问性MethodOneImpl::method1(int).显然我们无法使用此解决方案,method2因为名称不匹配.
FWIW,我认为这是一个解决方案,但它根本不是标准的一部分(换句话说它不会编译).我正在考虑向C++委员会提出建议; 如果有人有任何建议,我会感谢下面的任何评论(但请不要提交建议作为答案).
另一种选择(至少如果使用 MS VC++)是使用虚拟继承:
struct MyInterface
{
virtual void Method1() = 0;
virtual void Method2() = 0;
};
class Method1Impl : public virtual MyInterface
{
virtual void Method1() { _tprintf( _T("Method1\n") ); }
};
class Method2Impl : public virtual MyInterface
{
virtual void Method2() { _tprintf( _T("Method2\n") ); }
};
class InterfaceImpl : public virtual MyInterface,
private Method1Impl,
private Method2Impl
{
};
void TestWeirdInterfaceImpl()
{
MyInterface* pItf = new InterfaceImpl();
pItf->Method1();
pItf->Method2();
}
Run Code Online (Sandbox Code Playgroud)
虽然这似乎有效并满足您正在寻找的内容(除了 C4250 警告您必须使用 #pragma 进行抑制),但这不是我的方法。(我相信虚拟继承仍然不是所有编译器都支持的,但我可能是错的)。
我可能会选择遏制,一旦样板代码成为标识符,将其包装到某种宏映射中(类似于 ATL 或 MFC 中的映射),这将使它真的非常难以搞砸。
所以这就是我的宏观方法:
struct MyInterface
{
virtual float Method1( int x ) = 0;
virtual int Method2( float a, float b ) = 0;
virtual void Method3( const TCHAR* sz ) = 0;
};
class Method1Impl
{
public:
float Method1( int x ) {
_tprintf( _T("Method1: %d\n"), x ); return 5.0;
}
};
class Method2and3Impl
{
public:
int Method2( float a, float b ) {
_tprintf( _T("Method2: %f, %f\n"), a, b ); return 666;
}
void Method3( const TCHAR* sz ) {
_tprintf( _T("Method3: %s"), sz );
}
};
#define DECLARE_METHOD0( MethodName, Obj, R ) \
virtual R MethodName() { return Obj.MethodName(); }
#define DECLARE_METHOD1( MethodName, Obj, R, A1 ) \
virtual R MethodName( A1 a1 ) { return Obj.MethodName( a1 ); }
#define DECLARE_METHOD2( MethodName, Obj, R, A1, A2 ) \
virtual R MethodName( A1 a1, A2 a2 ) { return Obj.MethodName( a1, a2 ); }
class InterfaceImpl : public MyInterface
{
public:
DECLARE_METHOD1( Method1, m_method1Impl, float, int );
DECLARE_METHOD2( Method2, m_method2and3Impl, int, float, float );
DECLARE_METHOD1( Method3, m_method2and3Impl, void, const TCHAR* );
private:
Method1Impl m_method1Impl;
Method2and3Impl m_method2and3Impl;
};
void TestWeirdInterfaceImpl()
{
MyInterface* pItf = new InterfaceImpl();
pItf->Method1( 86 );
pItf->Method2( 42.0, 24.0 );
pItf->Method3( _T("hi") );
}
Run Code Online (Sandbox Code Playgroud)
在 C++ 上帝赐予我们可变参数宏之前,您必须为拥有的每个参数数量声明一个。另外,如果您使用多重继承,则可能不需要第二个“Obj”参数,但正如我之前所说,如果有另一种解决方案(在本例中是一个额外的参数),我会避免多重继承。
然而,第三种选择可能是《实用程序员》的作者似乎大力提倡的。如果您有大量不想重复的千篇一律的代码,因为正如您所指出的,它会引入人为错误。定义您自己的语言并编写代码生成器脚本(python、perl...)来自动创建实际代码。在这种情况下,您几乎可以指向一个界面,然后让脚本为您写出文本。我自己没有尝试过做这种事情,但最近一直想在某个地方使用它只是为了查看和评估结果。