Первое: почему мы должны избегать множественного наследования в C ++?Я никогда не видел большого приложения, которое не использовало бы его широко.Наследование от нескольких интерфейсов - хороший пример того, где оно используется.
Обратите внимание, что Java interface
не работает - как только вы захотите использовать программирование по контракту, вы застряли с использованием абстрактных классов, и они не допускают множественного наследования.В C ++, однако, это просто:
class One : boost::noncopyable
{
virtual void doFunctionOne( int i ) = 0;
public:
virtual ~One() {}
void functionOne( int i )
{
// assert pre-conditions...
doFunctionOne( i );
// assert post-conditions...
}
};
class Two : boost::noncopyable
{
virtual double doFunctionTwo() = 0;
public:
virtual ~Two() {}
double functionTwo()
{
// assert pre-conditions...
double results = doFunctionTwo();
// assert post-conditions...
return results;
}
};
class ImplementsOneAndTwo : public One, public Two
{
virtual void doFunctionOne( int i );
virtual double doFunctionTwo();
public:
};
В качестве альтернативы вы можете иметь составной интерфейс:
class OneAndTwo : public One, public Two
{
};
class ImplementsOneAndTwo : public OneAndTwo
{
virtual void doFunctionOne( int i );
virtual double doFunctionTwo();
public:
};
и наследовать его, что всегда имеет смысл.
Это более или менее стандартная идиома;в тех случаях, когда предположительно не может быть каких-либо предварительных или постусловий в интерфейсе (обычно это инверсия вызова), виртуальные функции могут быть общедоступными, но в целом они будут частными, так что вы можете принудительно применять до и послеусловия.
Наконец, обратите внимание, что во многих случаях (особенно если класс представляет значение), вы просто реализуете его напрямую, без интерфейса.В отличие от Java, вам не нужен отдельный интерфейс для поддержки реализации в файле, отличном от определения класса - так работает C ++ по умолчанию (с определением класса в заголовке, но с кодом реализации в исходном файле).