C++ 从 base 类 多重继承,成员同名

C++ multiple inheritance from base classes with members with same name

让我告诉你我遇到的问题。我正在设计一组 classes 来控制数字设备。该设备可以在两种操作模式下工作。在第一种模式下,它可以执行一组特定的操作,而在第二种模式下,它可以执行另一组操作(两者之间可能有一些共同的操作)。我还可以在 运行 上更改设备的模式,这样我就可以在必要时在两种模式之间切换。独立于模式,设备使用同一组寄存器。

我正在考虑为每个模式使用一个基础 class 来解决这个问题,这样当我需要第一组操作时我可以拥有模式 1 的对象,而当我需要第二组操作时我可以拥有模式 2 的对象一组操作。然后我可以从这两个基础 class 派生一个 class,这样我就可以拥有执行所有操作的对象。

我的设计存在的问题是两个基 classes 有一些共同的功能和对相同寄存器的引用。因为我不能阻止成员的继承,所以我会在派生的 class 中有重复项。我知道我可以选择使用范围运算符访问哪个副本,但我仍然认为这是一个糟糕的设计。

所以我的问题是:是否有解决此问题的惯用方法?

如果没有正确或简单的方法来解决这个问题,我正在考虑独立地分层设计 3 classes。我会有一些重复的代码,但这不是什么大问题,对吧?

下面的代码(简化)用于说明:

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 具有共同的 operation2 和所有数据成员。

状态设计模式看起来很适合您的案例。
作为一个最小的工作示例:

#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();
}

从设备用户的角度来看,使用什么模式并不重要。无论如何,根据要求,您可以随时动态更改它,您的设备将从那时起开始使用新模式。
首选组合而不是继承解决了由于名称冲突引起的问题。将外部 class 中的所有内容委托给内部状态即可完成剩下的工作。

请注意,如果您想在状态之间共享方法,没有什么能阻止您将它们放在基础中 class。

略有不同的版本可以帮助您在两者之间共享数据:

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};
};

对于特定模式唯一的所有这些参数都可以是 class 定义的一部分,或者放在 Data 中,如果您在不同的模式下工作,则将被忽略。

我将 mode1mode2 的公共部分放在一个公共基础 class 中,比方说 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 }
    };
};