我不确定我的措辞在技术上是否正确,所以请在标题和主题中纠正我。
所以基本上我的问题是关于在C中模拟多态性。例如,假设我有一个树,并且有一个struct tree_node类型。我有一些函数可以帮助我插入节点,删除节点等,例如:
void tree_insert(tree_node **root, tree_node *new_node);
然后我开始为我的应用程序构建其他东西,并且需要使用这个树来维护家庭成员。但是对于人类来说,我有另一个结构,让我们称之为#34; struct human_node"这是这样定义的,例如:
typedef struct human_node_ {
tree_node t_node;
char *name;
} human_node;
现在显然我想使用我为通用树构建的树实用程序函数。但他们采用tree_node指针。现在是多态仿真的时候了。所以这里有两个选项,一个是我的human_node,一个是在human_node中使用t_node成员:
human_node *myfamily_tree_root, *new_family_guy;
//some initialization code and other code later...
tree_insert((tree_node **)&myfamily_tree_root, &(new_family_guy->t_node));
为简洁起见,我将两种方式都放在上面的一个函数调用中。
这正是我困惑的地方。我应该使用哪一个,更重要的是,为什么?
答案 0 :(得分:4)
两者都是标准的,但一般来说如果你可以避免使用类型转换,那么你应该选择避免类型转换的解决方案。
这种内联数据结构实现的一个常见之处是甚至不要求树节点(或等效的)是结构中的第一个元素,因为您可能希望将节点输入到多个树中。那你肯定想要使用第二种方法。要在tree_node元素和包含你的结构之间进行转换,你必须拥有一些黑魔法宏,但这是值得的。例如,在avl树的实现中,我有这些宏:
#ifndef offsetof
#define offsetof(s, e) ((size_t)&((s *)0)->e)
#endif
/* the bit at the end is to prevent mistakes where n is not an avl_node */
#define avl_data(n, type, field) ((type *)(void*)((char *)n - offsetof(type, field) - (n - (struct avl_node *)n)))
所以我可以有类似的东西:
struct foo {
int data;
struct avl_node tree_node_1;
struct avl_node tree_node_2;
};
int
tree_node_1_to_data(struct avl_node *x)
{
return avl_data(x, struct foo, tree_node_1)->data;
}
如果你选择使你的代码变得通用,你肯定想要引用你的tree_node成员,而不是指向结构的指针。
答案 1 :(得分:1)
对于任何具体答案,此问题可能过于宽泛,但您可以查看how CPython does it。
基本上,所有Python结构都具有相同的标头,并且要定义自己的类型,您必须确保使用PyObject_HEAD macro(或PyObject_VAR_HEAD
为可变大小的对象(如字符串)启动结构。这会添加类型标记,引用计数等内容。
在实例化对象之后,您将它们作为PyObject*
传递,并且函数将推断对象实际上是什么类型(例如字符串,列表等)并且能够基于此进行分派。是的,您必须在某个时刻键入强制转换才能获得实际的对象内容。
例如,这是how Python's character strings are defined:
typedef struct {
PyObject_VAR_HEAD
Py_hash_t ob_shash;
char ob_sval[1];
/* Invariants:
* ob_sval contains space for 'ob_size+1' elements.
* ob_sval[ob_size] == 0.
* ob_shash is the hash of the string or -1 if not computed yet.
*/
} PyBytesObject;
您可以阅读有关CPython's type object inheritance model的更多信息。提取物:
始终通过“PyObject *”类型的指针访问对象。 类型'PyObject'是仅包含引用的结构 count和类型指针。为对象分配的实际内存 包含其他只能在转换后才能访问的数据 指向更长结构类型的指针。
请注意,此攻击角度可能更适合解释代码。您可能会看到其他与您的需求更直接相关的开源项目。