cppPrimer第九章

9.1 对于下面的程序任务,vector、deque和list哪种容器最为适合?解释你选择的理由。

(a)读取固定数量的单词,将它们按字典序插入到容器中

(b) 读取未知数量的单词,总是将新单词插入到末尾。删除在头部进行

© 从一个文件读取未知数量的整数。将这些数排序,然后将它们打印到标准输出

(a)适合用list,因为按字典序插入到容器中,很多情况是插入到原序列的中间位置,list在任何位置插入/删除的速度较快。

(b)适合于用deque(双端队列),因为一个标准的单端队列就是头删尾插,故执行该操作速度很快。

©适合用vector,因为读取的时候都往尾部插入即可,vector尾插效率是常数的时间复杂度,并且vector支持随机访问,进行排序也更为方便高效

9.3 构成迭代器范围的迭代器有何限制?

begin和end迭代器需要满足:

  1. 它们指向同一个容器中的元素,或者是容器最后一个元素之后的位置
  2. 我们可以通过反复递增begin来到end,换句话说,end不在begin之前。
9.4 为了索引int的vector中的元素,应该使用什么类型

vector::size_type

9.9 begin和cbegin两个函数有什么不同

begin根据调用该函数的容器对象是否为常数返回const_iterator或者iterator

cbegin返回const_iterator

9.10 下面四个对象分别是什么类型
vector<int> v1;
const vector<int> v2;
auto it1 = v1.begin(); //vector<int>::iterator
auto it2 = v2.begin(); // vector<int>::const_iterator
auto it3 = v1.cbegin(); //vector<int>::const_iterator
auto it4 = v2.cbegin(); //vector<int>::const_iterator

9.12 对于接受一个容器创建其拷贝的构造函数,和接受两个迭代器创建拷贝的构造函数,解释它们的不同。

接受一个容器创建其拷贝的拷贝构造函数,要求容器的类型及元素类型都要一直

接受两个迭代器创建拷贝范围的构造函数只需要元素的类型能够转换即可

9.17 假定c1 和c2是两个容器,下面比较操作有何限制
if(c1 < c2)

首先要满足c1和c2的容器类型相同,且存放的元素类型相同,其次存放的元素类型也有提供 < 运算符

