具有相同名称成员的基类的C++多重继承
C++ multiple inheritance from base classes with members with same name
让我告诉你我遇到的问题。我正在设计一组控制数字设备的类。该设备可以在两种操作模式下工作。在第一种模式中,它可以执行一组特定的操作,在第二种模式中它可以执行另一组操作(两者之间可能有一些常见的操作)。我还可以在运行中更改设备的模式,因此如果需要,我可以在两种模式之间切换。与模式无关,设备使用相同的寄存器集。
我想用每个模式一个基类来解决这个问题,这样当我需要第一组操作时,我可以有模式1的对象,当我需要第二组操作时可以有模式2的对象。然后我可以从这两个基类派生一个类,这样我就可以拥有执行所有操作的对象。
我的设计的问题是,这两个基类有一些共同的函数和对相同寄存器的引用。由于我不能阻止成员的继承,所以派生类中会有重复的成员。我知道我可以用scope操作符选择访问哪个副本,但我仍然认为这是一个糟糕的设计。
所以我的问题是:有没有一种惯用的方法来解决这个问题?
如果没有正确或简单的方法来解决这个问题,我会考虑设计3个层次独立的类。我会有一些重复的代码,但这不是什么大问题,对吧?
下面的代码(简化)用于说明:
class mode1
{
protected:
volatile uint8_t& reg1;
volatile uint8_t& reg2;
uint8_t data;
public:
virtual void operation1() final { // do something }
virtual void operation2() final { // do something }
virtual void operation3() final { // do something }
};
class mode2
{
protected:
volatile uint8_t& reg1;
volatile uint8_t& reg2;
uint8_t data;
public:
virtual void operation4() final { // do something }
virtual void operation2() final { // do something }
virtual void operation5() final { // do something }
};
class mode1and2 : public mode1, public mode2
{
public:
void operation6() { // do something }
void operation7() { // do something }
};
注释模式1和2具有操作2和所有数据成员。
我会把mode1
和mode2
的公共部分放在一个公共基类中,比如Common
,它包括您的数据和成员函数operation2
。然后,结合虚拟继承,您可以对同一数据拥有两个视图,即使在需要的时候也是如此。
class common {
friend class mode1;
friend class mode2;
protected:
volatile uint8_t& reg1;
volatile uint8_t& reg2;
uint8_t data;
public:
virtual void operation2() final { // do something
};
};
class mode1 : public virtual common
{
public:
virtual void operation1() final { // do something
};
virtual void operation3() final { // do something }
};
};
class mode2 : public virtual common
{
public:
virtual void operation4() final { // do something
}
virtual void operation5() final { // do something
}
};
class mode1and2 : public mode1, public mode2
{
public:
void operation6() { // do something }
};
void operation7() { // do something }
};
};
状态设计模式看起来是您的案例的一个很好的候选者
作为一个最小的工作示例:
#include<memory>
#include<iostream>
struct Behavior {
virtual void f() = 0;
virtual void g() = 0;
};
struct NullBehavior: Behavior {
void f() override {}
void g() override {}
};
struct Mode1: Behavior {
void f() override { std::cout << "mode 1 - f" << std::endl; }
void g() override { std::cout << "mode 1 - g" << std::endl; }
};
struct Mode2: Behavior {
void f() override { std::cout << "mode 2 - f" << std::endl; }
void g() override { std::cout << "mode 2 - g" << std::endl; }
};
struct Device {
template<typename B>
void set() { behavior = std::unique_ptr<Behavior>{new B}; }
void f() { behavior->f(); }
void g() { behavior->g(); }
private:
std::unique_ptr<Behavior> behavior{new NullBehavior};
};
int main() {
Device device;
device.f();
device.g();
device.set<Mode1>();
device.f();
device.g();
device.set<Mode2>();
device.f();
device.g();
}
从设备用户的角度来看,你使用的模式是什么并不重要。无论如何,根据要求,您可以随时动态更改它,从那时起,您的设备将开始使用新模式。
首选组合而非继承解决了名称冲突带来的问题。将从外部类到内部状态的所有内容委派完成其余工作
请注意,如果您想在状态之间共享方法,没有什么可以阻止您将它们放在基类中。
略有不同的版本可以帮助您在两者之间共享数据:
struct Data {
volatile uint8_t& reg1;
volatile uint8_t& reg2;
uint8_t data;
};
struct Behavior {
virtual void f(Data &) = 0;
virtual void g(Data &) = 0;
};
struct NullBehavior: Behavior {
void f(Data &) override {}
void g(Data &) override {}
};
struct Mode1: Behavior {
void f(Data &) override { /* ... */ }
void g(Data &) override { /* ... */ }
};
struct Mode2: Behavior {
void f(Data &) override { /* ... */ }
void g(Data &) override { /* ... */ }
};
struct Device {
template<typename B>
void set() { behavior = std::unique_ptr<Behavior>{new B}; }
void f() { behavior->f(data); }
void g() { behavior->g(data); }
private:
Data data{};
std::unique_ptr<Behavior> behavior{new NullBehavior};
};
对于特定模式唯一的所有参数都可以是类定义的一部分,或者放在Data
中,如果您在不同的模式下工作,则会被忽略。
- std::具有相同基类的类的变体
- 是否可以初始化不可复制类型的成员变量(或基类)
- 在C++中,是否可以基于给定的标识符创建基类的新实例,反之亦然
- 基类中的函数名称解析
- C++初始化基类
- 如何通过派生类函数更改基类中的向量
- 如何定义一个纯抽象基类
- 如何使用基类指针引用派生类成员
- 继承:构造函数,初始化C++11中基类的类C数组成员
- 使用基类指针创建对象时,缺少派生类析构函数
- 如何引用基类的派生类?
- 如果基类包含双指针成员,则派生类的构造函数
- 在模板基类中为继承类中的可选重写生成虚拟方法
- 为什么此派生对象无法访问基类的后递减方法?
- 公开最直接的基类模板名称
- 当基类是依赖类型时,这是一个缺陷吗
- 如何使基类的运算符对基类的可变参数数可见(请参阅下面的代码)?
- 模板基类中的静态变量
- C++ 继承:将子类传递给需要基类的函数并获取子类行为
- 继承和友元函数,从基类访问受保护的成员