C++在成员变量中保存不同类型数据的优雅方式

标签 c++ boost c++11

如果我有一个类,它的数据类型可能是intfloatdoublechar[] , 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/

相关文章:

c++ - 如何强制在编译时评估 constexpr 函数

c++ unordered_map vector 下标超出范围

c++ - 将模板类作为参数传递给类方法并使用模板类作为 vector 模板类型

c++ - boost - "static"与 "shared"库

c++ - C 字符串堆栈分配

c++ - "explicit"和 "implicit"调用operator()的区别

c++ - 使用 constexpr 而不仅仅是静态 const 变量还能提供什么?

c++ - 操作后恢复 std::cout 的状态

c++ - 使用 boost 将几何体切割成碎片

c++ - 获取当前时间作为 YYYY-MM-DD-HH-MM-SS 字符串