C ++中的地图,可以接受任何类型的值 [英] A map in c++ which can accept any type of value

查看:38
本文介绍了C ++中的地图,可以接受任何类型的值的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I want to create a map in c++ which can accept any type of value, i did the same in java using Object class Map but not getting how to do it in c++. Please help.

推荐答案

如上一个正确答案所示,您不能在C ++中立即使用它.我假设通过"[...]可以接受任何类型的值[...]" ,您的意思是值,而不是映射的键.

As the previous answer correctly suggested, you can't do it out of the box in C++. I am assuming that by "[...] which can accept any type of value [...]" you mean the value, not the key of the map.

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

Here is what you can do, though. You have two options; I'll go from ugly to nice.

第一种方法:

  • 创建一个值保存类,将其用作地图的 value .我们称之为.

在该类中为您要支持的所有类型实现显式构造函数,并跟踪该类当前存储的值的类型

Implement explicit constructors in that class for all types you want to support, and keep track on the type of value the class is currently storing

从地图获取值后检查值的类型,并使用适当的getter函数

Check the value's type after getting it from the map and use the appropriate getter function

可选地,重载 << 运算符以支持标准流

Optionally, overload the << operator to support standard streams

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

For a sample implementation, see the following code:

#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

现在有些人可能会认为这是

Now some might argue that this is

  • 效率低下(它为每个 Value 实例中未使用的类型保留了字段)
  • 难以扩展/维护(添加新字段有点麻烦)
  • 总体上不良的设计
  • Inefficient (it reserved fields for types that are not used in every Value instance)
  • Difficult to extend/maintain (adding new fields is kind of cumbersome)
  • In general bad design

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

and they are right. So here is an alternative using polymorphism and templates.

第二种方法:

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

This requires you to define the type of value you want to store when assigning it to a variable, and it requires the use of pointers. The reasons are given below.

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

For this approach, we do the following:

  • 创建一个基类 ValueBase ,该基类用作我们可以作为 value 类型放入地图中的类.

  • Create a base class ValueBase that serves as a class we can put in our map as value type.

从此类中派生一个模板化类 Value< T> ,该类具有模板类型 T 的任意值.

Derive from this class a templated class Value<T> that holds an arbitrary value of template type T.

为了支持 std :: cout 和朋友,我们为 ValueBase 类的<< 实现了一个运算符重载,在 ValueBase 中添加一个纯虚拟的 output 函数,并在 Value< T> 中覆盖此函数,以使用默认的<< 运算符,用于您在模板中使用的任何类型.

To support std::cout and friends, we implement an operator overloading for << for class ValueBase, add a pure virtual output function to ValueBase, and override this function in Value<T> to make use of the default << operator for any type you are using in the template.

请参见下面的代码示例:

See below for a code sample:

#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

第二种方法在用法上有一些差异.

There are a couple of differences in terms of usage for the second approach.

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

First of all, you need to use pointers. The reason for this is that this way, the member function vtable is preserved and you can override functions in the base class from derived classes. In our situation, this means: When we call output() on a pointer of type ValueBase that was initialized as a Value<T>, the output() function from Value<T> is used rather than from ValueBase. If you used normal variables instead of pointers, the output() function from ValueBase would be used, and we lose the information from the derived class.

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

Second, and this is related to the first one, you need to reference the pointer you get when using the value. If you want to output a ValueBase or Value<T> pointer with std::cout, you need to do it as std::cout << *var to output the contained value. If you just did std::cout << var, you would correctly get the address of the pointer instead.

我确定还有其他选择,尤其是在使用 Boost 时,但我不是专家那.其他人可能对此有更多有价值的信息.

I'm sure there are other options, especially when using Boost, but I'm no expert on that. Someone else might have more valuable information on that.

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

Other than that, what you are doing sounds like an act of laziness. C++ has a strongly typed system for a reason; not only is it well-defined, but you also know what to expect from your code. If you start making things fuzzy and use arbitrary container objects for all kinds of tasks, your code will lose readability, clarity, and will (most probably) produce numerous bugs that are very difficult to trace, debug, and in the end fix, because you need to support all the fancy containers you introduced to keep your framework running.

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

If you want to use a language like Java, its better to use Java instead of C++.

这篇关于C ++中的地图,可以接受任何类型的值的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