doc: Corrected docs for QList and QVector

The docs for QList advised users to choose QList
over QVector for efficiency reasons. The advise
should be to use QVector over QList for efficiency
reasons. This update corrects that misunderstanding.

Change-Id: Ie04c99ab7fe6aef4bd1d39175c9564455b0122de
Task-number: QTBUG-47196
Reviewed-by: Topi Reiniö <topi.reinio@digia.com>
Reviewed-by: Marc Mutz <marc.mutz@kdab.com>
This commit is contained in:
Martin Smith 2015-07-16 13:57:26 +02:00
parent c067c012dc
commit 02c906acc3
3 changed files with 120 additions and 73 deletions

View File

@ -65,4 +65,13 @@
\externalpage http://doc-snapshot.qt-project.org/qt5-5.4/designer-widget-mode.html#the-property-editor
\title Qt Designer's Widget Editing Mode#The Property Editor
*/
/*!
\externalpage http://marcmutz.wordpress.com/effective-qt/containers/#containers-qlist
\title Pros and Cons of Using QList
*/
/*!
\externalpage http://marcmutz.wordpress.com/effective-qt/containers/
\title Understand the Qt Containers
*/

View File

@ -332,41 +332,56 @@ void **QListData::erase(void **xi)
\reentrant
QList\<T\> is one of Qt's generic \l{container classes}. It
stores a list of values and provides fast index-based access as
well as fast insertions and removals.
stores items in a list that provides fast index-based access
and index-based insertions and removals.
QList\<T\>, QLinkedList\<T\>, and QVector\<T\> provide similar
functionality. Here's an overview:
APIs and functionality. They are often interchangeable, but there
are performance consequences. Here is an overview of use cases:
\list
\li For most purposes, QList is the right class to use. Its
index-based API is more convenient than QLinkedList's
iterator-based API, and it is usually faster than
QVector because of the way it stores its items in
memory. It also expands to less code in your executable.
\li If you need a real linked list, with guarantees of \l{constant
time} insertions in the middle of the list and iterators to
items rather than indexes, use QLinkedList.
\li If you want the items to occupy adjacent memory positions,
use QVector.
\li QVector should be your default first choice.
QVector\<T\> will usually give better performance than QList\<T\>,
because QVector\<T\> always stores its items sequentially in memory,
where QList\<T\> will allocate its items on the heap unless
\c {sizeof(T) <= sizeof(void*)} and T has been declared to be
either a \c{Q_MOVABLE_TYPE} or a \c{Q_PRIMITIVE_TYPE} using
\l {Q_DECLARE_TYPEINFO}. See the \l {Pros and Cons of Using QList}
for an explanation.
\li However, QList is used throughout the Qt APIs for passing
parameters and for returning values. Use QList to interface with
those APIs.
\li If you need a real linked list, which guarantees
\l {Algorithmic Complexity}{constant time} insertions mid-list and
uses iterators to items rather than indexes, use QLinkedList.
\endlist
\note QVector and QVarLengthArray both guarantee C-compatible
array layout. QList does not. This might be important if your
application must interface with a C API.
Internally, QList\<T\> is represented as an array of pointers to
items of type T. If T is itself a pointer type or a basic type
that is no larger than a pointer, or if T is one of Qt's \l{shared
classes}, then QList\<T\> stores the items directly in the pointer
array. For lists under a thousand items, this array representation
allows for very fast insertions in the middle, and it allows
index-based access. Furthermore, operations like prepend() and
append() are very fast, because QList preallocates memory at both
\note Iterators into a QLinkedList and references into
heap-allocating QLists remain valid long as the referenced items
remain in the container. This is not true for iterators and
references into a QVector and non-heap-allocating QLists.
Internally, QList\<T\> is represented as an array of T if
If \c{sizeof(T) <= sizeof(void*)} and T has been declared to be
either a \c{Q_MOVABLE_TYPE} or a \c{Q_PRIMITIVE_TYPE} using
\l {Q_DECLARE_TYPEINFO}. Otherwise, QList\<T\> is represented
as an array of T* and the items are allocated on the heap.
The array representation allows very fast insertions and
index-based access. The prepend() and append() operations are
also very fast because QList preallocates memory at both
ends of its internal array. (See \l{Algorithmic Complexity} for
details.) Note, however, that for unshared list items that are
larger than a pointer, each append or insert of a new item
requires allocating the new item on the heap, and this per item
allocation might make QVector a better choice in cases that do
lots of appending or inserting, since QVector allocates memory for
its items in a single heap allocation.
details.
Note, however, that when the conditions specified above are not met,
each append or insert of a new item requires allocating the new item
on the heap, and this per item allocation will make QVector a better
choice for use cases that do a lot of appending or inserting, because
QVector can allocate memory for many items in a single heap allocation.
Note that the internal array only ever gets bigger over the life
of the list. It never shrinks. The internal array is deallocated
@ -401,9 +416,10 @@ void **QListData::erase(void **xi)
\snippet code/src_corelib_tools_qlistdata.cpp 2
Because QList is implemented as an array of pointers, this
operation is very fast (\l{constant time}). For read-only access,
an alternative syntax is to use at():
Because QList is implemented as an array of pointers for types
that are larger than a pointer or are not movable, this operation
requires (\l{Algorithmic Complexity}{constant time}). For read-only
access, an alternative syntax is to use at():
\snippet code/src_corelib_tools_qlistdata.cpp 3
@ -417,10 +433,10 @@ void **QListData::erase(void **xi)
\snippet code/src_corelib_tools_qlistdata.cpp 4
Inserting and removing items at either ends of the list is very
fast (\l{constant time} in most cases), because QList
preallocates extra space on both sides of its internal buffer to
allow for fast growth at both ends of the list.
Inserting and removing items at either end of the list is very
fast (\l{Algorithmic Complexity}{constant time} in most cases),
because QList preallocates extra space on both sides of its
internal buffer to allow for fast growth at both ends of the list.
If you want to find all occurrences of a particular value in a
list, use indexOf() or lastIndexOf(). The former searches forward
@ -481,6 +497,11 @@ void **QListData::erase(void **xi)
\l{QStringList::removeDuplicates()}{removeDuplicates},
\l{QStringList::sort()}{sort}.
\section1 More Information on Using Qt Containers
For a detailed discussion comparing Qt containers with each other and
with STL containers, see \l {Understand the Qt Containers}.
\sa QListIterator, QMutableListIterator, QLinkedList, QVector
*/
@ -512,10 +533,11 @@ void **QListData::erase(void **xi)
Constructs a copy of \a other.
This operation takes \l{constant time}, because QList is
\l{implicitly shared}. This makes returning a QList from a
function very fast. If a shared instance is modified, it will be
copied (copy-on-write), and that takes \l{linear time}.
This operation takes \l{Algorithmic Complexity}{constant time},
because QList is \l{implicitly shared}. This makes returning a
QList from a function very fast. If a shared instance is modified,
it will be copied (copy-on-write), and that takes
\l{Algorithmic Complexity}{linear time}.
\sa operator=()
*/
@ -700,7 +722,7 @@ void **QListData::erase(void **xi)
Returns the item at index position \a i in the list. \a i must be
a valid index position in the list (i.e., 0 <= \a i < size()).
This function is very fast (\l{constant time}).
This function is very fast (\l{Algorithmic Complexity}{constant time}).
\sa value(), operator[]()
*/
@ -713,8 +735,8 @@ void **QListData::erase(void **xi)
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
\l{constant time}. If you do not want to modify the list you should use
QList::at().
\l{Algorithmic Complexity}{constant time}. If you do not want to modify
the list you should use QList::at().
\sa at(), value()
*/
@ -723,7 +745,7 @@ void **QListData::erase(void **xi)
\overload
Same as at(). This function runs in \l{constant time}.
Same as at(). This function runs in \l{Algorithmic Complexity}{constant time}.
*/
/*! \fn QList::reserve(int alloc)
@ -749,9 +771,9 @@ void **QListData::erase(void **xi)
This is the same as list.insert(size(), \a value).
If this list is not shared, this operation is typically
very fast (amortized \l{constant time}), because QList
preallocates extra space on both sides of its internal
buffer to allow for fast growth at both ends of the list.
very fast (amortized \l{Algorithmic Complexity}{constant time}),
because QList preallocates extra space on both sides of its
internal buffer to allow for fast growth at both ends of the list.
\sa operator<<(), prepend(), insert()
*/
@ -777,9 +799,9 @@ void **QListData::erase(void **xi)
This is the same as list.insert(0, \a value).
If this list is not shared, this operation is typically
very fast (amortized \l{constant time}), because QList
preallocates extra space on both sides of its internal
buffer to allow for fast growth at both ends of the list.
very fast (amortized \l{Algorithmic Complexity}{constant time}),
because QList preallocates extra space on both sides of its
internal buffer to allow for fast growth at both ends of the list.
\sa append(), insert()
*/
@ -870,7 +892,8 @@ void **QListData::erase(void **xi)
same as takeAt(0). This function assumes the list is not empty. To
avoid failure, call isEmpty() before calling this function.
If this list is not shared, this operation takes \l{constant time}.
If this list is not shared, this operation takes
\l {Algorithmic Complexity}{constant time}.
If you don't use the return value, removeFirst() is more
efficient.
@ -885,7 +908,8 @@ void **QListData::erase(void **xi)
not empty. To avoid failure, call isEmpty() before calling this
function.
If this list is not shared, this operation takes \l{constant time}.
If this list is not shared, this operation takes
\l {Algorithmic Complexity}{constant time}.
If you don't use the return value, removeLast() is more
efficient.

