如果我有一个类,它的数据类型可能是int
、float
、double
、char[]
, std::string
, std::vector
... 现在我正在使用一个 enum
来指示哪种类型data 是一个 void*
来为数据动态分配内存。但是,我确信必须有一种更优雅的方式。不使用boost
如何实现?
最佳答案
实现“Variant”或“Any”类型,正如其他人指出的那样,您已经可以使用一些实现。但如果您不想使用 boost 或其他替代方案,您可以实现自己的简单版本。
您的类型需要 2 个结构,一个是您存储的基类,一个是保存实际对象的派生模板类。
让我们称它们为 Placeholder 和 Holder:
这是基本结构:
/**
* @brief The place holder structure..
*/
struct PlaceHolder
{
/**
* @brief Finalizes and instance of the PlaceHolder class.
*/
virtual ~PlaceHolder() {}
/**
* @brief Gets the type of the underlying value.
*/
virtual const std::type_info& getType() const = 0;
/**
* @brief Clones the holder.
*/
virtual PlaceHolder * clone() const = 0;
};
这将是派生类:
template<typename ValueType>
struct Holder: public PlaceHolder
{
/**
* @brief Initializes a new instance of the Holder class.
*
* @param ValueType The value to be holded.
*/
Holder(const ValueType & value) : held(value) {}
/**
* @brief Gets the type of the underlying value.
*/
virtual const std::type_info & getType() const
{
return typeid(ValueType);
}
/**
* @brief Clones the holder.
*/
virtual PlaceHolder * clone() const
{
return new Holder(held);
}
ValueType held;
};
现在我们可以:
PlaceHolder* any = new Holder<int>(3);
我们可以像这样从中取回值:
int number = static_cast<Holder<int> *>(any)->held;
这不是很实用,所以我们创建一个类来为我们处理所有这些东西,并向其中添加一些商品,我们称之为 Any:
/**
* @brief This data type can be used to represent any other data type (for example, integer, floating-point,
* single- and double-precision, user defined types, etc.).
*
* While the use of not explicitly declared variants such as this is not recommended, they can be of use when the needed
* data type can only be known at runtime, when the data type is expected to vary, or when optional parameters
* and parameter arrays are desired.
*/
class Any
{
public:
/**
* @brief Initializes a new instance of the Any class.
*/
Any()
: m_content(0)
{
}
/**
* @brief Initializes a new instance of the Any class.
*
* @param value The value to be holded.
*/
template<typename ValueType>
Any(const ValueType & value)
: m_content(new Holder<ValueType>(value))
{
}
/**
* @brief Initializes a new instance of the Any class.
*
* @param other The Any object to copy.
*/
Any(const Any & other)
: m_content(other.m_content ? other.m_content->clone() : 0)
{
}
/**
* @brief Finalizes and instance of the Any class.
*/
virtual ~Any()
{
delete m_content;
}
/**
* @brief Exchange values of two objects.
*
* @param rhs The Any object to be swapped with.
*
* @return A reference to this.
*/
Any& swap(Any & rhs)
{
std::swap(m_content, rhs.m_content);
return *this;
}
/**
* @brief The assignment operator.
*
* @param rhs The value to be assigned.
*
* @return A reference to this.
*/
template<typename ValueType>
Any& operator=(const ValueType & rhs)
{
Any(rhs).swap(*this);
return *this;
}
/**
* @brief The assignment operator.
*
* @param rhs The value to be assigned.
*
* @return A reference to this.
*/
Any & operator=(const Any & rhs)
{
Any(rhs).swap(*this);
return *this;
}
/**
* @brief The () operator.
*
* @return The holded value.
*/
template<typename ValueType>
ValueType operator()() const
{
if (!m_content)
{
//TODO: throw
}
else if (getType() == typeid(ValueType))
{
return static_cast<Any::Holder<ValueType> *>(m_content)->held;
}
else
{
//TODO: throw
}
}
/**
* @brief Gets the underlying value.
*
* @return The holded value.
*/
template<typename ValueType>
ValueType get(void) const
{
if (!m_content)
{
//TODO: throw
}
else if (getType() == typeid(ValueType))
{
return static_cast<Any::Holder<ValueType> *>(m_content)->held;
}
else
{
//TODO: throw
}
}
/**
* @brief Tells whether the holder is empty or not.
*
* @return <tt>true</tt> if the holder is empty; otherwise <tt>false</tt>.
*/
bool isEmpty() const;
{
return !m_content;
}
/**
* @brief Gets the type of the underlying value.
*/
const std::type_info& getType() const;
{
return m_content ? m_content->getType() : typeid(void);
}
protected:
/**
* @brief The place holder structure..
*/
struct PlaceHolder
{
/**
* @brief Finalizes and instance of the PlaceHolder class.
*/
virtual ~PlaceHolder() {}
/**
* @brief Gets the type of the underlying value.
*/
virtual const std::type_info& getType() const = 0;
/**
* @brief Clones the holder.
*/
virtual PlaceHolder * clone() const = 0;
};
template<typename ValueType>
struct Holder: public PlaceHolder
{
/**
* @brief Initializes a new instance of the Holder class.
*
* @param ValueType The value to be holded.
*/
Holder(const ValueType & value) : held(value) {}
/**
* @brief Gets the type of the underlying value.
*/
virtual const std::type_info & getType() const
{
return typeid(ValueType);
}
/**
* @brief Clones the holder.
*/
virtual PlaceHolder * clone() const
{
return new Holder(held);
}
ValueType held;
};
protected:
PlaceHolder* m_content;
};
此实现基于 Ogre 中的任何一个
你可以像这样使用它:
int main()
{
Any three = 3;
int number = three.get<int>();
cout << number << "\n";
three = string("Three");
std::string word = three.get<string>();
cout << word << "\n";
return 0;
}
输出:
3
Three
关于C++在成员变量中保存不同类型数据的优雅方式,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/18566066/