c++ - 将任何数据类型传递给 C++ 中的函数

标签 c++ pointers struct

出于我的一个项目的目的,我想制作一个可以包含任何类型数据的 typedef struct。我制作了一个存储在 enum 中的数据类型,以及一个实际存储数据的 void *

在我目前的尝试中,它与 int 一起工作得很好,但是当我尝试添加其他类型如 std::stringfloat 时,它工作得不太好.

我已经尝试了几种不同的方法来尝试将数据放入容器并将其取出,但到目前为止没有一种方法适用于所有情况。

最好的方法是什么?我想将任何类型的数据填充到一个结构中,并能够以相同的形式检索数据。

编辑:

typedef struct{
    DataTypeInt,
    DataTypeFloat,
    DataTypeString
}DataType;

typedef struct{
    DataType type;
    void *data;
}Data;

起初我只用整数进行测试,我会简单地使用 (void *)foo 来输入数据。然后放入(int)bar.data 就可以把数据取出来了。这对 int 类型非常有效,但如果我尝试 std::stringfloat

我会收到编译器错误

在摸索了不同的指针和转换配置,并搜索了互联网之后,我最终简单地放置了 foo.data = &bar 来输入数据和 float foo = *(float* )bar.data 来获取数据。这没有给出任何错误,但数据没有正确输出。通过另一侧的任何 intfloat 都将是 0 并且字符串将是随机符号。

我还尝试了这两种方法,使用包装器结构来包含每种数据类型,然后将其作为数据传递。

最佳答案

1) 如果您需要变体,并且不介意使用 Boost,请考虑使用 Boost.Variant 或 Boost.Any。它干净、健壮且成熟。

2) 使用重载实现非 union 变体。 (真正的 union 的问题是针对非 POD 类型,它丑陋到连经验丰富的开发人员都忘记了如何正确地做)。最好远离。

class Variant 
{
    int i;
    float j;
    std::string s;

    // Add all of your typed getter and setters
}

通过重载,您可以创建一个非 union 包装类,每个类型都有一个成员,然后为每个类型添加带有覆盖的 getter/setter 方法。只要您不打算创建数百万个这种类型,它就比使用 void * 更安全。

3) 使用子类型多态性 - 指定所有不同可能类型的基类,然后使用特定实现进行扩展。

class VariantBase
{
public:
    virtual int          get_int()    { throw new "Not implemented"; }
    virtual float        get_float()  { throw new "Not implemented"; }
    virtual std::string  get_string() { throw new "Not implemented"; }
}

class VariantInt
{
    int val;
public:
    virtual int          get_int()      { return val; }
    virtual void         set_int(int i) { val = i; }
}

class VariantString
{
   ...
}

4) 如果您习惯了 C#,其中所有类型都派生自 System.Object,那么与 C++ 中最接近的是 void *但是,与 C# 不同的是,您在 void * 中存储的内容会暂时失去其类型,并且您无法使用反射。更重要的是,如果你踩它,垃圾收集器不会救你。因此,与其只希望最好,不如围绕 void * 实现一个安全的 Variant 包装器。保护您免受自己的伤害。

你可以把一个对象放入void *

void * vp = obj;

但要将其输出,您需要使用 C++ 强制转换。

MyClass * obj = static_cast<MyClass*>(vp);

您可以使用 void * 实现安全变体在 C++ 的规则之内。请记住,有 Boost.Variant (1)。我把它放在这里只是为了演示。这是危险的,通常意味着您忽略了其他有效的解决方案。但是,C 程序员经常使用该技术。 C++ 语言的创建者 Bjarne Stroustrup 明确认为这是合法且有值(value)的,我认为它只是 C++ 的一部分。这里的总体思路是一个简单的 3 类型变体。更好的 C++ 开发人员会使用模板来做很多这样的事情,但是由于您是 C++ 的新手,所以它是一种非模板解决方案,我希望您能理解。使用 void * 时在尝试转换指针之前,您必须始终确定指针指的是什么类型,因此一个好的规则是将所有访问器包装在一个验证枚举值的函数中。首先检查您的枚举值,如果不匹配(有人在字符串上调用 get_int)则抛出异常,尽管某些变体支持转换。由于您正在处理指针,因此 set_val_type() 方法将首先删除旧内存。注意 static_cast<> 的使用,如果您想要干净、定义好的代码,这是在 C++ 中转换 void * 的唯一方法。

enum VariantValType { None, Int, Float, StdString };

class Variant
{
     VariantValType valType;
     void *val;
public:
     Variant() : val(nullptr), valType(None) {}
     ~Variant() { reset_val(); }

     void set_val_type(VariantValType t) {
          if (t == valType)
               return;
          reset_val();
          valType = t;
     }

     void reset_val() {
          switch (valType) {
          case None:          break;
          case Int:           delete (static_cast<int*>(val)); break;
          case Float:         delete (static_cast<float*>(val)); break;
          case StdString:     delete (static_cast<std::string*>(val)); break;
          default:            throw "Unknown value type"; break;
          }
          valType = None;
          val = nullptr;
     }

     int get_int() {
          if (valType != Int)
               throw "Variant type mismatch";
          return *(static_cast<int*>(val));
     }

     void set_int(int i) {
          set_val_type(Int);
          val = new int{ i };
     }

     float get_float() {
          if (valType != Float)
               throw "Variant type mismatch";
          return *(static_cast<float*>(val));
     }

     void set_float(float f) {
          set_val_type(Float);
          val = new float{ f };
     }

     std::string& get_string() {
          // If you like, implement conversion here with std::to_string() like so
          // if(valType == Int) return std::to_string(*(static_cast<int*>(val)));

          if (valType != StdString)
               throw "Variant type mismatch";
          return *(static_cast<std::string*>(val));
     }

     void set_string(const std::string& s) {
          set_val_type(StdString);
          val = new std::string(s);
     }
};

关于c++ - 将任何数据类型传递给 C++ 中的函数,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/26580791/

相关文章:

json - 动态创建某种类型的结构并将 JSON 解码到该实例中

c++ - 如何在不干扰滚动的情况下创建带有按钮的 ScrollView ?

c++ - OwnerDraw 与 WS_EX_COMPOSITED 在 XP 下

c++ - 如何使 8 位位图在 C++ 中显示为单色?

c++ - 唯一指针作为默认参数

c++ - &array[size] 完全安全吗?

c++ - 在控制台上显示 C++ 中 .txt 文件的动态变化

c - 内存分配?

c++ - 作为类私有(private)成员的无名结构/union

c - 戈朗 : pack a struct