为什么 QList::at() 不检查索引是否存在并返回只读值? [英] Why doesn't QList::at() check if index exists and also returns a read-only value?

查看:54
本文介绍了为什么 QList::at() 不检查索引是否存在并返回只读值?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

QList::at() 不仅不检查索引是否越界,而且还返回一个 const 因此它只能用于只读 场景:

<块引用>

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

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

此功能非常快(恒定时间).

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

不检查索引有效性

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

<块引用>

std::vector::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;}

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

    struct 数据 {QtPrivate::RefCount ref;int 分配,开始,结束;无效 *数组[1];};

    因此访问它们并计算它们的差异并不会对性能造成太大影响(显然它有一些轻微影响,因为它引入了一些访问和算术运算以及由于 内部的跳转而备份和恢复堆栈QList::size() 函数).

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

返回只读值

通过这样做,QList::at() 函数提供了一个隐藏的和 (omho) 不是很有用的功能,这使得使用它和 QList::[] 更令人困惑.此外,与 std::vectorstd::array 等效的 C++ STL 允许使用此函数分配新值.

解决方案

QList 中,有3"种方法可以从其索引访问项目:

const T&QList::at(int) const夯;QList::operator[](int)const T&QList::operator[](int) const

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

<块引用>

与 at() 相同.此函数以恒定时间运行.

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

<块引用>

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

注意:对于 QVector 也是如此:

<块引用>

请注意,使用非常量运算符会导致 QVector 进行深度复制.

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

这意味着您不能直接在非常量共享 QList 或非常量 QVector 上使用 operator[],您需要做类似的事情:

QList&mySharedQList = ...;const QList&myConstRef = mySharedQList;myConstRef[0];//唯一不复制原始QList的方法

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

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

This question is more of an inquiry then actually seeking a solution for a problem.

QList::at() not only doesn't check if an index is out of bounds but it also returns a const hence it can be used only in a read-only scenario:

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

Returns the item at index position i in the list. i must be a valid index position in the list (i.e., 0 <= i < size()).

This function is very fast (constant time).

I've just found out these peculiar implementation details of QList while trying to assign a new value to an element in my list and I'm wondering if there is a reason for doing that.

No check for index validity

In C++ STL if we take std::array or std::vector (I'm not taking std::list since it doesn't have std::list::at() at all) we have the following:

std::vector::at

The function automatically checks whether n is within the bounds of valid elements in the vector, throwing an out_of_range exception if it is not

At first I thought that the check is not included to ensure the "This function is very fast (constant time)." however after looking at the implementation of QList I have to say even if the check was included a constant time (and high speed) is most certainly ensured.

A check for out of bounds would require two things (as far as I know):

  • Check if given index is < 0 - this is a constant time check (O(1) if we use some wacky notation). Obviously we can't have negative index (we can in Python but it has another meaning ;))
  • Check if given index is < QList::size() - constant time is also present here since the way QList::size() is implemented is:

    inline int size() const Q_DECL_NOTHROW { return d->end - d->begin; }
    

    This is again O(1) (if I'm not mistaken) since both values are stored inside the QList::d parameter which is a pointer to

    struct Data {
      QtPrivate::RefCount ref;
      int alloc, begin, end;
      void *array[1];
    };
    

    so accessing those and calculating their difference is not hurting the performance that much (obviously it has some minor impact since it introduces a couple of access and arithmetic operations plus backing up and restoring the stack due to the jump inside the QList::size() function).

So why dump the check for index validity? Is the performance impact actually that big?

Returning a read-only value

By doing so the QList::at() function offers a hidden and (omho) not very useful feature which makes the difference in using it and QList::[] even more confusing. Also the C++ STL equivalent for std::vector and std::array allows assignment of new values using this function.

解决方案

There are "3" ways of accessing an item from its index in a QList:

const T& QList::at(int) const
T& QList::operator[](int)
const T& QList::operator[](int) const

If you look at the doc, you will see that the last one is simply:

Same as at(). This function runs in constant time.

So we are left with the first one (at) and the non-const operator[]. Here is the problem, the documentation of the second one tells you that1:

If this function is called on a list that is currently being shared, it will trigger a copy of all elements. Otherwise, this function runs in constant time. If you do not want to modify the list you should use QList::at().

Note: This is also true for QVector:

Note that using non-const operators can cause QVector to do a deep copy.

1 For QList, this is actually only true for Qt 5, not for Qt 4 (at least in the documentation), but for QVector it was already true for Qt 4, so the .at method was probably made consistent between all Qt containers.

This means that you cannot use operator[] directly on a non-const shared QList or on a non-const QVector, you would need to do something like:

QList<T> &mySharedQList = ...;
const QList<T> &myConstRef = mySharedQList;
myConstRef[0]; // Only ways not to copy the original QList

My guess is that the purpose of the .at is to provide you with a method that guarantees a constant access time, whatever the QList or QVector is (which operator[] does not guarantee). You do not need such method for standard library containers because non-const operator[] are already guaranteed to run in constant time (they are never going to make a copy).

Regarding the check on the index, this is probably to keep the behavior of the operator[] since, unlike std::vector, you probably want to use .at everywhere you only need to read data.

这篇关于为什么 QList::at() 不检查索引是否存在并返回只读值?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