为什么 QList::at() 不检查索引是否存在并返回只读值?

rba*_*dar 5 c++ indexing stl constants qlist

这个问题更多的是一种询问,而不是实际寻求问题的解决方案。

QList::at()不仅不检查索引是否越界,而且还返回 aconst因此它只能在以下场景中使用read-only

const T &QList::at(int i) const

返回列表中索引位置 i 处的项目。i 必须是列表中的有效索引位置(即,0 <= i < size())。

这个函数非常快(恒定时间)。

我刚刚在尝试为列表中的元素分配新值时发现了这些特殊的实现细节QList,我想知道是否有这样做的理由。

不检查索引有效性

如果C++ STL我们采取std::arraystd::vector(我不采取std::list,因为它根本没有std::list::at()),我们有以下内容:

std::向量::at

该函数自动检查 n 是否在向量中有效元素的范围内,如果不在则抛出 out_of_range 异常

起初我以为不包括检查是为了确保“这个功能非常快(恒定时间)”。然而,在查看了实现之后,QList我不得不说,即使包含恒定时间(并且高速)的检查,也绝对可以保证。

检查越界需要两件事(据我所知):

  • 检查给定索引是否< 0 - 这是一个恒定时间检查(O(1)如果我们使用一些古怪的符号)。显然我们不能有负索引(我们可以,Python但它有另一个含义;))
  • 检查给定索引是否< QList::size()- 常数时间也存在于此,因为QList::size()实现方式是:

    inline int size() const Q_DECL_NOTHROW { return d->end - d->begin; }
    
    Run Code Online (Sandbox Code Playgroud)

    这又是O(1)(如果我没记错的话),因为这两个值都存储在QList::d参数内,该参数是指向

    struct Data {
      QtPrivate::RefCount ref;
      int alloc, begin, end;
      void *array[1];
    };
    
    Run Code Online (Sandbox Code Playgroud)

    因此,访问这些并计算它们的差异不会对性能造成太大影响(显然它会产生一些较小的影响,因为它引入了一些访问和算术操作,加上由于函数内的跳转而备份和恢复堆栈QList::size())。

那么为什么要放弃对索引有效性的检查呢?性能影响真的那么大吗?

返回只读值

通过这样做,该QList::at()函数提供了一个隐藏的且(哦)不是很有用的功能,这使得使用它变得不同,QList::[]甚至更加令人困惑。也C++ STL相当于std::vectorstd::array允许使用此函数分配新值。

Hol*_*olt 4

有“3”种方法可以从 a 中的索引访问项目QList

const T& QList::at(int) const
T& QList::operator[](int)
const T& QList::operator[](int) const
Run Code Online (Sandbox Code Playgroud)

如果您查看文档,您会发现最后一个很简单:

与 at() 相同。该函数以恒定时间运行。

所以我们只剩下第一个 ( at) 和非常量operator[]。这是问题,第二个的文档告诉你1

如果在当前正在共享的列表上调用此函数,它将触发所有元素的副本。否则,该函数将在恒定时间内运行。如果您不想修改列表,则应该使用QList::at().

注意:这也适用于QVector

请注意,使用非常量运算符可能会导致QVector进行深层复制。

1对于QList,这实际上仅适用于Qt 5,不适用于Qt 4(至少在文档中),但对于Qt 4QVector来说已经如此,因此该方法可能在所有 Qt 容器之间保持一致。.at

这意味着您不能operator[]直接在非常量共享QList或非常量上使用QVector,您需要执行以下操作:

QList<T> &mySharedQList = ...;
const QList<T> &myConstRef = mySharedQList;
myConstRef[0]; // Only ways not to copy the original QList
Run Code Online (Sandbox Code Playgroud)

我的猜测是, 的目的.at是为您提供一种保证恒定访问时间的方法,无论QListQVector是什么(这operator[]并不能保证)。对于标准库容器,您不需要这种方法,因为非常量operator[]已经保证在常量时间内运行(它们永远不会复制)。

关于对索引的检查,这可能是为了保持 的行为operator[],因为与 不同的是,您可能想在只需要读取数据的任何地方std::vector使用。.at