Необходимо ли иметь ctor по умолчанию для самого базового класса в виртуальном наследовании?

#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 помощью from VBoard , потому что, как я сказал в сообщении, модуль может быть как VBoardAcquisitor и, например, VBoardInterrupter . Хотя ваш ответ кажется правильным.