c++ - C++中的 map ,可以接受任何类型的值

标签 c++ maps

我想在C ++中创建一个可以接受任何类型值的映射,我在Java中使用Object类进行了相同的操作
映射,但不了解如何使用c ++进行操作。
请帮忙。

最佳答案

正如前面的答案正确建议的那样,您不能在C ++中开箱即用。我假设“ [...]可以接受任何类型的值[...]”是指值,而不是映射的键。

不过,这是您可以做的。您有两个选择;我会从丑到善。

第一种方法:


创建一个值保存类,将其用作地图的value。我们称之为价值。
在该类中为您要支持的所有类型实现显式构造函数,并跟踪该类当前存储的值的类型
从地图上获取值后检查其类型,并使用适当的getter函数
(可选)重载<<运算符以支持标准流


有关示例实现,请参见以下代码:

#include <iostream>
#include <memory>
#include <map>

class Value {
public:
  typedef enum {
    String,
    Integer,
    Double,
    Float
  } ContentType;

private:
  ContentType m_ctType;

  std::string m_strContent;
  int m_nContent;
  double m_dContent;
  float m_fContent;

public:
  Value() : m_strContent(""), m_ctType(String) {}
  explicit Value(const char* arrcString) : m_strContent(std::string(arrcString)), m_ctType(String) {}
  explicit Value(std::string strContent) : m_strContent(strContent), m_ctType(String) {}
  explicit Value(int nContent) : m_nContent(nContent), m_ctType(Integer) {}
  explicit Value(double dContent) : m_dContent(dContent), m_ctType(Double) {}
  explicit Value(float fContent) : m_fContent(fContent), m_ctType(Float) {}

  ~Value() {}

  ContentType type() {
    return m_ctType;
  }

  std::string stringValue() { return m_strContent; }
  int integerValue() { return m_nContent; }
  double doubleValue() { return m_dContent; }
  float floatValue() { return m_fContent; }
};

std::ostream& operator<<(std::ostream& osStream, Value& valOut) {
  switch(valOut.type()) {
  case Value::String: osStream << valOut.stringValue(); break;
  case Value::Integer: osStream << valOut.integerValue(); break;
  case Value::Double: osStream << valOut.doubleValue(); break;
  case Value::Float: osStream << valOut.floatValue(); break;
  }

  return osStream;
}


可以这样使用:

int main() {
  std::map<int, Value> mapAnyValue;

  mapAnyValue[0] = Value("Test");
  mapAnyValue[1] = Value(1337);

  std::cout << mapAnyValue[0] << ", " << mapAnyValue[1] << std::endl;

  return 0;
}


这个输出

Test, 1337


现在有人可能会说这是


效率低下(它为每个Value实例中未使用的类型保留了字段)
难以扩展/维护(添加新字段有点麻烦)
总体设计不好


他们是对的。因此,这是使用多态性和模板的替代方法。

第二种方法:

这要求您定义将值分配给变量时要存储的值的类型,并且需要使用指针。原因如下。

对于这种方法,我们执行以下操作:


创建一个基类ValueBase,该基类用作我们可以将其作为值类型放入地图中的类。
从此类派生一个模板化类Value<T>,该类拥有模板类型T的任意值。
为了支持std::cout和朋友,我们为<<类实现了ValueBase的运算符重载,向output添加了纯虚拟ValueBase函数,并在Value<T>中覆盖了此函数以使用默认值<<运算符,用于模板中使用的任何类型。


参见下面的代码示例:

#include <iostream>
#include <memory>
#include <map>

class ValueBase {
public:
  ValueBase() {}
  ~ValueBase() {}

  virtual void output(std::ostream& osStream) = 0;
};

template<typename T>
class Value : public ValueBase {
private:
  T m_tValue;

public:
  Value(T tValue) : m_tValue(tValue) {}
  ~Value() {}

  T value() {
    return m_tValue;
  }

  void output(std::ostream& osStream) override {
    osStream << m_tValue;
  }
};

std::ostream& operator<<(std::ostream& osStream, ValueBase& valOut) {
  valOut.output(osStream);

  return osStream;
}


可以这样使用:

int main() {
  std::map<int, std::shared_ptr<ValueBase>> mapAnyValue;

  mapAnyValue[0] = std::make_shared<Value<std::string>>("Test");
  mapAnyValue[1] = std::make_shared<Value<int>>(1337);

  std::cout << *mapAnyValue[0] << ", " << *mapAnyValue[1] << std::endl;

  return 0;
}


或没有智能指针:

int main() {
  std::map<int, ValueBase*> mapAnyValue;

  mapAnyValue[0] = new Value<std::string>("Test");
  mapAnyValue[1] = new Value<int>(1337);

  std::cout << *mapAnyValue[0] << ", " << *mapAnyValue[1] << std::endl;

  delete mapAnyValue[0];
  delete mapAnyValue[1];

  return 0;
}


两者输出

Test, 1337


第二种方法在用法上有几个差异。

首先,您需要使用指针。原因是这样,成员函数vtable被保留,您可以从派生类重写基类中的函数。在我们的情况下,这意味着:当我们在初始化为output()的类型为ValueBase的指针上调用Value<T>时,将使用output()中的Value<T>函数,而不是ValueBase中的函数。如果使用普通变量而不是指针,则将使用output()中的ValueBase函数,并且我们将从派生类中丢失信息。

其次,这与第一个有关,您需要引用使用该值时获得的指针。如果要使用ValueBase输出Value<T>std::cout指针,则需要将其作为std::cout << *var来输出包含的值。如果您只是执行std::cout << var,则可以正确获取指针的地址。

我确定还有其他选择,尤其是在使用Boost时,但是我对此并不熟练。其他人可能对此有更多有价值的信息。

除此之外,您正在做的事情听起来像是懒惰。 C ++具有强类型系统是有原因的。它不仅定义明确,而且您还知道对代码的期望。如果您开始使事情变得模糊,并使用任意容器对象执行各种任务,那么您的代码将失去可读性,清晰度,并且(很可能)会产生大量难以跟踪,调试和最终修复的错误,因为您需要支持您介绍的所有精美容器,以保持框架运行。

如果要使用Java之类的语言,最好使用Java而不是C ++。

关于c++ - C++中的 map ,可以接受任何类型的值,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/35192561/

相关文章:

maps - 具有多个图层和不同属性名称的 WFS GetFeature

c++ - 使用映射的迭代器混淆

c++ - 四元数的标准位置?

C++ - 基于距离的对象最快排序算法

javascript - 如何获得完整的图像图,包括平移、放大、缩小?

javascript - 让用户在谷歌地图上画曲线?

java - 如何使用 Selenium Java 自动化 map ?或使用页面坐标。请告诉我是否还有其他方法可以自动化 map

c++ - LNK2019 : Calling C function from CPP code

c++ - 返回字符串流 (char*)

c++ - 处理从十六进制到十六进制的转换