Chinaunix首页 | 论坛 | 博客
  • 博客访问: 1035409
  • 博文数量: 243
  • 博客积分: 3053
  • 博客等级: 中校
  • 技术积分: 2975
  • 用 户 组: 普通用户
  • 注册时间: 2009-05-02 21:11
文章分类

全部博文(243)

文章存档

2013年(2)

2012年(20)

2011年(5)

2010年(114)

2009年(102)

我的朋友

分类:

2010-10-25 18:20:36

STL容器包含元素的要求

 

STL容器的元素必可以构造(copy-constructible赋值 assignable。本上来,可拷构造意味着象可以拷贝给另一个象(尽管准的解复杂的多)。同的,可赋值意味着可以可以将一个赋值给另一个象。些定可能听起来是多余的,因为对象一般都是可拷构造和可赋值

另一个附加的要求是容器元素必有拷构造器、默构造器、赋值运算符和公有申明的销毁器(式或含的)。

访问单一元素

重载运算符[]和成员函数at()使直接访问vector的元素成为可能。因为同时有const和非const
版本,所以他们可分别用于访问const元素和非const元素。

重载的[]运算符设计的和内建版本一样有效率。因此,也不会检查是否引用的是有效的元素。由于缺乏运行期检查保证了最快的访问速度(运算符[]的调用通常是内联的)。但是非法的下标的运算符[]是未定义行为。当性能十分重要,当代码写的很仔细不可能有非法下标时,使用[]运算符。[]符号也更易读更直观。尽管如此,运行期检查在有些情况下也是不可避免的,例如当下标值是从外部源得到的:函数、数据库记录、或输入的。在这种情况下你要使用成员函数at ()来代替运算符[]。at()执行范围检查,并且在试图访问范围以外成员时它抛出类型std::out_of_range
的异常。这里时一个例子:
#include
#include
#include
#include
using namespace std;
int main()
{
  vector vs; //vs现在没有一个元素
  vs.push_back("string"); //增加第一个元素
  vs[0] = "overriding string"; //重载运算符[]
  try
  {
    cout<< vs.at(10) <  }
  catch(std::out_of_range & except)
  {
    //下标越界的处理程序
  }
}//end main

Front和Back操作

Front 和back操作分别引用容器开始和结尾。成员函数push_back()在容器的结尾附加一个元素。当容器耗尽它的空闲内存时,函数重分配内存再附加元素。成员函数pop_back()将最后一个元素移出容器。成员函数front()和back()分别访问容器开始和结尾的一个元素。front()和 back()有const和非const版本。例如
#include
#include
using namespace std;
int main()
{
  vector v;
  v.push_back(5);
  v.push_back(10);
  cout<<"front: " << v.front() << endl; //5
  cout<<"back: " << v.back() << endl; //10
  v.pop_back(); //remove v[1]
  cout<<"back: " << v.back() << endl; //now 5
  return 0;
}

容器赋值

STL容器重载赋值运算符,因此允许同类型的容器互相赋值,例如
#include
#include
using namespace std;
int main()
{
  vector vi;
  vi.push_back(1);
  vi.push_back(2);
  vector new_vector;
  //拷贝vi的内容到new_vector,自动增长到合适大小
  new_vector = vi;
  cout << new_vector[0] << new_vector[1] << endl;   //显显示1和2
  return 0;
}

一个vector不能存储Derived对象

每一个vector元素丢必须是同样的大小。因为派生对象可能有额外的成员,它的大小可能比基类要大。不要将派生对象存储在vector<基类 >中,因为这将导致未定义的向上对象转换(object slicing)。但是你可以通过存储派生对象指针到vector<基类*>来达到多态。

FIFO对象模型

在队列数据模型中(一个队列也叫FIFO先进先出),插入的第一个元素在顶部,其后插入的元素都在其后。队列的两个基本操作是pop()和push()。push()操作将一个元素插入到队列的底部。pop()操作从顶端移出第一次
插入的元素;从而使第二个元素成为顶端元素。STLqueue容器象下面这样使用:
#include
#include
using namespace std;
int main()
{
queue iq;
iq.push(93); //插入第一个元素,它是最顶部的元素
iq.push(250);
iq.push(10); //最后一个插入的元素在底部
cout<<"currently there are "<< iq.size() << " elements" << endl;
while (!iq.empty() )
{
   cout <<"the last element is: "<< iq.front() << endl; //front()返回
                                                        //顶部元素
  iq.pop(); //移出顶部元素
}
return 0;
}

STL也定义了双向队列,或deque(pronounced "deck")容器。deque是能有效处理尾部的队列。其他类型的队列有priority_queue。priority_queue的所有元素以其优先权排序。有高有限权的元素在顶部。为了取得作为priority_queue元素的资格,对象必须定义<运算符(priority_queue在“函数对象”一节讨论)。

迭代器

Iterators可以认为是一种通用指针。他们用于操作容器的元素而不需要知道实际元素类型。许多成员函数,比如begin()和end(),返回一个指向容器尾的迭代器。

begin()和end()

所有的STL容器提供begin()和end()成员函数。begin()返回指向容器第一个元素的迭代器。例

#include
#include
#include
using namespace std;
int main()
{
  vector v(1);  //单一元素的空间
  v[0] = 10;
  vector::iterator p  = v.begin();   //p指向v的第一个元素
  *p = 11; //通过p将一个新的值赋给v[0]
  cout << *p;  //输出11
  return 0;
}

另一方面成员函数end()返回一个指向位于容器最后一个元素之后(past)的迭代器。第一次听到时很不可思议,但是如果你考虑到C风格的字符串是如何表示的,这就不是什么不可理解的事:一个附加的null字符被自动附加到char 数组最后一个元素的后面。在STL中附加元素起同样的作用,标记容器的结尾。让end()返回指向容器最后一个元素之后的迭代器对于for和while循环很有用。例如
  vector v(10);
  int n=0;
  for (vector::iterator p = v.begin(); p    *p = n++;

begin()和end()有两种版本:const和非const。非const版本返回非const 迭代器,它允许用户修改容器元素的值。const版本返回const 迭代器,它不允许用户修改容器。

例如
  const vector v(10);
  vector::iterator p  = v.begin(); //错误,必须使用const_iterator
  vector::const_iterator cp  = v.begin(); //OK
  *cp = 'a'; //error,试图改变const对象
  cout << *cp;  //OK

成员函数rbegin()和rend()(降序begin()和降序end())与begin()和end()十分相似,只是返回降序迭代器,它们适用于降序序列。本质上说,降序迭代器就是普通迭代器,只不过所重载的运算符++不同--。需要降序访问容器的时候,是很有用的。

例如
#include
#include
#include
using namespace std;
void ascending_order()
{
  vector v(10);
  double d = 0.1;
  for (vector::iterator p = v.begin(); p  {
    *p = d;
    d+= 0.1;
  }
   //以升序显式v的元素
  for (vector::reverse_iterator rp = v.rbegin(); rp < v.rend(); rp++)
  {
    cout<< *rp<  }
}

与begin()和end()一样rbegin()和rend()也有const和非const
版本

迭代器的底层实现

STL 的大多数实现使用指针来实现迭代器。但是一个迭代器不需要一个指针,并且有一个很好的理由。考虑一个存储在6GB磁盘上扫描图象的巨大vector;大多数机器上内建的指针只有32位不够来表示这种巨大的vector。代替指针的一种方案是用64位整数来实现迭代器。同样的,保存位或半元组(nibbles )(内建指针不能引用)也可以通过不同的底层类型的迭代器来来实现,并提供相同的接口然而,有时候纯指针也用于迭代特定实现的容器的元素;例如
#include
#include
using namespace std;
void hack()
{
  vector vi;
  vi.push_back(5);
  int *p = vi.begin();//糟糕的程序设计习惯,尽管它可以工作
  *p = 6; //赋值vi[0]
  cout<}

使用纯指针来代替迭代器是一种糟糕的程序设计习惯,应该避免。

迭代器的“const Correctness”

当元素可以被访问但不能改变时,使用容器的const迭代器。作为普通指针类型,使用非const迭代器暗示容器的内容是可改变的。const使编译器可以检测到简单的错误,也更可读。

通过内建数组来初始化Vector的内容

就像前面说的,内建数组是一种有效的序列容器。因此,可以通过数组首尾的地址可以用内建数组的内容来初始化一个vector。例如
#include
#include
using namespace std;
int main()
{
  int arr[3];
  arr[0] = 4; arr[1] = 8;  arr[2] = 16;
   vector vi ( &arr[0], // 数组开始地址
                     &arr[3] ); //必须指向数组最后一个元素之后的元素
  cout<< vi[0] << '\t' << vi[1] << '\t' << vi[2] <  return 0;
}

迭代器的失效

当成员函数改变它的容器时将发生重分配。可能改变的成员函数是reserve()和resize(),push_back()和pop_back(), erase(),clear(),insert()和其他。另外,赋值运算符和可能改变的算法也可能导致重分配。当容器重分配它的元素是,他们的地址改变了。因此,存在的迭代器的值无效了。

例如
#include
#include
using namespace std;
int main()
{
  list payroll;
  payroll.push_back(5000.00);
  list::const_iterator p = payroll.begin(); //指向第一个元素
  for (int i = 0 ; i < 10; i++)
  {
      payroll.push_back(4500.00); //向payroll插入10以上元素;
                                   //将产生重分配
  }
     //危险
  cout << "first element in payroll: "<< *p <  return 0;
}

在前面的例子中,payroll可能在插入十个元素的过程中发生重分配,因此p的值可能失效。使用失效的迭代器就像使用指向已删除对象的指针一样,都是为定义行为。为了安全起见,推荐你在调用了可能改变的函数之后因该重赋值迭代器。例如
list::const_iterator p = payroll.begin();//指向第一个元素。
for (int i = 0 ; i < 10; i++)
{
  payroll.push_back(4500.00); //可能发生重分配
}
  p = payroll.begin(); //重赋值p
  cout <<"first element in payroll: "<<*p<}

作为选择,你可以通过在实例化预分配足够的内存来避免重分配。例如
int main()
{
  list payroll;
  payroll.reserve(11);
  payroll.push_back(5000.00);
  list::const_iterator p = payroll.begin(); 
  for (int i = 0 ; i < 10; i++)
  {
    payroll.push_back(4500.00); //不会发生重分配
  }
  cout << "first element in payroll: "<< *p <  return 0;
}

阅读(1832) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~