特别说明:作者写本书的时候C++11标准还没有发布,不同编译器厂商对C++标准的实现有些差异,导致现在去看书的部分内容和最新的C++标准以及大部分编译器的行为有些差异,请特别注意。
条款26:iterator优先于const_iterator, reverse_iterator, const_reverse_iterator
书中提到的例子,我进行了测试,除了连续存储的序列容器insert/erase成员函数的参数不能为reverse_iterator外,其他均可以,以及iterator与const_iterator之间的比较、算术运算等。
void iteratorTest1() {
vector<int> vec{0, 0, 1, 2, 3, 4};
// erase和insert成员函数中对迭代器类型的要求
vec.erase(vec.begin()); // right
vec.erase(vec.cbegin()); // right
// vec.erase(vec.rbegin()); // ERROR reverse iterator 不能作为erase的参数
vec.insert(vec.end(), 5); // right, vec{1,2,3,4,5}
vec.insert(vec.cend(), 6); // right, vec{1,2,3,4,5,6}
// vec.insert(vec.rbegin(), 5); // ERROR reverse iterator 不能作为insert的参数
// iterator 与 const_iterator 比较
auto iter = vec.begin();
auto citer = vec.cbegin();
if (iter == citer || citer == iter) {
// 为啥两个不同的迭代器类型可以比较呢,应该是iterator隐式转换为const_iterator了
// 实质上是两个const_iterator之间的比较
cout << "iterator can be compared with const_iterator" << endl;
}
std::advance(iter, 3); // 将iter向前累加3个位置
if (iter - citer >= 3 || citer + 3 <= iter) {
// iterator与const_iterator之间能进行算术运算
cout << "iterator can be operator with const_iterator" << endl;
}
return;
}
条款27:使用distance和advance将容器的const_iterator转换为iterator
现代C++大部分编译器都可直接转换,不需要条款中那么复杂的做法,但是书中的做法肯定是具有可移植性的
void iteratorTest2() {
// vector/string可以
vector<int> vec{1, 2, 3, 4, 5};
auto citer1 = vec.cbegin();
auto iter1 = citer1; // 可以转换
cout << *iter1 << endl;
std::advance(iter1, std::distance(iter1, citer1)); // 也OK,但是没必要这么复杂,上面的直接转换就可以
cout << *iter1 << endl;
// deque/list/set/map/unordered_set等
std::deque<int> dq{1, 2, 3, 4, 5};
auto citer2 = dq.cbegin();
auto iter2 = citer2; // 可以转换
cout << *iter2 << endl;
std::advance(iter2, std::distance(iter2, citer2 + 2)); // 也OK,但是没必要这么复杂,上面的直接转换就可以
cout << *iter2 << endl;
std::list<int> ls{1, 2, 3, 4, 5};
auto citer3 = ls.cbegin();
auto iter3 = citer3; // 可以转换
cout << *iter3 << endl;
std::advance(iter3, std::distance(iter3, citer3)); // 也OK,但是没必要这么复杂,上面的直接转换就可以
cout << *iter3 << endl;
std::map<int, int> mp{
{1, 2}, {2, 3}};
auto citer4 = mp.cbegin();
auto iter4 = citer4; // 可以转换
cout << iter4->first << ", " << iter4->second << endl;
std::advance(iter4, std::distance(iter4, citer4)); // 也OK,但是没必要这么复杂,上面的直接转换就可以
cout << iter4->first << ", " << iter4->second << endl;
std::unordered_set<int> ust{1,2,3,4,5};
auto citer5 = ust.cbegin();
auto iter5 = citer5;
cout << *iter5 << endl; // 可以转换
std::advance(iter5, std::distance(iter5, citer5)); // 也OK,但是没必要这么复杂,上面的直接转换就可以
cout << *iter5 << endl;
// 下面是条款中介绍的做法,这个做法肯定具有可移植性,但是稍显复杂
using ConstIter = std::unordered_set<int>::const_iterator;
std::advance(iter5, std::distance<ConstIter>(iter5, citer5));
return;
}
条款28:正确理解reverse_iterator的base()成员函数所产生的iterator的用法
void iteratorTest3() {
vector<int> vec{1, 2, 3, 4, 5};
auto riter1 = std::find(vec.rbegin(), vec.rend(), 3);
cout << "*riter1: " << *riter1 << endl; // 3
auto iter1 = riter1.base();
cout << "*iter1: " << *iter1 << endl; // 4
auto riter2 = vec.rbegin() + 2;
cout << "*riter2: " << *riter2 << endl; // 3
auto iter2 = riter2.base();
cout << "*iter2: " << *iter2 << endl; // 4
// 插入数据,行为是一致的
// vec.insert(riter2, 99); // 实际上不能用reverse_iterator作为参数,这儿假设能插入,则结果入下{1, 2, 3, 99, 4, 5}
iter2 = vec.insert(iter2, 99); // vec = {1, 2, 3, 99, 4, 5}
cout << *iter2 << endl;
// 如果是删除数据,则行为不一致,需要特别注意,这个时候两者不是指向同一个元素
auto riter3 = vec.rbegin() + 2;
cout << "*riter3: " << *riter3 << endl; // 99
auto iter3 = riter3.base();
cout << "*iter3: " << *iter3 << endl; // 4
// vec.erase(--riter3.base()); // 成功删除99, 但是某些编译器可能无法通过编译,因为C++规定从函数返回的指针不应该被修改
// vec.erase(--iter3); // 成功删除99
vec.erase((++riter3).base()); // 书中推荐的做法,成功删除99
return;
}
条款29:对于逐字符的输入请考虑使用istreambuf_iterator
istream_iterator是使用operator>>从输入流中读取单个字符,涉及许多格式化操作,默认情况下函数会跳过空白字符;
istreambuf_iterator则是直接从流的缓冲区中读取下一个字符,无格式化,不会跳过任何字符;
void iteratorTest4() {
// istream_iterator
// 从输入流中读取单个字符
// 默认使用operator>>,涉及格式化等操作,效率不高,而且会忽略空白字符
std::ifstream ifs1;
ifs1.open("EnData.txt");
std::string fileData1;
if (ifs1.is_open()) {
fileData1.append((std::istream_iterator<char>(ifs1)), std::istream_iterator<char>());
}
// istreambuf_iterator
// 适用于非格式化的逐个字符输入过程
// 直接从流的缓冲区中读取下一个字符,效率更高,且不会跳过任何字符
std::ifstream ifs2;
ifs2.open("EnData.txt");
std::string fileData2;
if (ifs2.is_open()) {
fileData2.append((std::istreambuf_iterator<char>(ifs2)), std::istreambuf_iterator<char>());
}
return;
}
调试截图
参考:《Effective STL中文版》