【问题标题】:Void Pointers in Red Black Tree红黑树中的空指针
【发布时间】:2015-12-21 14:46:47
【问题描述】:

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

我不明白插入函数的参数,更具体地说是使用

(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);
}

【问题讨论】:

  • 我认为这不是您的代码。你不应该在 C++ 中使用void*,因为我们有模板。你看到的叫explicit casting
  • 你有什么不明白的?它是一个存储void* 的通用树,它是常用的预模板,并且仍然在C 中。(如果该代码是通过将其包装在一层薄薄的C++ 类中而从C“移植”出来的,我不会感到惊讶。 )

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


【解决方案1】:

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

(void*)xexplicit type conversion 也称为 C 样式类型转换表达式。变量x 的类型转换为void*。在 C++ 中不鼓励使用 C 风格的强制转换。

推测x 的类型不是void*,因此需要进行转换以匹配参数的类型。

【讨论】:

    【解决方案2】:

    这段代码的作者使用了 C++ 中最抽象的指针类型:void*。它是一个指针,所以“某事”。这个“东西”可能是什么并没有在编译时定义。 (void*)x 是传统 C 语法中的类型转换,它将任何其他指针解释为 void*。首选的 C++ 语法是 static_cast&lt;void*&gt;(x),尽管 void* 只有在有非常非常好的理由时才应该使用。

    我了解,这是遗留代码,您已被要求处理。所以坦率地说,它有很多问题。

    1. 实现红黑树类有两个原因:学习数据结构和将std::map&lt;&gt; 作为标准库实现的一部分。在所有其他情况下,没有理由不喜欢std::map&lt;&gt;。它可以让你避免所有的设计陷阱,这段代码的作者已经介入了。

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

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

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

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

    综合起来,我会提出这样的界面:

    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 键一样。它们也总是使用相同的比较函数,默认为运算符&lt;

    用法也更容易理解:

    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&lt;&gt;。它的用法更加直观:

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

    【讨论】:

      猜你喜欢
      • 2016-06-26
      • 2010-09-06
      • 2013-06-05
      • 2018-02-07
      • 1970-01-01
      • 2015-11-17
      • 2012-11-30
      • 2014-07-05
      • 2013-02-28
      相关资源
      最近更新 更多