[英]C++ multiple inheritance from base classes with members with same name
讓我告訴你我的問題。 我正在設計一組控制數字設備的類。 該設備可以在兩種操作模式下工作。 在第一模式中,它可以執行一組特定的操作,而在第二模式中,它可以執行另一組操作(在兩者之間可能有一些共同的操作)。 我也可以在運行時更改設備的模式,因此如有必要,我可以在兩種模式之間切換。 與模式無關,設備使用相同的寄存器集。
我正在考慮用每個模式的一個基類來解決這個問題,所以當我需要第一組操作和模式2的對象時,當我需要第二組操作時,我可以擁有模式1的對象。 然后我可以從這兩個基類派生一個類,所以我可以擁有執行所有操作的對象。
我的設計的問題是兩個基類有一些共同的功能和對相同寄存器的引用。 由於我無法阻止成員的繼承,因此我會在派生類中重復。 我知道我可以選擇使用范圍運算符訪問哪些副本,但我仍然認為這是一個糟糕的設計。
所以我的問題是:是否有一種解決這個問題的慣用方法?
如果沒有正確或簡單的方法來解決這個問題,我正在考慮設計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具有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();
}
從設備用戶的角度來看,您使用的模式無關緊要。 無論如何,根據要求,您可以隨時動態更改它,並且您的設備將從此時開始使用新模式。
由於名稱沖突,優先考慮組合而不是繼承解決了這個問題。 委托從外部類到內部狀態的所有內容完成其余的工作。
請注意,如果要在狀態之間共享方法,則不會阻止您將它們放在基類中。
略有不同的版本可以幫助您在兩者之間共享數據:
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
,如果您在其他模式下工作則忽略。
我將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 }
};
};
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.