c++ - 如何消除这种与继承相关的代码异味?

标签 c++ c++11 inheritance

我需要实现很多具有不同 const 成员数据的派生类。数据处理应该在基类中处理,但我找不到访问派生数据的优雅方法。下面的代码可以运行,但我真的不喜欢它。

代码需要在小型嵌入式环境中运行,因此无法广泛使用堆或 Boost 等花哨的库。

class Base
{
  public:
    struct SomeInfo
    {
        const char *name;
        const f32_t value;
    };

    void iterateInfo()
    {
        // I would love to just write
        // for(const auto& info : c_myInfo) {...}

        u8_t len = 0;
        const auto *returnedInfo = getDerivedInfo(len);
        for (int i = 0; i < len; i++)
        {
            DPRINTF("Name: %s - Value: %f \n", returnedInfo[i].name, returnedInfo[i].value);
        }
    }
    virtual const SomeInfo* getDerivedInfo(u8_t &length) = 0;
};

class DerivedA : public Base
{
  public:
    const SomeInfo c_myInfo[2] { {"NameA1", 1.1f}, {"NameA2", 1.2f} };

    virtual const SomeInfo* getDerivedInfo(u8_t &length) override
    {
        // Duplicated code in every derived implementation....
        length = sizeof(c_myInfo) / sizeof(c_myInfo[0]);
        return c_myInfo;
    }
};

class DerivedB : public Base
{
  public:
    const SomeInfo c_myInfo[3] { {"NameB1", 2.1f}, {"NameB2", 2.2f}, {"NameB2", 2.3f} };

    virtual const SomeInfo *getDerivedInfo(u8_t &length) override
    {
        // Duplicated code in every derived implementation....
        length = sizeof(c_myInfo) / sizeof(c_myInfo[0]);
        return c_myInfo;
    }
};

DerivedA instanceA;
DerivedB instanceB;
instanceA.iterateInfo();
instanceB.iterateInfo();

最佳答案

您在这里不需要任何虚拟或模板。只需将 SomeInfo* 指针及其长度添加到 Base,并提供一个 protected 构造函数来初始化它们(由于没有默认构造函数,因此不可能忘记初始化它们)。

构造函数被保护不是硬性要求,但由于 Base 不再是抽象基类,使构造函数 protected 会阻止 Base 被实例化。

class Base
{
public:
    struct SomeInfo
    {
        const char *name;
        const f32_t value;
    };

    void iterateInfo()
    {
        for (int i = 0; i < c_info_len; ++i) {
            DPRINTF("Name: %s - Value: %f \n", c_info[i].name,
                     c_info[i].value);
        }
    }

protected:
    explicit Base(const SomeInfo* info, int len) noexcept
        : c_info(info)
        , c_info_len(len)
    { }

private:
    const SomeInfo* c_info;
    int c_info_len;
};

class DerivedA : public Base
{
public:
    DerivedA() noexcept
        : Base(c_myInfo, sizeof(c_myInfo) / sizeof(c_myInfo[0]))
    { }

private:
    const SomeInfo c_myInfo[2] { {"NameA1", 1.1f}, {"NameA2", 1.2f} };
};

class DerivedB : public Base
{
public:
    DerivedB() noexcept
        : Base(c_myInfo, sizeof(c_myInfo) / sizeof(c_myInfo[0]))
    { }

private:
    const SomeInfo c_myInfo[3] {
        {"NameB1", 2.1f},
        {"NameB2", 2.2f},
        {"NameB2", 2.3f}
    };
};

您当然可以使用小型、零开销的包装器/适配器类来代替 c_infoc_info_len 成员,以提供更好和更安全的访问(例如 begin()end() 支持),但这超出了这个答案的范围。

正如 Peter Cordes 指出的那样,这种方法的一个问题是,如果您的最终代码仍然使用虚拟对象(virtual你没有在你的帖子中展示过的函数。)如果没有虚拟对象,那么对象大小只会增加一个int。您确实说过您在一个小型嵌入式环境中,因此如果这些对象中有很多同时处于事件状态,那么这可能需要担心。

Peter 还指出,由于您的 c_myInfo 数组是 const 并且 使用常量初始化器,因此您不妨将它们设为 static 。这会将每个派生对象的大小减少数组的大小。

关于c++ - 如何消除这种与继承相关的代码异味?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/56627438/

相关文章:

c++ - 如何为自定义字符串类型编写构造函数/赋值运算符重载?

c++ - 获取类变量和类对象起始地址之间地址差异的最佳方法是什么?

java - 如何通过使用C++中的构造函数获取父类的变量来在子类中实现抽象类方法

java - 继承远程 webdriver 语法

c++ - 使用 std::find() 搜索多个元素

c++ - Visual Studio 中的 pthread_create 错误

windows - mingw std::thread 与 Windows API

c++ - 如何正确使用C++ 11风格的内存池?

java - 对类进行子类化以更改 Kotlin 中类型参数的方差

c++ - 高效移位或大位 vector