Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/cplusplus/137.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++ 如何删除与继承相关的代码气味?_C++_C++11_Inheritance - Fatal编程技术网

C++ 如何删除与继承相关的代码气味?

C++ 如何删除与继承相关的代码气味?,c++,c++11,inheritance,C++,C++11,Inheritance,我需要用不同的常量成员数据实现许多派生类。数据处理应该在基类中处理,但我找不到一种优雅的方法来访问派生数据。下面的代码正在运行,但我真的不喜欢它 代码需要在小型嵌入式环境中运行,因此不能广泛使用堆或类似Boost的奇特库 class Base { public: struct SomeInfo { const char *name; const f32_t value; }; void iterateInfo() {

我需要用不同的常量成员数据实现许多派生类。数据处理应该在基类中处理,但我找不到一种优雅的方法来访问派生数据。下面的代码正在运行,但我真的不喜欢它

代码需要在小型嵌入式环境中运行,因此不能广泛使用堆或类似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
{
常量字符*名称;
常数f32_t值;
};
void iterateInfo()
{
//我很想写一封信
//对于(const auto&info:c_myInfo){…}
u8_t len=0;
const auto*returnedInfo=getDerivedInfo(len);
对于(int i=0;i
您可以制作
Base
模板并获取常量数组的长度。大概是这样的:

template<std::size_t Length>
class Base
{
  public:
    struct SomeInfo
    {
        const char *name;
        const float value;
    };

    const SomeInfo c_myInfo[Length];

    void iterateInfo()
    {
        //I would love to just write
        for(const auto& info : c_myInfo) {
            // work with info
        }
    }
};
模板
阶级基础
{
公众:
结构SomeInfo
{
常量字符*名称;
常量浮点值;
};
const SomeInfo c_myInfo[长度];
void iterateInfo()
{
//我很想写一封信
用于(const auto&info:c_myInfo){
//使用信息
}
}
};
然后根据每个基类相应地初始化数组:

class DerivedA : public Base<2>
{
  public:
    DerivedA() : Base<2>{ SomeInfo{"NameA1", 1.1f}, {"NameA2", 1.2f} } {}
};

class DerivedB : public Base<3>
{
  public:
    DerivedB() : Base<3>{ SomeInfo{"NameB1", 2.1f}, {"NameB2", 2.2f}, {"NameB2", 2.3f} } {}
};
class-DerivedA:公共基础
{
公众:
DerivedA():Base{SomeInfo{“NameA1”,1.1f},{“NameA2”,1.2f}{}
};
B类:公共基
{
公众:
DerivedB():Base{SomeInfo{“NameB1”,2.1f},{“NameB2”,2.2f},{“NameB2”,2.3f}{}
};

然后像平常一样使用。此方法删除多态性,不使用堆分配(例如,no
std::vector
),就像用户SirNobbyNobbs请求的那样。

您可以将数据移动到类外部的二维数组中,并让每个类返回包含相关数据的索引

struct SomeInfo
{
    const char *name;
    const f32_t value;
};

const vector<vector<SomeInfo>> masterStore{
    {{"NameA1", 1.1f}, {"NameA2", 1.2f}},
    {{"NameB1", 2.1f}, {"NameB2", 2.2f}, {"NameB2", 2.3f}}
    };

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

        u8_t len = 0;
        auto index(getIndex());
        for(const auto& data : masterStore[index])
        {
            DPRINTF("Name: %s - Value: %f \n", data.name, data.value);
        }
    }
    virtual int getIndex() = 0;
};

class DerivedA : public Base
{
  public:

    int getIndex() override
    {
        return 0;
    }
};

class DerivedB : public Base
{
  public:

    int getIndex() override
    {
        return 1;
    }
};

DerivedA instanceA;
DerivedB instanceB;
instanceA.iterateInfo();
instanceB.iterateInfo();
struct SomeInfo
{
常量字符*名称;
常数f32_t值;
};
常量向量主存{
{{“NameA1”,1.1f},{“NameA2”,1.2f},
{{“NameB1”,2.1f},{“NameB2”,2.2f},{“NameB2”,2.3f}
};
阶级基础
{
公众:
void iterateInfo()
{
//我很想写一封信
//对于(const auto&info:c_myInfo){…}
u8_t len=0;
自动索引(getIndex());
用于(常量自动和数据:masterStore[索引])
{
DPRINTF(“名称:%s-值:%f\n”,data.Name,data.Value);
}
}
虚拟int getIndex()=0;
};
DerivedA类:公共基础
{
公众:
int getIndex()重写
{
返回0;
}
};
B类:公共基
{
公众:
int getIndex()重写
{
返回1;
}
};
速溶鹿蹄草;
DerivedB实例B;
instanceA.iterateInfo();
instanceB.iterateInfo();

此处不需要任何虚拟或模板。只需将
SomeInfo*
指针及其长度添加到
Base
,并提供一个受保护的构造函数来初始化它们(因为没有默认构造函数,所以不可能忘记初始化它们)

受保护的构造函数不是一个硬性要求,但由于
Base
不再是抽象基类,因此使构造函数受保护可防止
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}
    };
};
类基
{
公众:
结构SomeInfo
{
常量字符*名称;
常数f32_t值;
};
void iterateInfo()
{
对于(int i=0;i
当然,为了提供更好、更安全的访问(如
begin()
end()
支持),您可以使用一个小的、零开销的包装器/适配器类,而不是
c\u info
c\u info\u len
成员,但这超出了本答案的范围

正如Peter Cordes指出的那样,这种方法的一个问题是,如果最终代码仍然使用虚拟函数(您的帖子中没有显示的虚拟函数),那么派生对象现在会比指针大,再加上
int
的大小,那么对象大小只会增加
int
。你说过你在一个小的嵌入式环境中,所以如果这些对象中有很多是活动的
template<class Derived>
class impl_getDerivedInfo
  :public Base
{

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


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

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

};
struct SomeInfo
{
    const char *name;
    const f32_t value;
};

void processData(const SomeInfo* c_myInfo, u8_t len);
#include "SomeInfo.h"

void processData(const SomeInfo* c_myInfo, u8_t len)
{
    for (u8_t i = 0; i < len; i++)
    {
        DPRINTF("Name: %s - Value: %f \n", c_myInfo[i].name, c_myInfo[i].value);
    }
}
#include "SomeInfo.h"

struct A
{
    const SomeInfo info[2] { {"NameA1", 1.1f}, {"NameA2", 1.2f} };
    static const u8_t len = 2;
};

struct B
{
    const SomeInfo info[3] { {"NameB1", 2.1f}, {"NameB2", 2.2f}, {"NameB2", 2.3f} };
    static const u8_t len = 3;
};
#include "data.h"

int
main()
{
    A a;
    B b;
    processData(a.info, A::len);
    processData(b.info, B::len);
}
virtual const std::vector<SomeInfo>& getDerivedInfo() = 0;
virtual std::pair<SomeInfo*, SomeInfo*> getDerivedInfo() = 0;
template<class T>
struct ptr_range {
  std::pair<T*, T*> range_;
  auto begin(){return range_.first;}
  auto end(){return range_.second;}
};
virtual ptr_range<SomeInfo> getDerivedInfo() override
{
    return {std::begin(c_myInfo), std::end(c_myInfo)};
}
template<class T>
struct span {
  T* b = nullptr;
  T* e = nullptr;

  // these all do something reasonable:
  span()=default;
  span(span const&)=default;
  span& operator=(span const&)=default;

  // pair of pointers, or pointer and length:
  span( T* s, T* f ):b(s), e(f) {}
  span( T* s, size_t l ):span(s, s+l) {}

  // construct from an array of known length:
  template<size_t N>
  span( T(&arr)[N] ):span(arr, N) {}

  // Pointers are iterators:
  T* begin() const { return b; }
  T* end() const { return e; }

  // extended container-like utility functions:
  T* data() const { return begin(); }
  size_t size() const { return end()-begin(); }
  bool empty() const { return size()==0; }
  T& front() const { return *begin(); }
  T& back() const { return *(end()-1); }
};

// This is just here for the other array ctor,
// a span of const int can be constructed from
// an array of non-const int.
template<class T>
struct span<T const> {
  T const* b = nullptr;
  T const* e = nullptr;
  span( T const* s, T const* f ):b(s), e(f) {}
  span( T const* s, size_t l ):span(s, s+l) {}
  template<size_t N>
  span( T const(&arr)[N] ):span(arr, N) {}
  template<size_t N>
  span( T(&arr)[N] ):span(arr, N) {}
  T const* begin() const { return b; }
  T const* end() const { return e; }
  size_t size() const { return end()-begin(); }
  bool empty() const { return size()==0; }
  T const& front() const { return *begin(); }
  T const& back() const { return *(end()-1); }
};
class Base
{
public:
  void iterateInfo()
  {
    for(const auto& info : c_mySpan) {
        DPRINTF("Name: %s - Value: %f \n", info.name, info.value);
    }
  }
private:
  span<const char> c_mySpan;
  Base( span<const char> s ):c_mySpan(s) {}
  Base(Base const&)=delete; // probably unsafe
};
class DerivedA : public Base
{
public:
  const SomeInfo c_myInfo[2] { {"NameA1", 1.1f}, {"NameA2", 1.2f} };
  DerivedA() : Base(c_myInfo) {}
};
template<typename Derived>
class BaseT
{
  public:   
    struct SomeInfo
    {
        const char *name;
        const f32_t value;
    };

    void iterateInfo()
    {
        Derived* pDerived = static_cast<Derived*>(this);
        for (const auto& i: pDerived->c_myInfo)
        {
            printf("Name: %s - Value: %f \n", i.name, i.value);
        }
    }
};

class DerivedA : public BaseT<DerivedA>
{
  public:
    const std::array<SomeInfo,2> c_myInfo { { {"NameA1", 1.1f}, {"NameA2", 1.2f} } };
};

class DerivedB : public BaseT<DerivedB>
{
  public:
    const std::array<SomeInfo, 3> c_myInfo { { {"NameB1", 2.1f}, {"NameB2", 2.2f}, {"NameB2", 2.3f} } };
};