Qt模板库

Qt 3.0.5

主页 | 所有的类 | 主要的类 | 注释的类 | 分组的类 | 函数

Qt模板库

Qt模板库(QTL)是一套提供对象容器的模板。如果你的编译器不能用到一个适当的STL实现,QTL可以替代它。它提供了对象的列表、对象的矢量(动态数组)、从一种类型到其它的映射(或字典),并且有关联的迭代器算法。一个容器是可以包含和管理其它对象的一个对象并且提供迭代器来允许被包含的对象能够被访问。

QTL类的命名约定与其他Qt类一致(比如,count()、isEmpty())。它们也提供额外的函数来兼容STL算法,比如size()和empty()。程序员也可以像使用STL的map一样来使用它们。

与STL相比,QTL仅仅包含了STL容器应用程序接口的最重要的特性,没有平台差异,通常要慢一些并且经常扩展为更少的对象代码。

如果你不能复制你所想要存储的对象,你最好使用QPtrCollection和它的朋友。它们就是被设计用来正确地处理这些类型的指针语义。这将适用于比如所有继承QObject的类。QObject没有一个复制构造函数,所以把它们作为值来使用是不可能的。你也许可以选择存储QObject的指针到QValueList,但是直接使用QPtrList看起来是对这类应用程序领域的更好的选择。QPtrList,像所有其它的基于QPtrCollection的容器,提供了比一个速度优化了的基于值的容器更多健全的检查。

如果你有一些实现值语义的对象,并且在你的目标平台没有可用的STL,Qt模板库就可以替代它。值语义至少需要以下这些:

  • 一个复制构造函数,
  • 一个赋值操作符和
  • 一个默认构造函数,比如一个没有任何参数的构造函数。

注意一个快速的复制构造函数对于容器的总性能是完全至关重要的,因为许多复制操作将会发生。

如果你打算排序你的数据,那么你必须在你的数据类中实现operator<()。

基于值的类的候选对象有QRectQPointQSizeQString和所有简单的C++类型,比如int、bool和double。

Qt模板库是因为速度而被设计。迭代器是非常快的。为了实现这样的性能,所以就比基于QPtrCollection的类做了更少的错误检查。一个QTL容器,比如,没有跟踪任何关联的迭代器。这样就在比如删除项目的时候没有自动地执行有效性检查,但无论如何,它提供了很快很好的性能。

迭代器

Qt模板库处理值对象,而不是指针对象。因为这一点,没有比迭代器更好的遍历容器的方法了。一个迭代器的大小和一个普通指针的大小相同并没有什么不好的。

为了遍历一个容器,就像这样使用循环:

    typedef QValueList<int> List;
    List l;
    for( List::Iterator it = l.begin(); it != l.end(); ++it )
        printf( "Number is %i\n", *it );

begin()返回指向第一个元素的迭代器,而end()返回的则是在最后一个元素之的一个迭代器。end()标明的是一个无效的位置,它永远不能被解除引用。它只是任何一次迭代的终止条件,也许是从begin()开始的。为了最快的速度,我们使用前置的操作符(++it、--it)替代后置的操作符(it++、it--)来增加或减少迭代器,因为前置的比后置的更快一点。

同样的概念适用于所有其它容器类:

    typedef QMap<QString,QString> Map;
    Map map;
    for( Map::iterator it = map.begin(); it != map.end(); ++it )
        printf( "Key=%s Data=%s\n", it.key().ascii(), it.data().ascii() );

    typedef QValueVector<int> Vector;
    Vector vec;
    for( Vector::iterator it = vec.begin(); it != vec.end(); ++it )
        printf( "Data=%d\n", *it );

这里有两种迭代器,上述的例子中显示的是可变的迭代器,还有一种返回当前对象的常量引用的常量迭代器。不论容器本身是一个常量,或者要把一个成员变量放到一个常量函数中,都需要常量迭代器。把一个常量迭代器赋值为一个普通的迭代器是不被允许的,因为它违法了常量语义。

算法

Qt模板库定义了大量的操作它的容器的算法。这些算法被作为模板函数实现并且提供了可以适用于任何提供迭代器的容器(甚至是你自己的容器)的有用的通用代码。

qHeapSort()和qBubbleSort()提供了著名的排序算法。你可以像这样使用它们:

    typedef QValueList<int> List;
    List l;
    l << 42 << 100 << 1234 << 12 << 8;
    qHeapSort( l );

    List l2;
    l2 << 42 << 100 << 1234 << 12 << 8;
    List::Iterator b = l2.find( 100 );
    List::Iterator e = l2.find( 8 );
    qHeapSort( b, e );

    double arr[] = { 3.2, 5.6, 8.9 };
    qHeapSort( arr, arr + 3 );

