[C++] Виртуальное наследование, конструкторы с параметрами
Итак, есть базовый класс
Код:
class Tm_BasePackage
{
public:
Tm_BasePackage(std::uint64_t nLength, std::uint8_t nType, std::uint64_t nParamBitMap);
...
};
{
public:
Tm_BasePackage(std::uint64_t nLength, std::uint8_t nType, std::uint64_t nParamBitMap);
...
};
Допустим, от него наследуются два других:
Код:
class Tm_TxPackage : virtual public Tm_BasePackage
{
public:
Tm_TxPackage(std::uint64_t nLength, std::uint8_t nType, std::uint64_t nParamBitMap):
Tm_BasePackage(nLength, nType, nParamBitMap) { };
...
virtual std::uint64_t Pack(PkgBuffer& Buffer, std::uint64_t nDataPos = 0) throw () = 0;
};
....
class Tm_RxPackage : virtual public Tm_BasePackage
{
public:
Tm_RxPackage(std::uint64_t nLength, std::uint8_t nType, std::uint64_t nParamBitMap):
Tm_BasePackage(nLength, nType, nParamBitMap) { };
...
virtual bool Unpack(const PkgBuffer& Buffer, std::uint64_t nDataPos = 0) throw () = 0;
};
{
public:
Tm_TxPackage(std::uint64_t nLength, std::uint8_t nType, std::uint64_t nParamBitMap):
Tm_BasePackage(nLength, nType, nParamBitMap) { };
...
virtual std::uint64_t Pack(PkgBuffer& Buffer, std::uint64_t nDataPos = 0) throw () = 0;
};
....
class Tm_RxPackage : virtual public Tm_BasePackage
{
public:
Tm_RxPackage(std::uint64_t nLength, std::uint8_t nType, std::uint64_t nParamBitMap):
Tm_BasePackage(nLength, nType, nParamBitMap) { };
...
virtual bool Unpack(const PkgBuffer& Buffer, std::uint64_t nDataPos = 0) throw () = 0;
};
Каким образом унаследовать одновременно Tm_TxPackage и Tm_RxPackage, не определяя конструктора без параметров в базовом классе?
Этот код удалось скомпилировать:
Код:
class Tm_KeepAlivePkg: public Tm_TxPackage, public Tm_RxPackage
{
public:
Tm_KeepAlivePkg (std::uint64_t nLength, std::uint8_t nType, std::uint64_t nParamBitMap):
Tm_TxPackage(nLength, nType, nParamBitMap), Tm_RxPackage(nLength, nType, nParamBitMap), Tm_BasePackage(nLength, nType, nParamBitMap) { };
virtual std::uint64_t Pack(PkgBuffer& Buffer, std::uint64_t nDataPos = 0) throw () { };
virtual bool Unpack(const PkgBuffer& Buffer, std::uint64_t nDataPos = 0) throw () { };
};
{
public:
Tm_KeepAlivePkg (std::uint64_t nLength, std::uint8_t nType, std::uint64_t nParamBitMap):
Tm_TxPackage(nLength, nType, nParamBitMap), Tm_RxPackage(nLength, nType, nParamBitMap), Tm_BasePackage(nLength, nType, nParamBitMap) { };
virtual std::uint64_t Pack(PkgBuffer& Buffer, std::uint64_t nDataPos = 0) throw () { };
virtual bool Unpack(const PkgBuffer& Buffer, std::uint64_t nDataPos = 0) throw () { };
};
Но меня терзают смутные сомнения, действительно ли нужно всем родительским классам в явном виде передавать параметры (без этого не собирается)?
Как вариант, который тоже собирается, и который мне кажется более правильным - не определять конструкторы с параметрами в классах Tm_RxPackage и Tm_TxPackage, а в их производных инициализировать напрямую Tm_BasePackage.
Просветите, пожалуйста, как умные люди делают :)
UPD: нагуглил таки
http://www.devdoc.ru/index.php/content/view/virtual_inheritance.htm
насколько я понимаю, второй вариант (не определять конструкторы с параметрами в классах Tm_RxPackage и Tm_TxPackage) таки действительно правильный
Цитата:
C++ по умолчанию не создает ромбовидного наследования: компилятор обрабатывает каждый путь наследования отдельно, в результате чего объект D будет на самом деле содержать два разных подобъекта A, и при использовании членов A потребуется указать путь наследования (B::A или C::A). Чтобы сгенерировать ромбовидную структуру наследования, необходимо воспользоваться виртуальным наследованием класса A на нескольких путях наследования: если оба наследования от A к B и от A к C помечаются спецификатором virtual (например, class B : virtual public A), C++ специальным образом проследит за созданием только одного подобъекта A, и использование членов A будет работать корректно. Если виртуальное и невиртуальное наследования смешиваются, то получается один виртуальный подобъект A и по одному невиртуальному подобъекту A для каждого пути невиртуального наследования к A. При виртуальном вызове метода виртуального базового класса используется так называемое правило доминирования: компилятор запрещает виртуальный вызов метода, который был перегружен на нескольких путях наследования.
Что мы имеем: создается один объект Tm_BasePackage, но т.к. у него нет конструктора по умолчанию компилятор входит в замешательство не зная от какого из классов ( Tm_TxPackage или Tm_RxPackage ) взять параметры для конструктора. И чтобы не вышло гадости просит программиста явно указать параметры.
Лучше такую иерархию вобще не иметь. Это наиболее правильный способ. Но второй вариант правильней да.
Цитата: aks
Лучше такую иерархию вобще не иметь. Это наиболее правильный способ. Но второй вариант правильней да.
Ну у меня собственно сейчас все виртуальные методы определены в Tm_BasePackage. Но разделить их по подклассам показалось правильнее. Ладно, тогда забью. Спасибо.