9.20 编写程序,从一个list拷贝元素到两个deque中。值为偶数的所有元素都拷贝到一个deque中,而奇数值元素都拷贝到另一个deque中。
void p9_20()
{
	list<int> l{ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
	deque<int> dq1;
	deque<int> dq2;

	for (const int& n : l)
	{
		//巧用位运算
		(n & 0x1 ? dq1 : dq2).push_back(n);
	}

	for (const int& n : dq1)
		cout << n << " ";
	cout << endl;

	for (const int& n : dq2)
		cout << n << " ";
	cout << endl;
}
9.22 假定iv是一个int 的vector,下面的程序存在什么错误?你将如何修改?
vector<int>::iterator iter = iv.begin(), mid = iv.begin() + iv.size() / 2;
while (iter != mid)
    if (*iter == some_val)
        iv.insert(iter, 2 * some_val);

如果*iter != some_val,且iter又不会改变,则会陷入死循环;其次迭代器可能会失效,应该修改为

vector<int>::iterator iter = iv.begin(), mid = iv.begin() + iv.size() / 2;
while (iter != mid)
{
    if (*iter++ == some_val)
    {
        iter = iv.insert(iter, 2 * some_val);
    	++iter; // 让iter恢复到原来的位置上
    }
    ++iter; //iter向前移动一步,防止死循环
}
    
9.29 假定vec包含25个元素,那么vec.resize(100)会做什么?如果接下来调用vec.resize(10)会做什么

vec.resize(100)添加75个元素0,从末尾删除90个元素,最后剩下最开始前10个元素。

9.30 接受单个参数的resize版本对元素类型有什么限制(若有的话)

如果容器保存的是类类型元素,且resize向容器添加新元素,则我们必须提供初始值,或者元素类型必须提供一个默认构造函数

9.31 第316页中删除偶数值元素并复制奇数值元素的程序不能用于list或者forward_list.为什么?修改程序,使之也能用于这些类型。

对于list和forwardlist来说,不支持迭代器的+=,+运算符。对于forward_list不支持insert和erase,只支持insert_after和erase_after,修改为:

void p9_31()
{
	//删除偶数元素,复制每个奇数元素
	list<int> li = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
	//forward_list<int> li = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
	auto iter = li.begin();
	while (iter != li.end())
	{
		if (*iter % 2)
		{
			iter = li.insert(iter, *iter);//复制当前元素,插入到iter之前的位置,返回指向新插入的元素的位置的迭代器
			iter++;
			iter++;
		}
		else
		{
			iter = li.erase(iter);//删除偶数元素
		}
	}
	for (int n : li)
		cout << n << " ";
	cout << endl;
}
void p9_31_1()
{
	//删除偶数元素,复制每个奇数元素
	//list<int> li = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
	forward_list<int> li = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
	auto iter = li.begin();
	auto prev = li.before_begin();//首前迭代器
	while (iter != li.end())
	{
		if (*iter % 2)
		{
			li.insert_after(prev, *iter);//复制当前元素,插入到prev之后的位置,返回指向新插入的元素的位置的迭代器
			prev = iter;
			++iter;
		}
		else
		{
			iter = li.erase_after(prev);//删除偶数元素,返回被删除元素之后的迭代器
		}
	}
	for (int n : li)
		cout << n << " ";
	cout << endl;
}
9.32 像下面这样调用insert是否合法?如果不合法,为什么?
iter = vi.insert(iter, *iter++);

不合法,同一个表达式中不应该使用修改过的子表达式,因为求值顺序不定,导致表达式未定义

9.33 请问如下代码,如果不将insert的结果赋予begin,将会发生什么?
vector<int> vi{ 1, 2, 3 };
vector<int>::iterator begin = vi.begin();
while (begin != vi.end())
{
    ++begin;//向前移动begin,我们想在此元素之后插入元素
    begin = vi.insert(begin, 42); //begin迭代器失效
    ++begin;
}
for (int n : vi)
    cout << n << " ";
cout << endl;

插入元素后的迭代器失效即begin会失效,抛出异常

9.35 解释一个vector的capacity和size 有何区别?

capacity表示在不重新分配内存空间的情况下vector可存放元素的大小,size表示vector已经存放元素的个数

9.37 为什么list或array没有capacity成员函数?

因为list是实时分配的空间的,插入一个元素就分配一个空间,不会提前预留空间,没必要用一个capacity表示容量

array容器大小是固定的只用一个size()表示即可

9.38 探究在你的标准库实现中,vector是如何增长的。

在vs2019中提供的编译器,每次增长原来容量的一半,即当前capacity() = size()为50,再插入一个元素后capacity变为75

9.39 解释下面程序片段做了什么?
vector<string> svec;
svec.reserve(1024); // 分配至少能容纳1024个元素的空间
string word;
while (cin >> word)
    svec.push_back(word);
svec.resize(svec.size() + svec.size()/2); //改变容器的size大小,新加入的元素默认初始化为空串
9.40 若上一题程序读入了256个词,在resize之后容器的capacity可能是多少?如果读入了512个、1000个或1048个词呢?

读入256个词,capacity为1024

读入512个词,capacity为1024

读入1000个词,resize后size为1500,capacity为1536,

读入1048个词,resize后size为1572,capacity为2304

9.42 假定你希望每次读取一个字符存入一个string中,而且知道最少需要读取100个字符,应该如何提高程序性能

为string预留至少100个字符大小的空间如

string s;
s.reserve(128);
9.43 编写一个函数,接受三个参数s、oldVal和newVal。使用迭代器insert和erase函数将s中所有oldVal替换为newVal。测试你的程序。用它替换通用的简写形式,如将“tho”替换为“though”,将“thru”替换为“through”

方法一:

//判断以字符串s的ps位置开始是否有与字符串s2匹配
bool find(const string &s, string::const_iterator ps, const string& s2)
{
	for (string::const_iterator cit = s2.begin(); cit != s2.end(); ++ps, ++cit)
	{
		if (ps == s.cend() || *ps != *cit)
			return false;
	}
	return true;
}

void p9_43_1(string& s, const string& oldVal, const string& newVal)
{
	string::iterator it = s.begin();
	while (it != s.end())
	{
		if (find(s, it, oldVal))
		{
			//返回删除区间段最后一个位置的下一个位置的迭代器
			it = s.erase(it, it + oldVal.size());
			//返回插入区间第一个位置的迭代器
			it = s.insert(it, newVal.begin(), newVal.end());
			it += newVal.size();
		}
		else
			++it;
	}
}

void test02()
{
	string str = "dskthruid ththruid";
	p9_43_1(str, "thru", "through");
	cout << str << endl;
}

方法二:

void p9_43(string &s,const string &oldVal, const string &newVal)
{
	//替换 = 删除+插入
	string::iterator curr = s.begin();
	while (curr <= s.end() - oldVal.size())
	{
		//检测当前位置开始是否有满足条件的字符串
		if (oldVal == string(curr, curr + oldVal.size()))
		{
			//erase(pos,len)
			curr = s.erase(curr, curr + oldVal.size());
			//insert(pos, args)
			curr = s.insert(curr, newVal.begin(), newVal.end());
			curr += newVal.size();
		}
		else
			++curr;
	}
	cout << s << endl;
}
9.44 重写上一题函数这次使用一个下标和replace
void p9_44(string& s, const string& oldVal, const string& newVal)
{
	//判断的位置pos,最大到s.size()-oldVal.size();,再往后没有足够长的字符了
	for (size_t pos = 0; pos <= s.size() - oldVal.size(); )
	{
		//利用短路求值特性,如果第一个判断式不满足就不用在执行右边的判断了
		if (s[pos] == oldVal[0] && s.substr(pos, oldVal.size()) == oldVal)
		{
            //将s从pos位置开始的oldVal.size()个元素替换为newVal
			s.replace(pos, oldVal.size(), newVal);
			pos += newVal.size();
		}
		else
			++pos;
	}
}
9.49 如果一个字母延伸到中线之上如d或f,则称其有上出头部分,如果一个字母延伸到中线之下如p或g,则称其有下出头部分。编写程序,读入一个单词文件,输出最长的既不包含上出头部分,也不包含下出头部分的单词。

text49.txt

Description of the most important classes, functions and objects of the Standard Language Library, with descriptive fully-functional short programs as examples:
void find_no_head(const string& s, string &result)
{
	string head("dfpg");
	//若单词s中没有出头的字母
	if (s.find_first_of(head) == string::npos)
	{
		result = s.size() > result.size() ? s : result;
	}
}

void p9_49()
{
	ifstream ifs("text49.txt");
	string line;
	string longest , word;
    //对每个单词进行判断有无出头字母
    //并且对含无出头字母的单词比较长度
	while (ifs >> word)
	{
		find_no_head(word, longest);
	}
	//while (getline(ifs, line))
	//{
	//	istringstream ifs(line);//创建一个字符串输入流,用line初始化
	//	
	//	while (ifs >> word)
	//	{
	//		find_no_head(word, longest);
	//	}
	//}
	cout << longest << endl;
	
}
9.51 设计一个类,它有三个unsigned成员,分别表示年、月、日。为其编写构造函数,接受一个表示日期的string参数。你的构造函数应该能处理不同数据格式,如January 1, 1900, 1/1/1900,Jan 1 1900等
#include<iostream>
#include<string>
#include<vector>

using std::string;
using std::vector;
using std::cin;
using std::cout;
using std::endl;

class Date
{
public:
	Date(string date);
	void print()
	{
		cout << "day: " << day << " " << "month: " << month << " " << "year: " << year << endl;
	}
private:
	void convert1(const string& s);
	void convert2(const string& s);
	void convert3(const string& s);
	void convert_month(const string& s);
	unsigned year;
	unsigned month;
	unsigned day;
};

Date::Date(string date)
{
	if (date.find_first_of("/") != string::npos)
		convert1(date);
	else if (date.find_first_of(",") != string::npos)
		convert2(date);
	else if (date.find_first_of(" ") != string::npos)
		convert3(date);
	else
		year = 1900, month = 1, day = 1;
}

void Date::convert1(const string& s)
{
	month = stoi(s.substr(0, s.find_first_of("/")));
	day = stoi(s.substr(s.find_first_of("/") + 1, s.find_last_of("/") - s.find_first_of("/") - 1));
	year = stoi(s.substr(s.find_last_of("/") + 1, 4));
}

void Date::convert2(const string& s)
{
	convert_month(s);
	day = stoi(s.substr(s.find_first_of("123456789"), s.find_first_of(",") - s.find_first_of("123456789")));
	year = stoi(s.substr(s.find_last_of(" ") + 1, 4));
}

void Date::convert3(const string& s)
{
	convert_month(s);
	day = stoi(s.substr(s.find_first_of("123456789"), s.find_last_of(" ") - s.find_first_of("123456789")));
	year = stoi(s.substr(s.find_last_of(" ") + 1, 4));

}
void Date::convert_month(const string& s)
{
	if (s.find("Jan") != string::npos)	month = 1;
	else if (s.find("Feb") != string::npos)	month = 2;
	else if (s.find("Mar") != string::npos)	month = 3;
	else if (s.find("Apr") != string::npos)	month = 4;
	else if (s.find("May") != string::npos)	month = 5;
	else if (s.find("Jun") != string::npos)	month = 6;
	else if (s.find("Jul") != string::npos)	month = 7;
	else if (s.find("Aug") != string::npos)	month = 8;
	else if (s.find("Sept") != string::npos)	month = 9;
	else if (s.find("Oct") != string::npos)	month = 10;
	else if (s.find("Nov") != string::npos)	month = 11;
	else if (s.find("Dec") != string::npos)	month = 12;
}

测试代码

void test01()
{
	Date d1("March 13, 1998");
	Date d2("3/13/1998");
	Date d3("Mar 13 1998");
	d1.print();
	d2.print();
	d3.print();
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值