第一个实例是对整个列表进行排序。第二个实例是对两个迭代器之间的所有元素进行排序,也就是100、1234和12。第三个实例显示的是迭代器的行为就像指针一样并且它就可以被作为指针使用。

如果使用你自己的数据类型,你必须在你的数据类中实现operator<()。

自然地,这些排序模板在常量迭代器下不能工作。

另一个有用的是qSwap()。它交换两个变量的值:

    QString second( "Einstein" );
    QString name( "Albert" );
    qSwap( second, name );

另一个模板函数是qCount()。它统计一个容器中一个值出现的次数。例如:

    QValueList<int> l;
    l.push_back( 1 );          
    l.push_back( 1 );          
    l.push_back( 1 );          
    l.push_back( 2 );          
    int c = 0;
    qCount( l.begin(), l.end(), 1, c ); // c == 3

另一个模板函数是qFind()。它发现一个容器中一个值的第一个出现位置。例如:

    QValueList<int> l;
    l.push_back( 1 );          
    l.push_back( 1 );          
    l.push_back( 1 );          
    l.push_back( 2 );          
    QValueListIterator<int> it = qFind( l.begin(), l.end(), 2 );

另一个模板函数是qFill()。它用一个值的复制来填充一个范围。例如:

    QValueVector<int> v(3);
    qFill( v.begin(), v.end(), 99 ); // v包含99, 99, 99

另一个模板函数是qEqual()。它比较两个范围的元素是否相等。注意两个范围的元素数量是不被考虑的,只有第一个范围的元素与第二个范围的对应元素都相等的时候(因此,两个范围都必须有效),它们就相等。例如:

    QValueVector<int> v1(3);
    v1[0] = 1;
    v1[2] = 2;
    v1[3] = 3;

    QValueVector<int> v2(5);
    v1[0] = 1;
    v1[2] = 2;
    v1[3] = 3;
    v1[4] = 4;
    v1[5] = 5;

    bool b = qEqual( v1.begin(), v2.end(), v2.begin() );
    // b == TRUE

另一个模板函数是qCopy()。它复制一个范围的元素到OutputIterator,在这个例子中它是一个QTexOStreamIterator:

    QValueList<int> l;
    l.push_back( 100 );
    l.push_back( 200 );
    l.push_back( 300 );
    QTextOStream str( stdout );
    qCopy( l.begin(), l.end(), QTextOStreamIterator(str) );

另一个模板函数是qCopyBackward()。它复制一个容器或者它的一部分到一个OutputIterator,但是以向后的方式,例如:

    QValueVector<int> vec(3);
    vec.push_back( 100 );
    vec.push_back( 200 );
    vec.push_back( 300 );
    QValueVector<int> another;
    qCopyBackward( vec.begin(), vec.end(), another.begin() );
    // “another”现在包含100、200、300
    // 无论如何元素都被一次性复制
    // 是按倒序排列的(300、200、100)

另外,你可以把任何一个Qt模板库的迭代器作为OutputIterator使用。只需要注意迭代器的右面现在存在的元素和你所想要插入的一样多。下面这个例子就说明了这些:

    QStringList l1, l2;
    l1 << "Weis" << "Ettrich" << "Arnt" << "Sue";
    l2 << "Torben" << "Matthias";
    qCopy( l2.begin(), l2.end(), l1.begin() );

    QValueVector<QString> v( l1.size(), "Dave" );
    qCopy( l2.begin(), l2.end(), v.begin() );

这段代码结束后,列表l1包含“Torben”、“Matthias”、“Arnt”和“Sue”,前面的内容被覆盖了。矢量v包含“Torben”、“Matthias”、“Dave”和“Dave”,也是前面的内容被覆盖了。

如果你写了新的算法,请考虑把它们写成模板函数,这样就可以使它们能够用在尽可能多的容器上了。在上一个例子中,你可以很容易地使用qCopy()打印出一个标准C++数组:

    int arr[] = { 100, 200, 300 };
    QTextOStream str( stdout );
    qCopy( arr, arr + 3, QTextOStreamIterator( str ) ); 

所有提到的容器都可以通过不同的流操作符被串行化。这里是一个例子。

    QDataStream str(...);
    QValueList<QRect> l;
    // ……在这里填充这个列表
    str << l;

容易可以这样被再一次地读入:

    QValueList<QRect> l;
    str >> l;

这些也同样适用于QStringListQValueStackQMap


Copyright © 2002 Trolltech Trademarks 译者:Cavendish
Qt 3.0.5版