Другой вариант (по крайней мере, при использовании 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();
}
Хотя это, кажется, работает и удовлетворяет тому, что вы ищете (см. Предупреждение 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") );
}
Пока боги C ++ не утешают насс переменными макросами вам придется объявлять один для каждого количества параметров, которые у вас есть.Также, если вы используете множественное наследование, потенциально вам не понадобится второй параметр «Obj», но, как я уже говорил, я бы избежал множественного наследования, если есть другое решение, которое в данном случае является одним дополнительным параметром.
Тем не менее, третий вариант может быть чем-то, что авторы Pragmatic Programmer , похоже, отстаивают много.Если у вас есть тонна печенья код, который вы не хотите повторять, потому что, как вы указали, он вводит человеческую ошибку.Определите свой собственный язык и напишите сценарий генератора кода (python, perl ...) для автоматического создания реального кода.В этом случае вы можете почти указать на интерфейс, и сценарий напишет за вас текст.Я сам не пытался делать подобные вещи, но в последнее время хотел использовать это где-то, чтобы увидеть и оценить результат.