从一段小程序看vector的[]下标运算符与size()



先看看这段小程序,会输出什么


#include
#include


using namespace std;


void test_vec(void)
{
vector v;

v.reserve(10);
cout<<"capacity "< cout<<"size "<

v[0] = 1;
v[1] = 3;
cout << "v[0] = " << v[0] << endl;
cout << "v[1] = " << v[1] << endl;
cout << "size before push:" << v.size() << endl;

v.push_back(4);
v.push_back(5);
cout << "size after push:" << v.size() << endl;
vector::iterator iter;
int i = 0;
for (iter = v.begin(); iter != v.end(); iter++, ++i)
{
cout << i << " " << *iter< }
}


int main()
{
test_vec();
return 0;
}


当第一次看的时候,我以为会输出:
capacity 10
size 0
v[0] = 1
v[1] = 3
size before push:2
size after push:4
0 1
1 3
事实证明我对vector太不了解:
capacity 10
size 0
v[0] = 1
v[1] = 3
size before push:0
size after push:2
0 4
1 5


其实这里有两个需要注意的问题:
1. 容器的存储空间与size()
2. vectror的下标运算符[]
当调用reserve()时,只是为v保留了一段内存区域,而此时并未向容器中插入什么值,所以size()的结果是不会改变的。
使用命令在/usr/目录下使用命令 find ./ -name "*g++*"找到了gcc中STL实现的源代码,查看其中vector的reserve()
的注释说明的很清楚,它只是预分配了一段内存:
      /**
       *  @brief  Attempt to preallocate enough memory for specified number of
       *          elements.
       *  @param  n  Number of elements required.
       *  @throw  std::length_error  If @a n exceeds @c max_size().
       *
       *  This function attempts to reserve enough memory for the
       *  %vector to hold the specified number of elements.  If the
       *  number requested is more than max_size(), length_error is
       *  thrown.
       *
       *  The advantage of this function is that if optimal code is a
       *  necessity and the user can determine the number of elements
       *  that will be required, the user can reserve the memory in
       *  %advance, and thus prevent a possible reallocation of memory
       *  and copying of %vector data.
       */
      void
      reserve(size_type __n);
要明白第二次循环输出的结果,需要理解vector的下标运算符只是提供给我们一个对vector中元素的读写的方便,让我们像操作数组一样的习惯可以用于vector的中元素值的读取和修改,但它并不修改容器的size值!查看gcc源代码,还发现了其中有两个版本,一个是返回非常引用的,一个是返回常引用的版本:
      // element access
      /**
       *  @brief  Subscript access to the data contained in the %vector.
       *  @param n The index of the element for which data should be
       *  accessed.
       *  @return  Read/write reference to data.
       *
       *  This operator allows for easy, array-style, data access.
       *  Note that data access with this operator is unchecked and
       *  out_of_range lookups are not defined. (For checked lookups
       *  see at().)
       */
      reference
      operator[](size_type __n)
      { return *(this->_M_impl._M_start + __n); }
 
 /**
       *  @brief  Subscript access to the data contained in the %vector.
       *  @param n The index of the element for which data should be
       *  accessed.
       *  @return  Read-only (constant) reference to data.
       *
       *  This operator allows for easy, array-style, data access.
       *  Note that data access with this operator is unchecked and
       *  out_of_range lookups are not defined. (For checked lookups
       *  see at().)
       */
      const_reference
      operator[](size_type __n) const
      { return *(this->_M_impl._M_start + __n); }
很容易看出其中并没有修改size的操作。看到这里,可能部分人会有疑问:为什么这里要重载一个返回常引用的版本?
答案就是为了兼容一些使用常量或常引用作参数的场合。比如:
int func(const vector& v)
{
...
cout << v[1] << endl;
...
}
此种情况下,是需要提供常量版本的operator[]()。此时可能还有人会说,那提供一个这样的原型
xxx operator[](size_type __n) const;不是就可以了吗?可惜stl vector是通用模板,为了减少拷贝次数,返回的是引用;而一个const返回非const引用是不安全的,也是通不过编译的。


看了vector的operator[]的实现,个人认为其实应该在其中添加一个下标范围是否在size内的判断,这样的话,一些错
误的下标运算符访问就不会直接导致程序崩溃了(比如v[size()+1])。不过再仔细想想,这个时候应该给外部返回什么值最恰当呢?并且添加这个判断是否会导致通用性能下降?也许作者就是为了提高性能,降低实现复杂度而没有这样做吧!


总结一下:
1. vector的存储空间与元素个数(size)没有直接的联系。
2. operator[]只是为了已有元素读取、修改方便而实现,不会影响容器元素个数,也不会校验下标范围是否合法。

你可能感兴趣的:(语言)