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