#c #inheritance #default-constructor #virtual-inheritance
#c #наследование #default-constructor #виртуальное наследование
Вопрос:
Предварительные
Я пишу библиотеку высокого уровня для модулей VMEbus. У меня есть два уровня абстракции для представления модулей:
|---------------------|
| VBoard |
|---------------------|
| VBoard( address ) |
| Init() = 0 |
| ... |
|---------------------|
/ /
/__ /__
|| ||__________________________
|| ||
|--------------------| |-----------------------|
| VBoardAcquisitor | | VBoardInterrupter |
|--------------------| |-----------------------|
| AllocBuff() = 0 | | ... |
| ... | |-----------------------|
|--------------------| /
/ /__
/__ ______ may be _____________||
|| ||
.________________________.
| |
| V1785N |
|________________________|
| Init() override |
| AllocBuff() override |
|________________________|
Таким образом, каждый конкретный модуль (как V1785N
на приведенной выше диаграмме UML) является a VBoard
и должен переопределять Init()
функцию (и некоторые другие). Также существуют модули, наделенные функциями сбора данных. Для них существует другой интерфейс (абстрактный класс) с именем VBoardAcquisitor
, который, конечно же, также является VBoard
. Между модулем и конкретным модулем может быть больше промежуточных классов (например VBoardInterrupter
) VBoard
. Итак, виртуальное наследование.
Проблема
Решающий момент заключается в том, что VBoard
у него есть только параметризованный конструктор (параметр — это VME-адрес модуля). И я не хочу, чтобы у него был другой (copy-assignment и copy-ctor удаляются). Но при реализации приведенной выше схемы на C (см. Раздел Кода) Я получаю ошибку компиляции:
Code.cpp: In constructor ‘VBoardAcquisitor::VBoardAcquisitor()’:
Code.cpp:22:29: error: no matching function for call to ‘VBoard::VBoard()’
buffer( nullptr )
^
Code.cpp:22:29: note: candidates are:
Code.cpp:8:9: note: VBoard::VBoard(int)
VBoard( int address ) :
^
Code.cpp:8:9: note: candidate expects 1 argument, 0 provided
Code.cpp:3:7: note: constexpr VBoard::VBoard(const VBoardamp;)
class VBoard
^
Code.cpp:3:7: note: candidate expects 1 argument, 0 provided
Код
Это MRE (компилируется с g -std=c 11 Code.cpp -o Code
):
#include <iostream>
class VBoard
{
int address;
public :
VBoard( int address ) :
address( address )
{ }
virtual ~VBoard() { };
virtual void Init() = 0;
};
class VBoardAcquisitor : virtual public VBoard
{
int *buffer;
public :
VBoardAcquisitor() :
buffer( nullptr )//problem here
{ }
virtual ~VBoardAcquisitor() { };
virtual void AllocBuff() = 0;
};
class V1785N : public VBoardAcquisitor
{
public :
V1785N( int address ) :
VBoard( address ),
VBoardAcquisitor()
{ }
~V1785N() { }
void Init() override { std::cout << "Initn"; }
void AllocBuff() override { std::cout << "AllocBuffn"; }
};
int main()
{
V1785N adc( 0x40000000 );
return 0;
}
Он отлично компилируется, если я либо :
- вызов
VBoard
ctor с некоторым случайным адресом в списке инициализацииVBoardAcquisitor
- определите ctor по умолчанию для
VBoard
Хотя я знаю (проверено), что такой вызов из VBoardAcquisitor
(первый случай) будет проигнорирован, мне это не нравится, потому что я как бы вынужден использовать некоторый адрес платы «по умолчанию», и, по крайней мере, эстетически мне это не нравится.
Итак, мой вопрос: являются ли единственными возможными «решениями» эти два?
Система
ОС: Scientific Linux 7
версия gcc: 4.8.5
Примечание
Обратите внимание, что он отлично компилируется с gcc 7.5.0
as is в Ubuntu 18.04. Но я не знаю почему.
Комментарии:
1. Собираетесь ли вы на самом деле создавать VBoardAcquisitors напрямую по умолчанию? В примере кода вы создаете V1785N, который передает (предположительно значимый) адрес на VBoard и по умолчанию создает VBoardAcquisitor. Но VBoardAcquisitor наследует от VBoard, верно? Так почему бы вместо этого не передать это в VBoardAcquisitor и использовать его список инициализации члена для настройки VBoard? Есть ли идея, что могут быть некоторые классы, которые наследуют от VBoardAcquisitor несколько раз, но хотят использовать только один базовый виртуальный VBoard?
2. @NathanPierson, я думаю, вы, кажется, правы.
3. Предполагается, что VBoardInterrupter является самой виртуальной платой или он должен выполнять действия на виртуальной плате? Вполне может быть, что VBoardInterrupter не нужно наследовать от VBoard и / или конкретным экземплярам VBoard не нужно наследовать от VBoardInterrupter, если, например, у VBoardInterrupter есть переменная-член VBoard *, представляющая прерываемую VBoard.
4. @NathanPierson, да, я пока даже не знаю реализацию этого класса, tbh (: . Но по своей природе плата VME сама является прерывателем. Вероятно, вопрос не касается виртуального наследования. Но в любом случае, я полагаю, что уже слишком поздно что-то менять.
Ответ №1:
Конструктор V1785N
должен переслать адрес своему базовому классу VBoardAcquisitor
, который сам перешлет адрес на VBoard
Вот конструктор V1785N
V1785N( int address ) :
VBoardAcquisitor(address)
{}
и конструктор VBoardAcquisitor
VBoardAcquisitor(int address) :
VBoard( address ),
buffer( nullptr )
{ }
Обратите внимание, что вы не должны наследовать с virtual
помощью from VBoard
, поэтому первая строка VBoardAcquisitor
выглядит так
class VBoardAcquisitor : public VBoard
Полный код: https://ideone.com/YUeLtf
Комментарии:
1. Я должен наследовать с
virtual
помощью fromVBoard
, потому что, как я сказал в сообщении, модуль может быть какVBoardAcquisitor
и, например,VBoardInterrupter
. Хотя ваш ответ кажется правильным.