Мне нужно написать соглашение о кодировании, которое будет использоваться как новичками, так и опытными разработчиками на С++. Правило о наследовании для динамического полиморфизма выглядит следующим образом:
- Для динамического полиморфизма рассмотрим использование одиночного наследования (древовидная иерархия), возможно, с множественным наследованием абстрактных интерфейсов
- для наследования по иерархии (базовые классы и т.д.), по умолчанию, используйте public inheritance
- для наследования абстрактного интерфейса, по умолчанию, используйте общедоступное виртуальное наследование
За этим правилом последует подробная информация о реализации, возможных исключениях и т.д.
Итак, вопрос: Является ли это правило желательным как для новичков, так и для опытных разработчиков на С++? (плюсы/минусы, а также источники и ссылки приветствуются)
Я вижу:
Плюсы:
- правило, легко используемое новичками, без ограничения опытных разработчиков.
- знакомы тем, кто уже знаком с интерфейсами Java/.NET
- утилит проблемы, связанные с виртуальным наследованием реализации (поскольку он зарезервирован для абстрактных интерфейсов), а также не виртуальное наследование (возможная двусмысленность при кастинге в класс интерфейса)
Минусы:
- небольшая производительность (скорость при передаче на интерфейс, размер виртуальных таблиц, дополнительные указатели в экземпляре класса)
Примечание. Я прочитал следующие онлайн-источники:
- Когда виртуальное наследование - хороший дизайн?
- http://www.parashift.com/c++-faq-lite/virtual-inheritance-abcs.html
- http://www.artima.com/intv/abcs.html
- http://cpptips.com/virt_inher2
- http://g.oswego.edu/dl/mood/C++AsIDL.html
Примечание 2: Использование имени "абстрактного интерфейса" придумано после использования Sutter и Alexandrescu в пункте 36 "Стандарты кодирования С++"
Это один случай, который должен работать (его эквивалент Java/С# с использованием интерфейсов просто работает), но это не в С++, если наследование интерфейса не является виртуальным:
class A
{
public :
virtual ~A() = 0 {}
} ;
class B : public A {} ; // should have been virtual to avoid the error
class C : public A {} ; // should have been virtual to avoid the error
class D : public B, public C
{
public :
virtual ~D() {}
} ;
void foo(A * c) {}
void bar(D * d)
{
foo(d) ; // Error: ambiguous conversions from 'D *' to 'A *
}
И да, явное литье, чтобы удалить двусмысленность, является неправильным решением (явное литье, как правило, является неправильным решением).