View File

@ -45,34 +45,42 @@
stores its items in adjacent memory locations and provides fast
index-based access.
QList\<T\>, QLinkedList\<T\>, and QVarLengthArray\<T\> provide
similar functionality. Here's an overview:
QList\<T\>, QLinkedList\<T\>, QVector\<T\>, and QVarLengthArray\<T\>
provide similar APIs and functionality. They are often interchangeable,
but there are performance consequences. Here is an overview of use cases:
\list
\li For most purposes, QList is the right class to use. Operations
like prepend() and insert() are usually faster than with
QVector because of the way QList stores its items in memory
(see \l{Algorithmic Complexity} for details),
and its index-based API is more convenient than QLinkedList's
iterator-based API. It also expands to less code in your
executable.
\li If you need a real linked list, with guarantees of \l{constant
time} insertions in the middle of the list and iterators to
items rather than indexes, use QLinkedList.
\li If you want the items to occupy adjacent memory positions, or
if your items are larger than a pointer and you want to avoid
the overhead of allocating them on the heap individually at
insertion time, then use QVector.
\li If you want a low-level variable-size array, QVarLengthArray
may be sufficient.
\li QVector should be your default first choice.
QVector\<T\> will usually give better performance than QList\<T\>,
because QVector\<T\> always stores its items sequentially in memory,
where QList\<T\> will allocate its items on the heap unless
\c {sizeof(T) <= sizeof(void*)} and T has been declared to be
either a \c{Q_MOVABLE_TYPE} or a \c{Q_PRIMITIVE_TYPE} using
\l {Q_DECLARE_TYPEINFO}. See the \l {Pros and Cons of Using QList}
for an explanation.
\li However, QList is used throughout the Qt APIs for passing
parameters and for returning values. Use QList to interface with
those APIs.
\li If you need a real linked list, which guarantees
\l{Algorithmic Complexity}{constant time} insertions mid-list and
uses iterators to items rather than indexes, use QLinkedList.
\endlist
\note QVector and QVarLengthArray both guarantee C-compatible
array layout. QList does not. This might be important if your
application must interface with a C API.
\note Iterators into a QLinkedList and references into
heap-allocating QLists remain valid long as the referenced items
remain in the container. This is not true for iterators and
references into a QVector and non-heap-allocating QLists.
Here's an example of a QVector that stores integers and a QVector
that stores QString values:
\snippet code/src_corelib_tools_qvector.cpp 0
QVector stores a vector (or array) of items. Typically, vectors
QVector stores its items in a vector (array). Typically, vectors
are created with an initial size. For example, the following code
constructs a QVector with 200 elements:
@ -166,6 +174,11 @@
with references to its own values. Doing so will cause your application to
abort with an error message.
\section2 More Information on Using Qt Containers
For a detailed discussion comparing Qt containers with each other and
with STL containers, see \l {Understand the Qt Containers}.
\sa QVectorIterator, QMutableVectorIterator, QList, QLinkedList
*/
@ -218,10 +231,11 @@
Constructs a copy of \a other.
This operation takes \l{constant time}, because QVector is
\l{implicitly shared}. This makes returning a QVector from a
function very fast. If a shared instance is modified, it will be
copied (copy-on-write), and that takes \l{linear time}.
This operation takes \l{Algorithmic Complexity}{constant time},
because QVector is \l{implicitly shared}. This makes returning
a QVector from a function very fast. If a shared instance is
modified, it will be copied (copy-on-write), and that takes
\l{Algorithmic Complexity}{linear time}.
\sa operator=()
*/