我在 C 中创建了一个简单的列表结构,它使用 void* 指针保存任何类型的数据,例如:
struct node
{
void *the_data;
node *next;
};
Run Code Online (Sandbox Code Playgroud)
它工作得很好,但我有一些列表,例如,一个列表中只包含 struct_a,另一个列表中只包含 struct_b。我需要在单个 if() 问题中检查列表的第一个元素是 struct_a 类型还是 struct_b 类型,以便我知道该列表保存的数据。这该怎么做?谢谢
编辑:
我想出了一个简单的解决方案,现在已经足够解决这个问题了,不知道它是否是最好的:
struct node
{
void *data;
node *next;
}
Run Code Online (Sandbox Code Playgroud)
我添加了一个“描述符”节点,其中包含第一个节点的地址和有关类型的信息:
struct list_desc
{
node *list;
short int type;
}
Run Code Online (Sandbox Code Playgroud)
我会将它与一些宏一起使用,例如
#define __data_type1 10
#define __data_type2 20
Run Code Online (Sandbox Code Playgroud)
所以稍后我可以比较 if( struct.type == __data_tipe1 ) 等...
C 不提供运行时类型信息。您必须自己添加此信息。有两种基本方法(假设为 C99 或 C11):
您可以将结构(可能)与匿名联合一起使用:
struct Node {
enum {
OBJ_TYPE_A,
OBJ_TYPE_B
} type;
union {
struct NodeA a;
struct NodeB b;
}; // anonymous, if a name is added, you have an additional field name
};
...
struct Node *node = ...;
if ( node->type == OBJ_TYPE_A ) {
node->a.a_field = whatever;
...
Run Code Online (Sandbox Code Playgroud)
缺点是你必须在一个地方定义联合并且不能在另一个模块中扩展它。
更好的方法是使用匿名结构构建真正的类层次结构:
struct Node {
enum {
OBJ_TYPE_A,
OBJ_TYPE_B
} type;
struct Node *next;
};
struct NodeA {
struct Node; // base class, first field!
int a_field;
};
struct NodeB {
struct Node; // base class, first field!
float b_field;
};
void take_node(struct Node *node)
// takes a generic pointer to the base-class
{
if ( node->type == OBJ_TYPE_A ) {
struct NodeA *a_node = (NodeA)node;
a_node->a_field = whatever;
} else if ...
...
}
// until here that is normal C99
int main(void)
{
struct NodeA anode = { .type = OBJ_TYPE_A, .a_field = 42 };
struct NodeB bnode = { .type = OBJ_TYPE_B, .b_field = 4.2 };
take_node((Node *)&anode); // standard: inhibits type checking
take_node(&anode); // plan9-extension: no cast required
take_node(&bnode); // plan9-extension: no cast required
}
Run Code Online (Sandbox Code Playgroud)
优点是在创建新的子结构时不需要扩展基本结构(它不依赖于子结构)。匿名结构允许创建直接子代的子代和访问字段,正如在 OOP 中所期望的那样(没有字段说明符链:child1_1_1_ptr->child1_1.child1.base.base_field,而只是child1_1_1_ptr->base_field)。
后两个调用take_node使用 gcc C-extension -fplan9-extensions。这避免在仍然启用类型检查(基类或子类)的同时转换为基类。这已经是向 OOP 迈出的一大步。
这可以扩展到全尺寸的 OOP 方法,包括虚拟方法。然而,有一点像 C++ 那样使用真正的 OOPL 会变得更容易。
您对此有何看法:
typedef enum tag_ElementType
{
ElementA,
ElementB
} en_ElementType;
typedef struct tag_Container
{
en_ElementType ElementType;
void *pData;
} st_Container;
Run Code Online (Sandbox Code Playgroud)
在这种情况下,您的结构将如下所示:
typedef struct tag_node
{
st_Container the_data;
struct tag_node *next;
} st_node;
Run Code Online (Sandbox Code Playgroud)