【问题标题】:Need help understanding operator overloading in C++需要帮助理解 C++ 中的运算符重载
【发布时间】:2010-09-02 11:21:11
【问题描述】:

我正在尝试实现一个结构,该结构基本上是一个自定义哈希表,其中包含任何内容 (NULL) 或指向二叉搜索树对象的指针。

无论如何,我无法弄清楚如何做一些事情,例如将哈希表(一个数组)设置为 NULL,以及将 BST 对象从一个表存储到另一个表。

BST *prevData;
BST *currData;

据我了解,我认为我必须重载 = 和 == 运算符以允许将数组元素设置为 NULL,这是否正确?我真的不清楚如何实现这一切,但从谷歌的例子来看,我有:

BST& BST :: operator= (int null);
bool BST :: operator== (int null);

== 运算符重载是检查特定数组索引是否等于 NULL。我假设我需要一些额外的代码,而不仅仅是原型,这就是我遇到困难的地方。

第二点是memcpy,编译器不允许我这样做。

memcpy(prevData[x], currData[x], sizeof(BST) * height);

抱怨

错误 C2664:“memcpy”:无法转换 参数 1 从 'BST' 到 'void *' 否 用户定义的转换运算符 可以执行此操作的可用 转换,否则运算符不能 叫

如果您需要更多信息,我很乐意为您填写。

有什么线索吗?谢谢。

【问题讨论】:

  • memcpy() 在 C++ 代码中调用...有异味...
  • 你可以向我们展示你所有的函数代码BST& BST :: operator= (int null);
  • 我没有代码,正如我上面提到的,我不确定我应该在这里重载什么,你有什么好的例子来说明应该如何做到这一点(具体来说,设置一个自定义-object 数组为 NULL)。
  • 另外,请告诉我在 C++ 中复制对象的最佳方式。我为这个项目提供的示例代码最初使用 memcpy() 作为布尔数组,所以我认为这很好。
  • 您不能重载运算符来检查 C++ 中的特定值 (null)。要么我误解了你,要么你误解了运算符重载。你能告诉我们你想如何使用你试图重载的操作符吗?

标签: c++ arrays operator-overloading


【解决方案1】:

据我了解,我认为我必须 将 = 和 == 运算符重载为 允许将数组元素设置为 NULL, 这是正确的吗?

不,一点也不。您已经描述了 BST * 指针数组,而不是 BST 对象数组。 NULL 已经是一个有效的指针值,您不需要更改任何 C++ 行为:

BST *array[LENGTH]; // LENGTH > 1
array[0] == NULL;
array[0] == new BST; // but watch for memory leaks!
array[1] == array[0];
array[0] == NULL; // now moved the array[0] pointer to array[1]

只要您有一个指针数组,内存管理就会变得棘手,因此考虑智能指针(例如shared_ptr)变得很重要。

编辑:正如 cmets 中所讨论的,更好的解决方案是使用某种空 BST 值:

class BST {
    bool isNull_;
    BST() : isNull_(true) {}
    bool isNull() { return isNull_; }
    // rest of class definition...
};

现在,您有一个“空”BST 值,bst.isNull_ 为真。默认值为空值。所以如果你创建一个空向量:

std::vector<BST> vec = std::vector<BST>(10);

这将初始化为 10 个空 BST。 new[] 数组分配运算符也是如此。

第二点是memcpy,我就是 不被允许做 编译器。

memcpy(prevData[x], currData[x], sizeof(BST) * height);

不要那样做!错误的原因有很多,但主要是因为它是遗留的 C 函数,不适合 C++ 类;并且因为您复制的是一个项目,而不是其中的一个块。

要复制一项,请使用operator=

currData[x] = prevData[x];

复制块数据的C++函数是std::copy。使用它的一个原因是它可以识别重载的operator= 赋值运算符,而memcpy 不能。示例:

std::copy(prevData, prevData + LENGTH, currData);

(我假设你想复制 from prevData to currData 就像我在这里所做的那样,而不是像你为 @ 所做的那样987654338@?)

【讨论】:

  • 对于第一部分,我假设您的意思是 array[0] = NULL;因为这是我想做的。我不能让它按你说的那样工作。我正在使用 BST * 对象,但将其设置为 NULL 会给我错误“错误 C2679:二进制 '=':未找到采用 'int' 类型的右手操作数的运算符(或没有可接受的转换) " 这让我觉得我必须首先使用运算符重载。
  • @shrodes:这很奇怪。如果 NULL 是适当的宏(应该只是 0),您可以将 NULL 分配给任何指针。也许您将NULL 设置为一些有趣的值?如果你这样做 array[0] = 0; 会发生什么?
  • 我想我明白了,我需要更改为:BST **prevData;并将数组初始化为: data = new BST*[some_size];?
  • @shrodes:我对此有点紧张。如果使用动态分配对象的动态分配数组,则需要非常小心内存管理。你真的应该把它全部放在智能指针中,但如果你不能使用 STL 或 Boost,那不是一个选择。也许您应该为您的对象创建一个“NULL 值”,以允许您的对象表示一个空状态?实现set_null()is_null() 方法。
  • @shrodes: 如果你将空 BST 对象设为默认构造函数 BST() 的结果,你甚至不需要将数组初始化为 BST_NULL。
【解决方案2】:

如果不是作业(那是你的标签?)我会推荐一个 unordered_map 或 map。你的概念还没有融合在一起 - 最好简化它并一次解决一件事,而不是咬得比你能咀嚼的更多。如果你想要一个树的哈希图,我建议你从你可以依赖的 C++ STL 容器开始构建它,然后 - 如果你不应该将它们用于此任务 - 一个一个地替换它们,确保你保持高层次的使用/测试在你做的时候工作。这意味着:

typedef vector<map<T> > Container;
Container container;

您可以使用 container.resize(n) 用 n 个空地图填充容器,或者将容器从以前的某个大小扩大到一个新的大小,而额外的地图为空。

真的,从那开始。然后,如果您必须作为练习,独立实现每个向量和映射,并在将它们放回混合哈希映射之前使用简单的独立测试用例测试它们的用法。

听起来我有点苛刻,所以让我们通过你的一些问题来看看我所说的一些误解:

无论如何,我很难弄清楚 搞清楚如何做一些事情,比如 设置哈希表,这是一个 数组为 NULL,并且还 memcpy'ing BST 对象从一张桌子转移到另一张桌子。

我的建议是保持简单,做明显和干净的事情。如果你必须使用数组而不是向量,那么如果它在堆栈上:

X* buckets[N];
for (int i = 0; i < sizeof buckets / sizeof buckets[0]; ++i)
    array[i] = NULL;

如果它在堆上:

X** p_buckets = new X*[N];
for (int i = 0; i < N; ++i)
    array[i] = NULL;

这比 memset() 慢,但它简单易懂,让你养成考虑循环容器的习惯,做你想做的操作。有更多的 C++ 方法可以做到这一点,但是对于其他任意任务使用相同样式的迭代会更难,所以坚持下去,直到你完全适应它为止。

BST *prevData; BST *currData;

据我了解,我认为我必须重载 = 和 == 运算符才能允许设置 数组元素为NULL,这是正确的吗?我真的不清楚如何实施所有 这个,但是从谷歌的例子来看,我有:

BST& BST :: operator= (int null); bool BST :: operator== (int null); == 运算符重载是检查特定数组索引是否等于 NULL。 我假设我需要一些额外的代码,而不仅仅是原型,这是 我来不及了。

您有两个指向 BST 的指针。您的哈希表是此类对象的数组,但只有您的一条评论建议您将这些视为指向两个此类哈希表的指针(而不是说在通过哈希表迭代期间记录非 NULL 元素的临时变量)。它们应该是 BST**,因为它们指向哈希表中的第一个 BST*。但是,这是实现哈希表的一种非常危险的方式,因为客户端使用的是指向哈希表的指针,而不是指向某个提供哈希表概念的安全实现的类的指针。有了这样一个指针,他们所能做的就是索引到特定的 BST,但是与您打算维护哈希表的函数没有关联,也没有跟踪它当前有多少存储桶空间的变量,它有多少元素包含等。您应该将数组放入提供所有这些的类中;有很多设计决策,但它应该看起来像这样:

template <typename Key, typename Value>
class Hash
{
  public:
    Hash() : p_(NULL), size_(0), num_buckets_(0) { }

    // find/make entry for Key, giving read/write access to the current/a-default value
    Value& operator[](const Key&);        
    const Value& operator[](const Key&) const;

    // try to find an entry, throw an exception if not found...
    Value& at(const Key&);
    const Value& at(const Key&) const;

    // report whether a key is already in the hash table...
    bool has_key(const Key&) const;

    int size() const;

  private:
    BST* p_;
    int num_buckets_;
    int size_;
};

(再一次,我只能建议您首先尝试将 p_ 替换为该地图矢量)。

第二点是memcpy,编译器不允许我这样做。

memcpy(prevData[x], currData[x], sizeof(BST) * height);

抱怨

错误 C2664:“memcpy”:无法将参数 1 从“BST”转换为“void *” 没有可以执行此转换的用户定义转换运算符,或 无法调用操作员

将所有内容从一个哈希表中的一个存储桶复制到另一个哈希表中的同一个存储桶中,这是一个非常奇怪的请求。例如,如果存储桶的数量不同或散列函数不同,那么您可能会损坏目标存储桶。无论如何,我建议你实现一个具有值语义的 BS​​T 类(即复制现有树的复制构造函数和 operator=)。然后你可以简单地使用:

prevData.buckets[x] = currData.buckets[x];

(注意:运算符应该在哈希表中查找,并且鉴于键类型应该能够是 int,你不想使用相同的符号来复制整个存储桶 - 你不能重载一个函数如果两者都具有相同的签名。这就是为什么我在这里展示桶是 BST 的暴露容器 - 暴露这会破坏封装;在真实的类中,您只是不希望用户甚至知道桶(或 BST))。

【讨论】:

  • 是的,我希望我可以使用类似的东西,但整个任务是使用自定义哈希表,选择哈希函数,并在表本身中使用二叉搜索树。我们实际上不允许在大多数事情上使用 STL。
  • @shrodes:“我们实际上不允许在大多数事情上使用 STL。”啊!坏的!逃跑! D:STL 是用 C++ 学习的最重要的东西,因为它是界面设计的一个很好的例子。
  • 我可以接受您不将 STL 用作垫脚石,但必须坚持认为这是您将此问题分解为可管理的块的最实用方法。一旦你构建了一些东西来控制地图向量,然后先用你自己的实现替换向量,然后再替换地图。
猜你喜欢
  • 2020-10-19
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-09-27
  • 1970-01-01
  • 1970-01-01
  • 2013-07-10
  • 1970-01-01
相关资源
最近更新 更多