在C中“模拟”多态时,使用结构成员和强制转换结构指针之间的区别

时间:2014-02-19 08:21:57

标签: c pointers

我不确定我的措辞在技术上是否正确,所以请在标题和主题中纠正我。

所以基本上我的问题是关于在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));

为简洁起见,我将两种方式都放在上面的一个函数调用中。

这正是我困惑的地方。我应该使用哪一个,更重要的是,为什么?

2 个答案:

答案 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和类型指针。为对象分配的实际内存   包含其他只能在转换后才能访问的数据   指向更长结构类型的指针。

请注意,此攻击角度可能更适合解释代码。您可能会看到其他与您的需求更直接相关的开源项目。