请考虑以下事项:
class fooBase{
public:
enum dataType {fooInt, fooFloat, fooInvalid};
fooBase(){}
/* void getLocation .. setLocation .. */
virtual dataType getDataType() = 0;
private:
int fooDataLocation;
};
template <typename T> class fooDataPoint : public fooBase{
public:
fooDataPoint(T foodat) :
fooData(foodat){}
dataType getDataType(){
if(typeid(float) == typeid(T))
return fooFloat;
if(typeid(int) == typeid(T))
return fooInt
return fooInvalid;
}
private:
T fooData;
};
template <typename T> class calculatableFooDataPoint : public fooBase{
public:
fooDataPoint(T foodat, T bardat) :
fooData(foodat){}
dataType getDataType(){
if(typeid(float) == typeid(T))
return fooFloat;
if(typeid(int) == typeid(T))
return fooInt
return fooInvalid;
}
void doCalculation(){
foodata *= barData;
}
/* .. getData .. */
private:
T fooData;
T barData;
};
std::vector<foobase *> fooVector;
这将使我能够在向量中存储不同类型的数据。然后,当我需要强制转换时,而不是通过无穷无尽的dynamic_case<class<type> *>(pointer)
列表,我可以先检查数据类型,然后再转换为正确的类类型。
同样在序列化和反序列化数据点时(我只需要保存结构,而不是已经处理的实际数据),我可以使用getDataType来确定类型并编写它的序列化形式。
我想我的底线是,使用typeid(类型)比较一个坏主意,为什么?
其次,是否有可以涵盖此类案例的设计模型?
答案 0 :(得分:1)
我想我的底线是,使用typeid(类型)比较一个坏主意,为什么?
在这种情况下,我相信它是。 T
的类型在编译时是已知的,因此没有理由通过一系列if
来确定您已经知道的内容。你可以为此编写一个简单的元函数。此外,不需要使用动态分派来确定函数的类型,您可以将值存储在基数中:
class fooBase {
enum dataType { fooInt, fooFloat, … };
const dataType type;
fooBase(dataType t) : type(t) {}
…
dataType getDataType() const { return type; }
};
template <typename T>
struct DataType;
template <> struct DataType<int>
{ static const dataType value = fooBase::fooInt; };
template <> struct DataType<float>
{ static const dataType value = fooBase::fooFloat; };
然后dataPoint
的构造函数将是:
template <typename T>
fooDataPoint<T>::fooDataPoint(T data)
: fooBase(DataType<T>::value)
, fooData(data)
{}
除此之外,设计对于可扩展性并不是那么好,因为需要为您可能想要包含的任何类型T
更新基础(扩展枚举),但如果该集合是提前知道这可能不会造成太多负担。
如果选项的数量有限,和对象的大小不是太大,你可以使用像boost::variant
这样的预建解决方案,它的优势在于它不会需要动态分配,它不会强制层次结构。潜在的缺点是,作为一个有区别的联合,每个对象的大小大约是它所拥有的最大类型的大小(sizeof
)。