C++ 访问元素

示例

访问元素中的元素有两种主要方法 std::vector

  • 基于索引的访问

  • 迭代器

基于索引的访问:

这可以通过下标运算符[]或成员函数来完成at()。

两者均会返回对元素中相应位置的引用std::vector(除非是vector<bool>),以便可以读取和修改该元素(如果向量不是const)。

[]和at()不同之处在于,[]它不能保证同时执行任何边界检查at()。访问元素index < 0或index >= size为时未定义行为的元素[],而at()抛出std::out_of_range异常。

注意:为了清楚起见,以下示例使用C ++ 11样式的初始化,但是运算符可用于所有版本(除非标记为C ++ 11)。

C ++ 11
std::vector<int> v{ 1, 2, 3 };

// 使用[]

int a = v[1];    // 一个是2

v[1] = 4;        // v现在包含{1,4,3}

// 使用at()

int b = v.at(2); // b是3

v.at(2) = 5;     // v现在包含{1,4,5}

int c = v.at(3); // 引发std :: out_of_range异常

由于该at()方法执行边界检查并可能引发异常,因此它比慢[]。这将成为[]首选代码,在该代码中,操作的语义可确保索引处于范围内。在任何情况下,对向量元素的访问都在固定时间内完成。这意味着访问向量的第一个元素的时间(时间)与访问第二个元素,第三个元素等的代价相同。

例如,考虑此循环

for (std::size_t i = 0; i < v.size(); ++i) {

    v[i] = 1;

}

在这里,我们知道index变量i始终处于界限内,因此i对于每次调用都检查界限是否在CPU周期内是浪费的operator[]。

的front()和back()成员函数允许向量的第一和最后一个元素容易参考存取,分别。这些位置是经常使用的,特殊访问器比使用[]以下方法的替代方法更具可读性:

std::vector<int> v{ 4, 5, 6 }; // 在C ++ 11之前的版本中,这比较冗长

int a = v.front();   // a为4,v.front()等同于v [0]

v.front() = 3;       // v现在包含{3,5,6}

int b = v.back();    // b为6,v.back()等同于v [v.size()-1]

v.back() = 7;        // v现在包含{3,5,7}

注意:调用front()或back()在空向量上是未定义的行为。您需要empty()在调用front()或之前使用成员函数(用于检查容器是否为空)检查容器是否为空back()。以下是使用'empty()'测试空向量的简单示例:

int main ()

{

  std::vector<int> v;

  int sum (0);

  for (int i=1;i<=10;i++) v.push_back(i);//创建并初始化向量

  while (!v.empty())//循环直到向量测试为空

  {

     sum += v.back();//保持连续运行

     v.pop_back();//弹出将其从向量中删除的元素

  }

  std::cout << "total: " << sum << '\n';//将总数输出给用户

  return 0;

}

上面的示例创建了一个向量,该向量的序列从1到10。然后弹出向量的元素,直到向量为空(使用' empty()')以防止未定义的行为。然后计算向量中数字的总和并显示给用户。

C ++ 11

该data()方法返回一个指针,该指针指向用来std::vector内部存储其元素的原始内存。在将向量数据传递给期望使用C样式数组的旧代码时,通常使用此方法。

std::vector<int> v{ 1, 2, 3, 4 }; // v包含{1、2、3、4}

int* p = v.data(); // p指向1

*p = 4;            // v现在包含{4,2,3,4}

++p;               // p指向2

*p = 3;            // v现在包含{4,3,3,4}

p[1] = 2;          // v现在包含{4,3,2,4}

*(p + 2) = 1;      // v现在包含{4,3,2,1}

C ++ 11

在C ++ 11之前,data()可以通过调用front()并获取返回值的地址来模拟该方法:

std::vector<int> v(4);

int* ptr = &(v.front()); // or &v[0]

之所以可行,是因为假设矢量的内容不会覆盖unary,则始终可以保证矢量将其元素存储在连续的内存位置中operator&。如果是这样,则必须std::addressof在C ++ 11之前的版本中重新实现。它还假定向量不为空。

迭代器:

迭代器在示例“迭代之上std::vector”和文章迭代器中有更详细的说明。简而言之,它们的作用类似于指向向量元素的指针:

C ++ 11
std::vector<int> v{ 4, 5, 6 };

auto it = v.begin();

int i = *it;        // 我4岁

++it; 

i = *it;            // 我5岁

*it = 6;            // v包含{4,6,6}

auto e = v.end();   // e指向v结束后的元素。可以是 

                    // 用于检查迭代器是否到达向量的末尾:

++it; 

it == v.end();      // false,它指向位置2处的元素(值6)

++it;

it == v.end();      // 真正

std::vector<T>迭代器实际 T*s与标准是一致的,但是大多数标准库都不这样做。不这样做会改善错误消息,捕获不可移植的代码,并且可用于在非发行版中通过调试检查来对迭代器进行检测。然后,在发行版本中,围绕基础指针的类被优化了。


您可以持久保存对向量元素的引用或指针,以进行间接访问。这些对元素中元素的引用或指针vector保持稳定,并且访问保持定义,除非您在中的元素之前或之后添加/删除元素vector,或者导致vector容量发生变化。这与使迭代器无效的规则相同。

C ++ 11
std::vector<int> v{ 1, 2, 3 };

int* p = v.data() + 1;     // p指向2

v.insert(v.begin(), 0);    // p现在无效,访问* p是未定义的行为。

p = v.data() + 1;          // p指向1

v.reserve(10);             // p现在无效,访问* p是未定义的行为。

p = v.data() + 1;          // p指向1

v.erase(v.begin());        // p现在无效,访问* p是未定义的行为。

以上是 C++ 访问元素 的全部内容, 来源链接: utcz.com/z/340656.html

回到顶部