(a) 读取固定数量的单词,将它们按字典序插入到容器中。我们将在下一章中看到,关联容器更适合这个问题。
(b) 读取未知数量的单词,总是将单词插入到末尾。删除操作在头部进行。
© 从一个文件读取未知数量的整数。将这些数排序,然后将它们打印到标准输出。
(a)list。由于要求按字典顺序插入到容器中,因此需要进行中间插入,使用list较为合适。
(b)deque。由于需要在头部和末尾进行插入和删除操作,而deque是最适合的。
(c)vector。需要对读入容器中的数量进行排序,而vector支持随机访问,更适合用于此任务。
list
begin和end必须均指向同一个容器中的元素或尾元素之后的位置。且end不在begin之前。
bool func(vector::iterator first, vector::iterator secornd, int val){
if (first < second){
vector::iterator begin = first, end = second;
}else{
vector::iterator begin = second, end = first;
}
while(begin <= end){ //begin和end可能指向同一个目标
if (*begin == val)
return true;
++begin;
}
return false; //如果循环结束依然没有返回值,说明迭代器范围内没有给定的值
}
int func(vector::iterator first, vector::iterator secornd, int val){
if (first < second){
vector::iterator begin = first, end = second;
}else{
vector::iterator begin = second, end = first;
}
while(begin <= end){ //begin和end可能指向同一个目标
if (*begin == val)
return *begin;
++begin;
}
return -1; //如果循环结束依然没有返回值,说明迭代器范围内没有给定的值。用-1代表失败。
}
list lst1;
list::iterator iter1 = lst1.begin(),iter2 = lst1.end();
while (iter1 < iter2) /* ... */
list不是随机访问容器,因此迭代器没有重载小于运算符,故不能使用iter1 < iter2
的形式。
改为iter1 != iter2
。
vector
list
list
当使用auto类型时,cbegin必定返回常量类型,而begin则依据容器类型决定返回常量类型或非常量类型。
vector v1;
const vector v2;
auto it1 = v1.begin(), it2 = v2.begin();
auto it3 = v1.cbegin(), it4 = v2.cbegin();
it1 是vector::iterator。
it2 是vector::const_iterator。
//但由于auto仅能作为一种类型使用,而it1和it2不是同一种类型,因此将发生编译错误。!!!
it3 是vector::const_iterator。
it4 是vector::const_iterator。
vector v1; //默认初始化,不包含任何内容
vector v2 = {1,2,3}; //初始化为列表中的拷贝。包含1,2,3这3个int值。
vector v3 = v2; //初始化为另一个vector对象的拷贝。包含1,2,3这3个int值。
vector v4(v3.begin(), v3.end()); //初始化为两个迭代器指定范围内的元素的拷贝。同上。
vector v5(3); //包含3个元素,进行了值初始化。3个值为0的int值。
vector v6(3, 3); //包含3个值为3的元素。
接受容器创建其拷贝的构造函数,其元素类型和容器类型均必须相同。
接受两个迭代器创建拷贝的构造函数,其容器不一定相同,同时其元素类型也可以不相同,只要能够转换成所需的元素类型即可。
list lst(2,2);
vector vi(2,2);
vector v1(lst.begin(), lst.end());
vector v2(vi.begin(), vi.end());
int main() {
char ca1[] = "Hello, World.";
list lst(1, ca1);
vector v;
v.assign(lst.begin(), lst.end());
cout << *v.begin() << endl;
return 0;
}
bool compare(const vector& v1, const vector& v2){
bool result = true;
if (v1.size() == v2.size()){
vector::const_iterator begin1 = v1.begin(), begin2 = v2.begin();
while(begin1 != v1.end()){
if (*begin1 != *begin2){
result = false;
break;
}
++begin1, ++begin2;
}
}
return result;
}
bool compare(const list& lst1, const vector& v2) {
bool result = true;
if (lst1.size() == v2.size()) {
list::const_iterator begin1 = lst1.begin();
vector::const_iterator begin2 = v2.begin();
while (begin1 != lst1.end()) {
if (*begin1 != *begin2) {
result = false;
break;
}
++begin1, ++begin2;
}
}
return result;
}
if (c1 < c2)
首先,c1,c2不可以是无序关联容器。其次,c1,c2必须是保存相同类型的元素的相同类型的容器。
int main(){
string str;
deque deq;
while(cin >> str)
deq.push_back(str);
deque::const_iterator iter = deq.begin();
while (iter != deq.end()){
cout << *iter << endl;
++iter;
}
return 0;
}
int main(){
string str;
list deq;
while(cin >> str)
deq.push_back(str);
list::const_iterator iter = deq.begin();
while (iter != deq.end()){
cout << *iter << endl;
++iter;
}
return 0;
}
不做任何改变,用list替代deque即可。
int main(){
list lst = {1,2,3,4,5,6,7,8,9,10};
deque odd, even;
list::iterator iter = lst.begin();
while (iter != lst.end()){
if (*iter % 2 == 1)
odd.push_back(*iter);
else
even.push_back(*iter);
++iter;
}
return 0;
}
循环依然等价于调用push_front,将标准输入的字符依次插入vector的头部,但将非常耗时。
vector::iterator iter = iv.begin(), mid = iv.begin() + iv.size() / 2;
while (iter != mid)
if (*iter == some_val)
iv.insert(iter, 2 * some_val);
1.迭代器在循环中没有自增,将始终原地踏步。
2.当iv成功插入新元素后,iter,mid都将失效。
修改如下。
vector::iterator iter = iv.begin();
auto half = iv.size() >> 1;
while ((iv.end() - iter) > half ) { //利用距离替代mid来判断
if (*iter == some_val) {
iter = iv.insert(iter, 2 * some_val);
++iter; //如果插入了新元素,多向后迭代一步
}
++iter;
}
均为c中的第一个元素。
int main(){
vector v;
int val1 = v.at(0); // terminating with uncaught exception of type std::out_of_range
int val2 = v[0]; // Segmentation fault: 11
int val3 = v.front(); // Segmentation fault: 11
int val4 = *v.begin(); // Segmentation fault: 11
return 0;
}
如果elem1与elem2相等,则删除区间为0,故将不删除元素。
如果elem2是尾后迭代器,则删除elem1及之后的全部元素。
若均为尾后迭代器,则不删除元素。
使用下面代码定义的ia,将ia拷贝到一个vector和一个list中。使用单迭代器版本的erase从list中删除奇数元素,从vector中删除偶数元素。
int ia[] = { 0, 1, 1, 2, 3, 5, 8, 13, 21, 55, 89 };
int main() {
int ia[] = { 0, 1, 1, 2, 3, 5, 8, 13, 21, 55, 89 };
vector v;
list lst;
for (int i : ia) {
v.push_back(i);
lst.push_back(i);
}
vector::iterator iterV = v.begin();
while (iterV != v.end()) {
if (*iterV % 2 != 1)
iterV = v.erase(iterV);
else
++iterV;
}
list::iterator iterL = lst.begin();
while (iterL != lst.end()) {
if (*iterL % 2)
iterL = lst.erase(iterL);
else
++iterL;
}
}
int main(){
forward_list flst = {0,1,2,3,4,5,6,7,8,9};
auto prev = flst.before_begin();
auto curr = flst.begin();
while (curr != flst.end()){
if (*curr % 2)
curr = flst.erase_after(prev); //通过前驱删除当前元素
else{
prev = curr;
++curr;
}
}
for (auto i : flst)
cout << i;
}
void func(forward_list &flst, string str1, string str2) {
auto prev = flst.before_begin();
auto curr = flst.begin();
while (curr != flst.end()) {
if (*curr == str1) {
curr = flst.insert_after(curr, str2);
return;
}
else {
prev = curr;
++curr;
}
}
flst.insert_after(prev, str2);
}
会默认初始化75个元素添加到vec的末尾。
会将vec中的后90个元素删除。
元素类型必须提供一个默认构造函数。
list和forward_list不支持随机访问,因此其迭代器也不支持+=2的加减运算。其原因在于链表类型容器的元素存储是不连续的,无法通过增加固定的数字来移动迭代器。
//forward_list
int main() {
forward_list lst = { 0,1,2,3,4,5,6,7,8,9 };
auto curr = lst.begin();
auto prev = lst.before_begin();
while (curr != lst.end()) {
if (*curr % 2) {
lst.insert_after(prev, *curr);
prev = curr;
++curr;
}
else
curr = lst.erase_after(prev);
}
for (auto i : lst)
cout << i << endl;
}
//list
int main() {
list lst = { 0,1,2,3,4,5,6,7,8,9 };
auto curr = lst.begin();
while (curr != lst.end()) {
if (*curr % 2) {
curr = lst.insert(curr, *curr);
++curr;
++curr;
}
else
curr = lst.erase(curr);
}
for (auto i : lst)
cout << i << endl;
}
在第316页的程序中,像下面语句这样调用insert是否合法?如果不合法,为什么?
iter = vi.insert(iter, *iter++);
不合法。虽然上面语句可以正常运行,但是由于在C++中并没有指定函数形参的入栈顺序,因此可能是先输入iter这个形参,或是先输入*iter++
这个形参。对于这两种情况而言,得到的结果将会不同。
因此是不合法的。
如果容器是vector或string以及deque,迭代器将会失效,程序崩溃。如果是list将正常运行。
iter = vi.begin();
while (iter != vi.end())
if (*iter % 2)
iter = vi.insert(iter, *iter);
++iter;
遇到奇数时复制该奇数再次插入,并移动至下个元素。
然而iter将被重新赋值为iter之前的元素,因此移动至原先的下个元素需要自增iter两次。故将死循环。
size是指vector已经保存的元素的数目,capacity是vector在不分配新的内存空间的前提下它最多可以保存多少元素。
不可能
list的元素不是以连续的地址存储的,因此不需要担心当前内存地址不够用的问题,只需找到新的可用空间即可。
array是固定长度的数组,其capacity在初始化之初就固定了,因此其成员函数无意义。
int main() {
vector v = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
v.reserve(50);
cout << "v: size: " << v.size()
<< " capacity: " << v.capacity() << endl;
while (v.size() != v.capacity())
v.push_back(0);
cout << "v: size: " << v.size()
<< " capacity: " << v.capacity() << endl;
v.push_back(42);
cout << "v: size: " << v.size()
<< " capacity: " << v.capacity() << endl; //增加1/2原有长度
return 0;
}
解释下面程序片段做了什么:
vector svec;
svec.reserve(1024); //为该vevtor申请1024个元素空间
string word;
while (cin >> word)
svec.push_back(word);
svec.resize(svec.size() + svec.size() / 2); //根据vector内元素数目将其扩大0.5倍
256:1024,512:1024 resize后数量不超过预留空间,因此capacity不改变。
1000:因为resize会添加元素进入容器,因此vector将根据具体实现提升capacity,且不低于1500。
1048:因为resize会添加元素进入容器,因此vector将根据具体实现提升capacity,且不低于1524。
vector vc = {'H', 'E', 'L', 'L', 'O'};
string str(vc.begin(), vc.end());
预先将该string执行成员函数reserve(100)。这样就能极大减少初期的扩容操作。
void func(string &s, const string &oldVal, const string &newVal) {
string::iterator iter = s.begin();
auto len = oldVal.size();
//因为(iter, iter + len)是个左闭右开区间,因此判断条件为iter + len - 1 != s.end()
while (s.end() - len + 1 != iter) {
if (oldVal == string(iter, iter + len)) {
iter = s.erase(iter, iter + len);
iter = s.insert(iter, newVal.begin(), newVal.end());
iter += len;
}
else
++iter;
}
}
void func(string &s, const string &oldVal, const string &newVal) {
string::size_type pos = 0;
auto len = oldVal.size();
while (pos + len - 1 != s.size()) { //如果插入值后,s.size()会变,因此不能把它保存在对象里
if (oldVal == s.substr(pos, len)) {
s.replace(pos, len, newVal);
pos += len;
}
else
++pos;
}
}
string func(const string & str,const string & prefix,const string & suffix){
string result(str);
auto beg = result.begin();
result.insert(beg, prefix.begin(), prefix.end());
result.append(suffix);
return result;
}
string func(const string & str, const string & prefix, const string & suffix) {
string result(str);
result.insert(0, prefix);
result.insert(result.size(), suffix);
return result;
}
int main() {
string str("ab2c3d7R4E6");
string letter("abcdRE");
string number("23467");
string::size_type pos = 0;
while ((pos = str.find_first_of(number, pos)) != string::npos) {
cout << "found number at index: " << pos << ", element is " << str[pos] << endl;
++pos;
}
pos = 0;
while ((pos = str.find_first_of(letter, pos)) != string::npos) {
cout << "found letter at index:" << pos << ", element is " << str[pos] << endl;
++pos;
}
pos = 0;
while ((pos = str.find_first_not_of(letter, pos)) != string::npos) {
cout << "found number at index:" << pos << ", element is " << str[pos] << endl;
++pos;
}
pos = 0;
while ((pos = str.find_first_not_of(number, pos)) != string::npos) {
cout << "found letter at index:" << pos << ", element is " << str[pos] << endl;
++pos;
}
}
string::npos
int main(int argc, char const * argv[]){
string noneed("bdfhkltgjpqy");
string word, result;
fstream& in(argv[1]);
while(in >> word){
if (word.find_first_in(noneed) == string::npos && word.size() > result.size())
result = word;
}
cout << result;
}
int main() {
vector v = { "1", "2", "3", "4", "5" };
int resultI = 0;
for (const string& str : v)
resultI += stoi(str);
cout << resultI << endl;
float resultF = 0.0;
for (const string& str : v)
resultF += stof(str);
cout << resultF << endl;
}
#include
#include
#include
using namespace std;
class Date
{
friend void print(Date& item);
private:
unsigned year;
unsigned month;
unsigned date;
public:
Date(const string& s);
unsigned change_to_digit(const string& s);
};
unsigned Date::change_to_digit(const string& s)
{
string numbers{ "0123456789" };
if (s.find_first_of(numbers) != string::npos)
return stoi(s);
vector v{ "Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec" };
decltype(v.size()) loc = 0;
for (decltype(v.size()) i = 0; i != v.size(); ++i)
if (s.find(v[i]) != string::npos)
{
loc = i + 1; break;
}
return loc;
}
Date::Date(const string& s)
{
string punct{ " /," };
auto pos = s.find_first_of(punct);
month = change_to_digit(s.substr(0, pos));
++pos;
auto pos2 = s.find_first_of(punct, pos);
date = stoi(s.substr(pos, pos2 - pos));
++pos2;
auto pos3 = s.find_first_of(punct, pos2);
year = stoi(s.substr(pos2, pos3 - pos2));
}
void print(Date& item)
{
cout << item.year << " " << item.month << " " << item.date << endl;
}
int main()
{
Date date1("January 1,1990");
print(date1);
Date date2("1/1/1990");
print(date2);
Date date3("Jan 1 1990");
print(date3);
system("pause");
}