虚拟继承中最基本的 class 是否需要默认构造函数?
Is it necessary to have default ctor for the most-base class in virtual inheritance?
预赛
我正在为 VMEbus 模块编写高级库。我有两个抽象级别来表示模块:
|---------------------|
| VBoard |
|---------------------|
| + VBoard( address ) |
| + Init() = 0 |
| + ... |
|---------------------|
/\ /\
/__\ /__\
|| ||__________________________
|| ||
|--------------------| |-----------------------|
| VBoardAcquisitor | | VBoardInterrupter |
|--------------------| |-----------------------|
| + AllocBuff() = 0 | | ... |
| + ... | |-----------------------|
|--------------------| /\
/\ /__\
/__\ ______ may be _____________||
|| ||
.________________________.
| |
| V1785N |
|________________________|
| + Init() override |
| + AllocBuff() override |
|________________________|
所以每个具体模块(如上面 UML 图中的 V1785N
)都是一个 VBoard
并且必须覆盖 Init()
函数(和其他一些)。还有一些模块具有数据采集功能。对他们来说,还有另一个名为 VBoardAcquisitor
的接口(抽象 class),当然,它也是一个 VBoard
。 VBoard
和具体模块之间可能有更多的中间 classes(如 VBoardInterrupter
)。所以虚拟继承。
问题
关键时刻VBoard
确实只有参数化的构造函数(参数是模块的VME地址)。而且我不希望它有其他的(复制分配和复制者被删除)。但是在 C++ 中实现上述方案时(参见 Code 部分)我得到编译错误:
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
的默认构造函数
尽管我知道(检查过)来自 VBoardAcquisitor
的此类调用(第一种情况)将被忽略,但我不喜欢这样,因为我有点被迫使用一些“默认”板地址和至少在美学上我对此感到不舒服。
所以我的问题是:唯一可能的“解决方案”是这两个吗?
系统
OS : 科学 Linux 7
gcc 版本:4.8.5
备注
请注意,它可以在 Ubuntu 18.04 上与 gcc 7.5.0
原样 一起正常编译。但是不知道为什么。
V1785N
的构造函数应该将地址转发到它的基 class VBoardAcquisitor
,它本身会将地址转发到 VBoard
这里是V1785N
的构造函数
V1785N( int address ) :
VBoardAcquisitor(address)
{}
和VBoardAcquisitor
的构造函数
VBoardAcquisitor(int address) :
VBoard( address ),
buffer( nullptr )
{ }
请注意,您不应该使用 virtual
从 VBoard
继承,因此 VBoardAcquisitor
的第一行看起来像
class VBoardAcquisitor : public VBoard
预赛
我正在为 VMEbus 模块编写高级库。我有两个抽象级别来表示模块:
|---------------------|
| VBoard |
|---------------------|
| + VBoard( address ) |
| + Init() = 0 |
| + ... |
|---------------------|
/\ /\
/__\ /__\
|| ||__________________________
|| ||
|--------------------| |-----------------------|
| VBoardAcquisitor | | VBoardInterrupter |
|--------------------| |-----------------------|
| + AllocBuff() = 0 | | ... |
| + ... | |-----------------------|
|--------------------| /\
/\ /__\
/__\ ______ may be _____________||
|| ||
.________________________.
| |
| V1785N |
|________________________|
| + Init() override |
| + AllocBuff() override |
|________________________|
所以每个具体模块(如上面 UML 图中的 V1785N
)都是一个 VBoard
并且必须覆盖 Init()
函数(和其他一些)。还有一些模块具有数据采集功能。对他们来说,还有另一个名为 VBoardAcquisitor
的接口(抽象 class),当然,它也是一个 VBoard
。 VBoard
和具体模块之间可能有更多的中间 classes(如 VBoardInterrupter
)。所以虚拟继承。
问题
关键时刻VBoard
确实只有参数化的构造函数(参数是模块的VME地址)。而且我不希望它有其他的(复制分配和复制者被删除)。但是在 C++ 中实现上述方案时(参见 Code 部分)我得到编译错误:
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
的默认构造函数
VBoard
ctor
尽管我知道(检查过)来自 VBoardAcquisitor
的此类调用(第一种情况)将被忽略,但我不喜欢这样,因为我有点被迫使用一些“默认”板地址和至少在美学上我对此感到不舒服。
所以我的问题是:唯一可能的“解决方案”是这两个吗?
系统
OS : 科学 Linux 7
gcc 版本:4.8.5
备注
请注意,它可以在 Ubuntu 18.04 上与 gcc 7.5.0
原样 一起正常编译。但是不知道为什么。
V1785N
的构造函数应该将地址转发到它的基 class VBoardAcquisitor
,它本身会将地址转发到 VBoard
这里是V1785N
V1785N( int address ) :
VBoardAcquisitor(address)
{}
和VBoardAcquisitor
VBoardAcquisitor(int address) :
VBoard( address ),
buffer( nullptr )
{ }
请注意,您不应该使用 virtual
从 VBoard
继承,因此 VBoardAcquisitor
的第一行看起来像
class VBoardAcquisitor : public VBoard