我有一个通常可以保存任何(基本)类型的类:
class Value
{
private:
int i_value;
unsigned int ui_value;
long l_value;
unsigned long ul_value;
short s_value;
float f_value;
double d_value;
char c_value;
bool b_value;
std::string str_value;
int type;
void setValue(int value);
void setValue(unsigned int value);
void setValue(long value);
void setValue(unsigned long value);
void setValue(short value);
void setValue(float value);
void setValue(double value);
void setValue(char value);
void setValue(bool value);
void setValue(std::string value);
public:
Value(int value);
Value(unsigned int value);
Value(long value);
Value(unsigned long value);
Value(short value);
Value(float value);
Value(double value);
Value(char value);
Value(bool value);
Value(std::string value);
Value(Value& other); //Copy Constructor
~Value();
int getType();
std::string toString(int format);
};
这很好,因为我可以做类似的事情:
Value * v1 = new Value(55);
Value * v2 = new Value(1.2);
Value * v3 = new Value("yes");
Value * v4 = new Value(true);
然而,正如你所看到的,它很丑陋;大量的超载使其成功。我在想模板可以使这个通用。但是,据我所知,你总是必须指明类型,这种类型会破坏班级的整个目的。
例如:
Value<int> * v1 = new Value<int>(55);
Value<double> * v2 = new Value<double>(1.2);
Value<string> * v3 = new Value<string>("yes");
Value<bool> * v4 = new Value<bool>(true);
如果我使用模板,我就不能像以前那样做vector<Value *>
之类的事情了。这是正确的,还是我错过了某些可能有助于这种情况的模板方面?
答案 0 :(得分:4)
您需要的只是模板一的父类:
class BaseValue
{
public:
virtual ~BaseValue()
{}
};
template<typename T>
class Value : public BaseValue
{
public:
Value(const T& value)
:m_value(value)
{}
void set(const T& value)
{
m_value = value;
}
const T& get()
{
return m_value;
}
virtual ~Value()
{}
private:
T m_value;
};
std::vector<BaseValue*> values;
values.push_back(new Value<int>(1)); // int
values.push_back(new Value<double>(1.0)); // double
values.push_back(new Value<char*>("asdf")); // pointer to array on stack :(
values.push_back(new Value<char>('c')); // char
答案 1 :(得分:3)
第一个问题:使用模板工厂功能而不是新功能。
第二个问题:使用一个共同的基类。
为了能够正确删除向量上指针所指向的对象,您需要一个虚拟析构函数。另外,为了对指向基类的指针做任何有用的事情,你需要在基类中使用虚方法。
示例:
class ValueBase
{
public:
virtual ~ValueBase() = default;
virtual void Print(std::ostream & os) const = 0;
};
std::ostream & operator<< (std::ostream & os, const ValueBase & value)
{
value.Print(os);
return os;
}
template<typename T> class Value : public ValueBase
{
T value;
public:
Value(const T & v) : value(v) {}
const T & Get() const;
void Set(const T & v);
void Print(std::ostream & os) const
{
os << value;
}
// ...
};
template<typename T> Value<T> * NewValue(const T & v)
{
return new Value<T>(v);
}
现在你可以做到
ValueBase * v1 = NewValue(55);
ValueBase * v2 = NewValue(1.2);
ValueBase * v3 = NewValue<std::string>("yes");
ValueBase * v4 = NewValue(true);
std::vector<ValueBase *> vec;
vec.push_back(v1);
vec.push_back(v2);
vec.push_back(v3);
vec.push_back(v4);
vec.push_back(NewValue(2350983444325345ll));
for (const auto & entry : vec)
{
std::cout << *entry << " ";
}
std::cout << "\n";
请注意,您通常不需要NewValue的模板参数,因为它将被推导出来。使用"yes"
时,这不起作用,因为Value<T>
将使用T = char [4]
进行实例化,这需要您在构造函数中使用strcpy
。我发现明确指出如上所述需要转换是非常好的。如果您隐含地喜欢这个,请执行重载:
Value<std::string> * NewValue(const char * v)
{
return new Value<std::string>(v);
}
确保手动删除内容
for (const auto & entry : vec)
{
delete entry;
}
vec.clear();
或使用std::unique_ptr
代替裸指针:
template<typename T> std::unique_ptr<Value<T>> UniqueValue(const T & v)
{
return std::unique_ptr<Value<T>>(new Value<T>(v));
}
std::vector<std::unique_ptr<ValueBase>> vec;
vec.push_back(NewValue(4.5));
如果扩展Value
并且事实证明你需要在析构函数中执行某些操作,则必须实现复制构造函数,赋值运算符以及可能的移动构造函数和移动赋值运算符。 (&#34;三个规则&#34;或&#34;五个规则&#34;。)在上面的版本中,&#34;零规则&#34;仍然适用,因为析构函数仍然与隐式定义的析构函数相同(= default
)。
如果添加析构函数,它应该是虚拟的。否则,如果删除VirtualBase
指针,可能会导致内存泄漏。
答案 2 :(得分:1)
当然你可以做模板,但你也需要做多态。
class StupidAndEmpty {} // add virtual destructor if Value needs a destructor
template<class dType>
Value : StupidAndEmpty {
// do smart things with dType
}
vector<StupidAndEmpty *> notSoStupid;
唯一的问题是当你从矢量中取回它们时如何使用它们。