使用迭代器时如何访问对象在向量中的位置?
How to access the position of an object within a vector when using an iterator?
我目前正在这样做:
for (std::vector<myClass>::const_iterator i = myVec.begin(); i != myVec.end(); ++i) {
std::cout << *i << ", ";
}
因为这是输出向量所有成员的推荐方法。问题是这意味着这个向量的所有成员都在一条单数线上得到输出。我想拥有它,以便我在每行输出 5,这样我就不必滚动太久。 如果我不使用迭代器,所以我的i
只是一个int
,我可以说if (i%5 == 0)
然后cout << endl
但因为它是一个迭代器,我很困惑。我尝试了各种方式说我想做什么,但我找不到答案......这可以通过迭代器来实现,还是我应该只使用 int 代替..?
添加int pos = i-myVec.begin()
。然后,您可以根据需要使用if (pos%5 == 0)
。
是的,您可以使用迭代器执行此操作:
for (std::vector<myClass>::const_iterator i = myVec.begin(); i != myVec.end(); ++i) {
std::cout << *i << ", ";
if (i != myVec.begin() && std::distance(myVec.begin(), i) % 5 == 0)
std::cout << "n";
}
在这种情况下,计数循环也可以工作:
for (auto i = 0u; i < myVec.size(); ++i) {
std::cout << myVec[i] << ", ";
if (i && i % 5 == 0)
std::cout << "n";
}
你也可以用一个范围循环来写这个:
int i = 0;
for (auto &elem : myVec) {
std::cout << elem << ", ";
if (++i % 5 == 0)
std::cout << "n";
}
如果使用 range-v3 库,则可以执行以下操作:
namespace rs = ranges;
for (auto line : myVec | rs::views::chunk(5)) {
rs::copy(line, rs::ostream_iterator<myClass>(std::cout, ",");
std::cout << "n";
}
或者改为:
namespace rv = ranges::views;
for (auto [i, line] : rv::enumerate(myVec)) {
std::cout << elem << ", ";
if (i && i % 5 == 0)
std::cout << "n";
}
在 C++20 中,您可能会执行以下操作:
for (int counter = 0; auto&& e : myVec) {
std::cout << e << ", ";
if (++counter == 5) {
counter = 0;
std::cout << "n";
}
}
以前
int counter = 0;
for (auto&& e : myVec) {
std::cout << e << ", ";
if (++counter == 5) {
counter = 0;
std::cout << "n";
}
}
与往常一样,实现这一目标的可能性很大。但要回答你最初的问题:
for (std::vector<myClass>::const_iterator i = myVec.begin(); i != myVec.end(); ++i) {
std::cout << *i << ", ";
size_t index = i - myVec.begin();
if (index % 5 == 0) {
std::cout << "n";
}
}
这将打印迭代器位置的值和向量中的索引。计算向量begin()
与当前迭代器位置(即索引)之间的差值。
虽然可以这样做,但我不建议这样做,因为每次迭代都有一个额外的减法。这通常不是迭代器非常有用的情况,正如您自己所看到的那样。
如果你可以访问一个足够现代的C++编译器(我猜你是这样做的),你可以使用非常优雅的基于范围的for循环:
for (myClass& obj : myVec) {
std::cout << obj;
}
这将"提取"对矢量中对象的引用。当然,现在你离索引位置更远了,因为你不能使用迭代减法技巧。要解决保留索引的问题,您只需保留一个单独的索引计数器:
int i = 0;
for (myClass& obj : myVec) {
std::cout << obj << ", ";
if (i % 5 == 0) {
std::cout << "n";
}
i++;
}
甚至使用内联初始化变量的非常新的 C++20 功能(我不记得这些的正式名称):
for (int i = 0; myClass& obj : myVec) {
std::cout << obj << ", ";
if (i++ % 5 == 0) {
std::cout << "n";
}
}
或者你可以只使用经典的for (int i = 0; i < size; i++) ...
,尽管这不像基于范围的for循环版本那么清楚。使用基于范围的for循环,可以立即看到矢量中所有对象都已完成某些操作。
您可以声明一个不同的变量来使用它进行计数:
// example vector
vector<int> myVec {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15};
int c = 0;
for (auto i = myVec.begin(); i != myVec.end(); i++) {
// incrementing the counter and verifying if it gives 0 as the remainder
if (c++ % 5 == 0)
cout << endl;
std::cout << *i << ", ";
}
然后,您将获得:
1, 2, 3, 4, 5, // 5 values
6, 7, 8, 9, 10, // 5 values
11, 12, 13, 14, 15, // 5 values
注意:您可以使用auto
关键字来减少此类长声明:
std::vector<myClass>::const_iterator i = myVec.begin();
// better:
auto i = myVec.begin();
只是为了好玩,这里有一个示例代码,它允许您通过 range-for 循环访问索引和值。这具有有限的价值,但显示了一般方法(RangesTS内置了此方法):
#include <vector>
#include <utility>
#include <iostream>
template<class UnderlyingIterator>
struct IndexingIterator {
using ValueType = std::pair<long long int, const typename UnderlyingIterator::value_type&>;
IndexingIterator(const UnderlyingIterator it) : underlyingIterator(it) { }
IndexingIterator& operator++() {
++underlyingIterator;
++ix;
return *this;
}
bool operator != (const IndexingIterator& rhs) const {
return underlyingIterator != rhs.underlyingIterator;
}
ValueType operator*() const {
return ValueType{ix, *underlyingIterator};
}
long long int index() const { return ix; }
private:
UnderlyingIterator underlyingIterator;
long long int ix = 0;
};
template<class Container>
struct Enumeratable {
using Iterator = typename Container::const_iterator;
using Indexing = IndexingIterator<Iterator>;
Enumeratable(const Container& container) : b{container.begin()}, e{container.end()} {}
Indexing begin() const { return b; }
Indexing end() const { return e; }
private:
Indexing b;
Indexing e;
};
template<class Container>
auto enumerate(const Container& container) {
return Enumeratable<Container>{container};
}
void testDriver(const std::vector<int>& vec) {
for (const auto& [index, value]: enumerate(vec)) {
std::cout << index << ": " << value << "n";
}
}
int main() {
std::vector<int> vec{10, 34, 122, 12};
testDriver(vec);
return 0;
}
if (!myVec.empty())
{
auto iter = myVec.begin(), end = myVec.end();
size_t count = 0;
do {
std::cout << *iter++;
if (iter == end) break;
std::cout << ", ";
if ((++count % 5) == 0)
std::cout << 'n';
}
while (true);
}
或者,您可以编写一个模仿std::ostream_iterator
(或更好的infix_iterator
)的自定义迭代器,在需要时在输出中注入额外的换行符,例如:
#include <ostream>
#include <iterator>
template <class T,
class charT = char,
class traits = std::char_traits<charT> >
class my_infix_ostream_iterator :
public std::iterator<std::output_iterator_tag, void, void, void, void>
{
std::basic_ostream<charT, traits> *os;
charT const* delimiter;
size_t counter, numPerLine;
public:
typedef charT char_type;
typedef traits traits_type;
typedef std::basic_ostream<charT,traits> ostream_type;
my_infix_ostream_iterator(ostream_type& s, size_t numPerLine = 0, charT const *d = 0)
: os(&s), delimiter(d), numPerLine(numPerLine), counter(0)
{}
my_infix_ostream_iterator& operator=(T const &item)
{
if (counter > 0)
{
if (delimiter)
*os << delimiter;
if ((numPerLine > 0) && ((counter % numPerLine) == 0))
*os << os->widen('n');
}
*os << item;
++counter;
return *this;
}
my_infix_ostream_iterator& operator*() {
return *this;
}
my_infix_ostream_iterator& operator++() {
return *this;
}
my_infix_ostream_iterator& operator++(int) {
return *this;
}
};
然后你可以像这样使用它:
my_infix_ostream_iterator<myClass> iter(std::cout, 5, ", ");
for (const auto &elem : myVec) {
*iter++ = elem;
}
或:
std::copy(myVec.begin(), myVec.end(),
my_infix_ostream_iterator<myClass>(std::cout, 5, ", ")
);
现场演示
- 找不到成员对象:没有名为get_event()的成员,也处理多态性和向量
- 迭代时从向量和内存中删除对象
- 如何为模板化对象创建模板向量?VS正在投掷C3203
- 为什么我不能将一个对象push_back到属于另一个类的对象向量中?
- 如何创建从同一类继承的不同对象的向量
- 在nlohmann json中,如何将嵌套对象的数组转换为嵌套结构的向量
- 从多个源构造一个对象,包括一个对象向量
- 在C 中,是否有可能在不兼容类型的std ::向量对象之间传输不同类型的缓冲区
- 将向量对象存储在共享指针投掷错误中
- E0312,C2664尝试将向量对象作为函数参数传递时错误
- 相对于向量对象的两个成员,找到两个向量的相交的有效方法
- 将数据从文件读取到向量对象
- C 向量对象访问
- 向量对象声明后的括号是什么意思
- 2向量对象指向相同的分配内存
- 如何返回向量对象
- 传递单个或向量对象时没有重载函数的实例
- 指向数组和向量对象的指针
- 向量对象库,可以存储其他对象类型的对象
- 如何从另一个向量/对象集构造新的向量/指针集