红黑树中的空指针

时间:2015-12-21 14:46:47

标签: c++ tree void-pointers red-black-tree

有两个整数x和7是随机生成的整数。该程序使用红黑树成员fucntion插入将新值插入树中。

我不理解插入函数的参数,更具体地说是

的使用
(void*)x and (void*y) 

这是main

中的函数调用
rbt.rbtree_insert(t, (void*)x, (void*)y, compare_int);

这是定义的插入函数

void RBTree::rbtree_insert(rbtree t, void* key, void* value, compare_func compare)
{
    node inserted_node = new_node(key, value, RED, NULL, NULL);
    if (t->root == NULL)
    {
        t->root = inserted_node;
    }
    else
    {
        node n = t->root;
        while (1)
        {
            int comp_result = compare(key, n->key);
            if (comp_result == 0)
            {
                n->value = value;
                return;
            }
            else if (comp_result < 0)
            {
                if (n->left == NULL)
                {
                    n->left = inserted_node;
                    break;
                }
                else
                {
                    n = n->left;
                }
            }
            else
            {
                assert(comp_result > 0);
                if (n->right == NULL)
                {
                    n->right = inserted_node;
                    break;
                }
                else
                {
                    n = n->right;
                }
            }
        }
        inserted_node->parent = n;
    }
    insert_case1(t, inserted_node);
    verify_properties(t);
}

2 个答案:

答案 0 :(得分:0)

void*是一种类型。更具体地说,它是指针类型。更具体地说,它是special pointer类型,可以指向任何类型。 void*是一种在C中实现polymorphism的方法。通常不建议在C ++中使用void*

(void*)x是一个explicit type conversion,也称为C风格的类型转换表达式。变量x的类型将转换为void*。在C ++中不鼓励使用C风格的演员表。

大概x的类型不是void*,因此需要进行转换才能匹配参数的类型。

答案 1 :(得分:0)

此代码的作者使用C ++中提供的最抽象的指针类型:void*。这是一个指针,所以“东西”。这个“东西”可能在编译时没有定义。 (void*)x是遗留C语法中的类型转换,它将任何其他指针解释为void*。首选的C ++语法是static_cast<void*>(x),但只有void*才应该使用,当有非常好的理由时这样做。

我理解,这是遗留代码,您被要求继续工作。所以坦率地说,它存在很多错误。

  1. 实现红黑树类有两个原因:学习数据结构和std::map<>作为标准库实现的一部分。在所有其他情况下,没有理由不喜欢std::map<>。它可以帮助你摆脱所有的设计陷阱,这段代码的作者介入了。

  2. 将类的名称添加到成员函数的名称是多余的。称之为RBTree::insert()而不是RBTree::rbtree_insert()。当您为不同的容器类型使用一致的成员函数名称时,您可以在将来轻松地交换它们,而无需更改对所有成员函数的所有调用。标准容器是这里的一个很好的灵感来源。

  3. 红黑树实例始终必须使用相同的比较功能。要一次又一次地将比较功能传递给insert()find()erase()等不仅多余而且容易出错。将它作为构造函数的参数,或者更好地作为红黑树类模板的模板参数。

  4. 在任何情况下,红黑树都应该是一个模板,它有一个键和一个值类型作为模板参数。然后,insert()find()等所有成员函数都可以是类型安全的。

  5. 为什么要将此对象显式传递给成员函数。我想,该代码的作者一直在尝试用C ++编写Python。在C ++中,this对于成员函数始终是隐式的,与Python中的self不同。

  6. 总而言之,我建议使用这样的界面:

    template<typename key_t,
             typename value_t,
             typename Compare = std::less<key_t>>
    class rb_tree {
        void insert(const key_t& key, const value_t& value);
        void erase(const key_t& key);
        value_t find(const key_t& key);
    };
    

    如您所见,我们现在定义键和值的类型,并在insert()erase()find()中使用它们。这些函数永远不会尝试使用int键遍历树,就好像它有std::string个键一样。它们也总是使用相同的比较函数,默认为运算符<

    使用起来要好得多:

    rb_tree<int, int> rbt; // we use the default comparison
    int x = 42;
    int y = 4711;
    rbt.insert(x, y);
    assert(rbt.find(x) == y);
    rbt.erase(x);
    

    嗯,实际上我真正的建议是放弃本土的红黑树,而是使用std::map<>。它的用法更直观:

    std::map<int, int> rbt;
    int x = 42;
    int y = 4711;
    rbt[x] = y;
    assert(rbt[x] == y);
    rbt.erase(x);