【问题标题】:Is it necessary to have default ctor for the most-base class in virtual inheritance?虚拟继承中最基类是否需要默认ctor?
【发布时间】:2021-02-02 07:41:15
【问题描述】:

预赛

我正在为 VMEbus 模块编写一个高级库。我有两个抽象级别来表示模块:


|---------------------|
|        VBoard       |
|---------------------|
| + VBoard( address ) |
| + Init() = 0        |
| + ...               |
|---------------------|
         /\        /\
        /__\      /__\
         ||        ||__________________________
         ||                                  ||
|--------------------|            |-----------------------|
|  VBoardAcquisitor  |            |   VBoardInterrupter   |
|--------------------|            |-----------------------|
| + AllocBuff() = 0  |            | ...                   |
| + ...              |            |-----------------------| 
|--------------------|                       /\
         /\                                 /__\
        /__\      ______ may be _____________||
         ||       ||
.________________________.
|                        |
|        V1785N          |
|________________________|
| + Init() override      |
| + AllocBuff() override |
|________________________|

所以每个具体模块(如上面 UML 图中的V1785N)都是一个VBoard,并且必须覆盖Init() 函数(以及其他一些函数)。还有一些具有数据采集功能的模块。对于他们来说,还有另一个名为VBoardAcquisitor 的接口(抽象类)当然也是VBoard。在VBoard 和具体模块之间可能有更多的中间类(如VBoardInterrupter)。所以虚拟继承。

问题

关键时刻VBoard确实只有参数化的构造函数(参数是模块的VME地址)。而且我不希望它有其他的(复制分配和复制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 VBoard&)
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 << "Init\n"; }
        void AllocBuff() override { std::cout << "AllocBuff\n"; }
};


int main()
{
    V1785N adc( 0x40000000 );
    return 0;
}

如果我选择它,它编译得很好:

  • VBoardAcquisitor 的初始化列表中调用VBoard ctor带有一些随机地址
  • 定义VBoard的默认ctor

虽然我知道(检查过)来自VBoardAcquisitor(第一种情况)的此类调用将被忽略,但我不喜欢这样,因为我不得不使用一些“默认”板地址,至少在美学上我对此感到不舒服。

所以我的问题是:唯一可能的“解决方案”是这两个吗?

系统

操作系统:科学 Linux 7

gcc 版本:4.8.5

注意

请注意,它可以在 Ubuntu 18.04 上使用 gcc 7.5.0按原样 正常编译。但我不知道为什么。

【问题讨论】:

  • 您是否打算直接默认构造 VBoardAcquisitors?在示例代码中,您构造了一个 V1785N,它将一个(可能有意义的)地址传递给 VBoard 并默认构造一个 VBoardAcquisitor。但是 VBoardAcquisitor 是从 VBoard 继承的,对吧?那么为什么不将它传递给 VBoardAcquisitor 并使用其成员初始化列表来设置 VBoard?是否可能有一些类多次从 VBoardAcquisitor 继承但只想使用单个底层虚拟 VBoard?
  • @NathanPierson,我认为你似乎是对的。
  • VBoardInterrupter 是否应该成为 VBoard 本身,还是应该对 VBoard 采取行动?很可能 VBoardInterrupter 不需要从 VBoard 继承和/或 VBoard 的具体实例不需要从 VBoardInterrupter 继承,例如,如果 VBoardInterrupter 具有代表它正在中断的 VBoard 的 VBoard* 成员变量。
  • @NathanPierson,是的,我现在什至不知道这个类的实现,tbh (: 。但本质上,VME 板本身就是一个中断器。可能,这个问题不处理虚拟继承。但无论如何,我想改变一切为时已晚。

标签: c++ inheritance default-constructor virtual-inheritance


【解决方案1】:

V1785N 的构造函数应该将地址转发给它的基类VBoardAcquisitor,它本身会将地址转发给VBoard

这里是V1785N的构造函数

V1785N( int address ) :
    VBoardAcquisitor(address)
{}

VBoardAcquisitor的构造函数

VBoardAcquisitor(int address) :
    VBoard( address ),
    buffer( nullptr )
{ }

注意,你不应该从VBoard继承virtual,所以VBoardAcquisitor的第一行看起来像

class VBoardAcquisitor : public VBoard

完整代码:https://ideone.com/YUeLtf

【讨论】:

  • 应该virtual继承VBoard,因为正如我在帖子中所说,一个模块可能既是VBoardAcquisitor又是VBoardInterrupter。不过,您的答案似乎是正确的。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-09-18
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-04-18
  • 2021-06-02
相关资源
最近更新 更多