出于我的一个项目的目的,我想制作一个可以包含任何类型数据的typedef struct
。我创建了一个存储在enum
中的数据,以及实际保存数据的void *
。
在我目前的尝试中,它在整数方面效果很好,但是当我尝试添加其他类型时,例如std::string
或float
,它的效果并不好。
我尝试过几种不同的尝试将数据放入容器并将其取出的方法,但到目前为止还没有一种方法适用于所有内容。
最好的方法是什么?我想将任何类型的数据填充到结构中,并能够以相同的形式检索数据。
编辑:
typedef struct{
DataTypeInt,
DataTypeFloat,
DataTypeString
}DataType;
typedef struct{
DataType type;
void *data;
}Data;
起初我只使用整数进行测试,我只需使用(void *)foo
来输入数据。然后放(int)bar.data
以获取数据。这对于int
类型非常有效,但如果我尝试std::string
或float
在使用不同的指针和转换配置并搜索互联网后,我最终只是简单地foo.data = &bar
输入数据,float foo = *(float*)bar.data
来获取数据。这并没有给出任何错误,但数据并没有正确显示。来自另一方的任何int
或float
都是0
,字符串将是随机符号。
我还尝试使用包装器结构的这两个方法来包含每种数据类型,然后将其作为数据传递。
答案 0 :(得分:2)
1)如果您需要变体,并且不介意使用Boost,请考虑Boost.Variant或Boost.Any。它干净,健壮,成熟。
2)使用重载来实现非联合变体。 (真正的工会的问题是针对非POD类型,它的丑陋甚至经验丰富的开发人员忘记了如何做到这一点)。最好远离。
class Variant
{
int i;
float j;
std::string s;
// Add all of your typed getter and setters
}
使用重载创建一个非联合包装类,每个类型都有一个成员,然后添加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);
您可以在C ++规则中使用void *
实现安全的Variant。记住,有Boost.Variant(1)。我把它放在这里只是为了证明。这是危险的,通常意味着你通过了其他有效的解决方案。但是,C程序员经常使用该技术。 C ++语言创建者Bjarne Stroustrup明确地认为这是合法且有价值的,我认为它只是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);
}
};
答案 1 :(得分:1)
您传递一个指向数据的任意指针并对其进行操作。请参阅void*
类型。
答案 2 :(得分:0)
以下是定义通用数据类型并在代码中使用它的完整演示。我不久前写过这个演示。现在它已更新,以支持您持有std :: string数据类型的要求。对浮动的支持以“双重”形式出现。数据类型。
您可以向其添加任何数据类型。分配数据类型时应小心。这意味着,你知道自己在做什么。
将代码下方的粘贴复制到控制台应用程序,看看它是如何工作的。
#include "windows.h"
#include "vector"
#include "string"
using namespace std;
enum GENERIC_DATA_TYPE
{
GENERIC_TYPE_NONE = 0,
GENERIC_TYPE_INT,
GENERIC_TYPE_SHORT,
GENERIC_TYPE_DOUBLE,
GENERIC_TYPE_CHAR,
GENERIC_TYPE_STD_STRING,
GENERIC_TYPE_STD_WSTRING,
};
#define SAFE_ARRAY_DELETE( ptrArray ) \
delete[] ptrArray;\
ptrArray = 0;
#define SAFE_DELETE( ptrPointer ) \
delete ptrPointer;\
ptrPointer = 0;
struct GENERIC_DATA
{
GENERIC_DATA_TYPE m_DataType;
UINT m_DataSize;
void* m_ptrData;
GENERIC_DATA() : m_DataType( GENERIC_TYPE_NONE ),
m_DataSize( 0 ),
m_ptrData( 0 )
{
}
~GENERIC_DATA()
{
CleanMemory();
}
bool CleanMemory()
{
try
{
switch( m_DataType )
{
case GENERIC_TYPE_INT:
{
int* ptrInt = reinterpret_cast<int*>( m_ptrData );
SAFE_ARRAY_DELETE( ptrInt );
break;
}
case GENERIC_TYPE_SHORT:
{
short* ptrShort = reinterpret_cast<short*>( m_ptrData );
SAFE_ARRAY_DELETE( ptrShort );
break;
}
case GENERIC_TYPE_DOUBLE:
{
double* ptrDouble = reinterpret_cast<double*>( m_ptrData );
SAFE_ARRAY_DELETE( ptrDouble );
break;
}
case GENERIC_TYPE_CHAR:
{
// Since string is kept as an array of string,
// we need to iterate each string
// and delete.
char** ptrString = reinterpret_cast<char**>( m_ptrData );
for( UINT uCounter = 0; m_DataSize > uCounter; ++uCounter )
{
SAFE_ARRAY_DELETE( ptrString[uCounter]);
}
// Now delete the double pointer.
SAFE_ARRAY_DELETE( ptrString );
break;
}
case GENERIC_TYPE_STD_STRING:
{
string* pstdString = reinterpret_cast<string*>( m_ptrData );
SAFE_DELETE ( pstdString );
break;
}
case GENERIC_TYPE_STD_WSTRING:
{
wstring* pstdString = reinterpret_cast<wstring*>( m_ptrData );
SAFE_DELETE ( pstdString );
break;
}
}
m_DataSize = 0;
m_DataType = GENERIC_TYPE_NONE;
return true;
}
catch( ... )
{
}
return false;
}
};
typedef vector<GENERIC_DATA*> GENERIC_DATA_VECTOR;
int main()
{
GENERIC_DATA_VECTOR vData;
// Allocate memory to hold the data
GENERIC_DATA* ptrData = new GENERIC_DATA();
// PUT SOME INTERGERS
// Of course the array size would be determined at runtime.
const int INT_COUNT = 10;
int* ptrIntArray = new int[INT_COUNT];
UINT nCounter = 0;
// Fill ptrIntArray with some integers
for( nCounter = 0; INT_COUNT > nCounter; ++nCounter )
{
ptrIntArray[nCounter] = rand();
}
// It is verly important to set the correct type here.
ptrData->m_DataType = GENERIC_TYPE_INT;
ptrData->m_DataSize = INT_COUNT;
ptrData->m_ptrData = ptrIntArray;
// Now put the data in the vector;
vData.push_back( ptrData );
// PUT A STRING
string* pstrString = new string();
*pstrString = "Helo World";
ptrData = new GENERIC_DATA();
// It is verly important to set the correct type here.
ptrData->m_DataType = GENERIC_TYPE_STD_STRING;
ptrData->m_DataSize = 1;
ptrData->m_ptrData = pstrString;
// Now put the data in the vector;
vData.push_back( ptrData );
// Now, at a later time we can manipulate each
// or the required type in the vector as below.
GENERIC_DATA_VECTOR::iterator DATA_VECTOR_END = vData.end();
GENERIC_DATA_VECTOR::iterator itrData = vData.begin();
GENERIC_DATA* ptrDataToProcess = 0;
for( ; itrData != DATA_VECTOR_END; ++itrData )
{
ptrDataToProcess = ( *itrData );
// Look for integer
if( GENERIC_TYPE_INT == ptrDataToProcess->m_DataType )
{
int* ptrIntArray = reinterpret_cast<int*>( ptrDataToProcess->m_ptrData );
// Process integers
for( nCounter = 0; ptrDataToProcess->m_DataSize > nCounter; ++nCounter )
{
printf( "\n %d", ptrIntArray[nCounter]);
}
continue; // Continue to next data.
}
// Look for string
if( GENERIC_TYPE_STD_STRING == ptrDataToProcess->m_DataType )
{
string* pstrString = reinterpret_cast<string*>( ptrDataToProcess->m_ptrData );
// Process the string
printf( "\n %s", pstrString->c_str());
}
continue; // Continue to next data.
}
// Once we finish with the data, iterate the vector and delete each entry.
DATA_VECTOR_END = vData.end();
itrData = vData.begin();
for( ; itrData != DATA_VECTOR_END; ++itrData )
{
delete ( *itrData );
}
return 0;
}
答案 3 :(得分:-1)
你可以重载那个函数..这里是简单的例子..
int AddI(int nX, int nY)
{
return nX + nY;
}
double AddD(double dX, double dY)
{
return dX + dY;
}