【问题标题】:Difficulty implementing the -> structure dereference operator难以实现 -> 结构取消引用运算符
【发布时间】:2013-09-16 21:48:13
【问题描述】:

一些(相关的?)背景: 我有一个类在 c++ 中提供类似迭代器的功能。它用于解压缩大型数据集。在幕后,我有一个CompressedData 对象。迭代器类对压缩数据进行迭代,返回未压缩的Data对象

Data ExpandingIterator::operator*() const;

取消引用操作符返回一个 Data 而不是 Data& 因为没有任何地方可以存储未压缩的 Data 对象来持久化。例如,压缩数据可能包含以下三个条目:

  • 5'3',
  • 3 个 1,
  • 6 个“0”

然后当你用 ExpandingIterator 迭代这个集合时,你会得到:

3, 3, 3, 3, 3, 1, 1, 1, 0, 0, 0, 0, 0, 0

3 和 1 和 0 永远不会同时存在于数组的某个位置。它们是从压缩数据中一次一个地扩展的。现在,真实数据比简单的整数更复杂。有数据、时间戳和其他方法可以将数据转换为其他形式的数据。

问题: 我已经成功实现了上面提到的解引用操作符。问题是当我使用 ExpandingIterator 并想要访问 Data 对象上的方法之一时,我最终会写

(*iterator).Method()

我更喜欢写

iterator->Method();

到目前为止,这个问题的解决方案似乎很明显。我需要实现 -> 运算符。这最初看起来很简单,但我担心我遗漏了一些东西(可能很明显)

一开始,我试过了:

Data ExpandingIterator::operator->() const{
    return **this; //Just call the regular dereference operator
}

但这给我留下了错误消息:类型'Data'没有重载成员'operator ->'。更多研究,基于this answer here,我相信有一些可能的解决方案:

  1. 也给“数据”一个 -> 运算符。不幸的是,我认为这只会将我对问题的误解推到 Data 对象中,在那里我会遇到与我在这里遇到的完全相同的问题
  2. 返回指向“数据”对象的指针。不幸的是,Data 对象并不真正存在于可以指向的位置,所以这似乎是一个非首发
  3. 给 Data 一个隐式对象以进行指针转换 - 这对我来说似乎很荒谬。
  4. 放弃,回家吧。只需执行 (*Data).Method() 并被 c++ 击败。

有人可以帮助消除对 -> 运算符应该如何实现的误解(或缺乏理解)吗?

【问题讨论】:

  • operator -> 应该返回一个指向您希望应用-> 的实例的指针,或者一个实现-> 重载的(引用)对象。
  • 难道不能在迭代器类中存储一个 Data 实例并返回一个指向该实例的指针吗?无论如何,这似乎是一个好主意,因为如果迭代器被多次访问,该实例基本上将用作缓存。如果您的减压步骤很昂贵,那可能很重要。
  • @dsharlet 这不是一个糟糕的主意。解压步骤并不昂贵,它只依赖于跟踪您已经迭代了多少解压项目。话虽如此,将当前数据项保留在某个地方并没有什么坏处
  • @jxh 棘手的部分是我不确定要返回指针的实例应该驻留在哪里。

标签: c++ iterator operator-overloading


【解决方案1】:

要重载operator->(),您最终需要得到一个实际指针,例如,T const* 用于合适的类型T。当您从operator->() 返回不同于指针的内容时,编译器将对返回的对象调用operator->()

从它的声音来看,在您的情况下,从ExpandingIterator::operator->() 返回一个Data 对象可能是合理的,该对象在给定位置填充相应的数据。当然,Data 需要另一个 operator->(),它可以返回,例如,指向自身的指针,即 this,假设 Data 具有您要调用的实际成员函数。

原则上,在你最终到达一个你真正想要返回指针的对象之前,你可以有多个间接。但是,似乎一旦您返回了 Data 对象,您手中就有了一个想要调用成员函数的实体,因此返回指向它的指针应该可以工作。请注意,临时对象的规则仍然适用,即,如果您返回 Data 对象,它将在完整表达式的末尾消失。

【讨论】:

  • 听起来像是投票给#1。数据对象在表达式末尾消失可能是可以的——它是不可变的。大多数方法要么根据数据项计算一些值,要么将其转换为其他类型的对象
【解决方案2】:

您可以在迭代器中定义一个帮助类,该类可以将-> 操作委托给Data 指针。

class ExpandingIterator {
    //...
    struct DataPtr {
        Data data;
        DataPtr () {}
        DataPtr (const Data &d) : data(d) {}
        Data * operator -> () { return &data; }
    }
    //...
    DataPtr operator -> () const { return **this; }
    //..
};

【讨论】:

  • 这是否购买了将 -> 运算符添加到数据类本身没有的任何东西?
  • 它限制了对迭代器的更改范围,您不必在Data 类中添加愚蠢。
  • 如果您想知道Data 实例应该驻留在哪里,它是嵌入在DataPtr 临时中的成员,如我所示。
  • 这是明智的 - 使用普通编译器,它会给出相同的代码来将方法添加到 Data 类。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-01-22
  • 1970-01-01
  • 2016-09-30
  • 2014-05-20
  • 2017-10-24
相关资源
最近更新 更多