C++标准模板库编程实战 第二章

目录

2.1序列容器

2.2 array数组,n>

2.2.1 访问元素

2.2.2 数组容器迭代器 

 2.2.3 比较数组容器

2.3 vector向量

2.3.2 vector 的容量capacity()和大小size()

 2.3.3 访问元素

2.3.5 添加元素

2.3.6删除元素

 2.3.7 vector容器

2.4 deque双向队列

2.4.3 添加和移除元素

2.4.4 assign()替换deque中的元素

2.5 list链表:

2.5.3 添加和移除元素

2.6 forward_list正向链表


2.1序列容器

序列容器以线性序列的方式存储,不对元素进行排序。

2.2 array<T,N>数组

长度固定,不能增加或删除元素。N必须是常量;

value.fill(3.15):将所有元素设为3.15;

2.2.1 访问元素

1.应当总是使用at()进行边界检查。

        values.at(4) = values.at(3) + 2.0 * values.at(1);

每次都进行边界检查会产生很多开销;

2.size()  empty();

3. 基于范围的循环:

double total {};
for(auto && value : values)//计算容器所有元素的和
    total += value;

 4.get<n>():获取容器第n个元素.提供了一种不需要再运行时检查,但能用安全的索引值访问元素的方式。N必须是一个在编译时可以确定的常量表达式。

std::array<std::string,5> words {"one", "two", "three", "four"}
std::cout<<std::get<3>(words)<<std::endl; //output words[3]
2.2.2 数组容器迭代器 

 5.算法generate()函数模板,提供了用函数对象计算值、初始化一段元素的可能:

unsigned int height {};
std::generate(std::begin(height_ins),std::end(height_ins),
                [height, &min_he, &ht_step] ()mutable 
                {return height += height==0 ? min_he : he_step;});

6.cbegin() cend() rbegin() rend() crbegin() crend() 当只想访问元素时,应使用const迭代器。

 2.2.3 比较数组容器

7.不同于标准数组,可以将一个数组容器赋给令一个数组容器(元素数量种类相同);

2.3 vector<T>向量

大小可以自动增长。在扩展容量,以及删除添加元素时会产生一些开销。

std::vector<long> numbers(20,99L);

第一个参数不需要是一个常量表达式。

移动迭代器初始化:std::make_move_iterator(std::begin(words));

std::vector<std::string> words_copy {  std::make_move_iterator(std::begin(words)), std::make_move_iterator(std::end(words))  }

2.3.2 vector 的容量capacity()和大小size()

当容器大小等于容量时,容器每次增加多少容量,取决于算法的实现。

通过reserve() 增加容器的容量,元素个数没有改变。

resize()可以改变容器的大小,可能导致容量的增加。减少容器的大小不会影响容器的容量。 

 2.3.3 访问元素

1.索引可能越界,应该使用at()

2.3.5 添加元素

向容器中添加元素的唯一方式是使用它的成员函数。

1.emplace_back()比push_back()更有效率。

2.插入元素 :emplace()

std::vector<std::string> words{ "first", "second" };
	auto iter = words.emplace(++std::begin(words), 5, 'A');
	words.emplace(++iter, "$$$$");
//first AAAAA $$$$ second

3.插入元素 : insert()

auto iter = words.insert(++std::begin(words), "two");
string more[]{ "five", "six", "seven" };
iter = words.insert(--std::end(words), std::begin(more), std::end(more));

在使用相同参数的情况下 insert不如emplace效率高,因为在inset()调用中,构造函数调用string(“two")生成了一个对象,作为传入的第二个参数。emplace()调用中,构造函数用第二个参数直接在容器中生成的字符串对象。

(4) 在插入点插入多个单个元素:

iter = words.insert(cend(words)-1, 2, "nine");

(5)在插入点插入初始化列表指定的元素:

iter = words.insert(end(words), {”twelve“ , ”thirteen “});

所有不在尾部插入都会有开销,如果插入后元素个数超过了容量,会有更多额外开销。

2.3.6删除元素

1.clear()删除所有元素。大小变为0,容量不变。

2.pop_back()

3.成员函数shrink_to_fit()去掉容器中多余的容量,执行完这个操作后,最好重新获取迭代器。

4.erase()删除一个或多个元素。

        auto iter = data.erase(begin(data)+1, begin(data)+3);//删除begin(data)+1和begin(data)+2.

返回的迭代器指向被删除元素后的位置,即std::begin(data)+1.

5.全局函数remove()

 erase-remove操作:执行删除操作后,iter指向最后一个元素之后的位置,即标识了被删除序列的第一个元素,被删除序列的结束位置由 end(words)指定。

auto iter = std::remove(begin(words), end(words), "none");
words.erase(iter, end(words));

可以合并为一句:

words.erase(std::remove(begin(words), end(words), "none"), end(words));

创建容器时最好初始化大小(reserve()),选择容器初始大小时,最好稍微高估vector的初始大小。

也可以自己管理内存分配:

if(data.size() == data.capacity())
    data.reserve(data.size() + 10);
 2.3.7 vector<bool>容器

bitset<N>是更好的选择。不过bitset不是容器,没有迭代器,但是提供了一些vector<bool>没有的操作。

2.4 deque<T>双向队列

相较于vector,能在头部和尾部高效的添加和删除对象。当应用包含先入先出的事务处理时,应优先使用。数据库事务,模拟超市的结账队列。因为deque内部组织元素的方式不同,deque的操作比vector慢。

可以使用at()进行边界检查。

2.4.3 添加和移除元素

1.push_back() pop_back() push_front() pop_front()

2.emplace_back() emplace_front()

3.emplace() insert()

4.erase() clear()

2.4.4 assign()替换deque中的元素
deque<string> words{ "one", "two", "three", "four"};
words.assign({ "seven","eight", "nine" });// seven eight nine
words.assign(begin(words)+1,--end(words));//eight
words.assign(8, "eight");//eight eight eight eight eight eight eight eight

2.5 list<T>链表:

访问任意元素的速度比前三个慢,因为必须从第一个或最后一个元素开始并沿着链表移动。

 可以在常规时间内,在序列已知的任意位置插入或删除元素,这是vector和deque都不具备的优势。缺点是,无法通过位置直接访问序列中的元素,只能从开始或者结尾处遍历。

2.5.3 添加和移除元素

1.push_back() pop_back() push_front() pop_front()

2.emplace_back() emplace_front() 更好,这些成员函数的参数是被生成元素的构造函数的参数,消除了push_back()和push_front()必须执行的右值移动运算。

3.list元素的迭代器只有在它指向的元素被删除时才会失效。 

auto = begin(data);
std::advance(iter, 9);
data.insert(iter, 3, 88);

4.clear() erase() remove()

 成员函数remove_if()接受一个一元断言为参数。一元断言接受一个和元素同类型的参数或引用,返回一个布尔值。

number.remove_if([] (int n) {return n%2 == 0;});
//这里的参数是一个lambda表达式,也可以是一个函数对象。

5. unique()

2.5.4 排序和合并元素

algorithm中的sort()算法要求随机访问迭代器,不能用于list容器。

list提供了自己的sort()函数。

1.无参sort

2.sort()接受一个函数对象或lambda表达式作为参数。

names.sort(std::greater<std::string>());//greater<T>在 function头文件中
names.sort(great<>());//简洁版greater<T>断言,通过使用完美转发提升效率。

3. 合并 merge() 两个容器中的元素必须是升序,合并后也是升序。

std::list<int> my_values {2, 4, 6, 14};
std::list<int> your_values{ -2, 1, 7, 10};
my_values.merge(your_values); // my_values contains: -2 1 2 4 6 7 10 14
your_values.empty(); // Returns true

第二个版本的merge()函数可以提供一个比较函数作为第二个参数。

std::list<std::string> my_words {"three", "six", "eight"};
std::list<std::string> your_words {"seven", "four", "nine"};
auto comp_str = [](const std::string& s1, const std::string& s2){ return s1[0]<s2[0]; };
my_words.sort(comp_str); // "eight" "six" "three"
your_words.sort(comp_str); // "four" "nine" "seven"
my_words.merge(your_words, comp_str); // "eight" "four" "nine" "six" "seven" "three"

 4.剪切splice()

三个版本:

接受三个参数:

std::list<std::string> my_words {"three", "six", "eight"};
std::list<std::string> your_words {"seven", "four", "nine"};
my_words.splice(++std::begin(my_words), your_words, ++std::begin(your_words))
//your_words: "seven," "nine"
//my_words: "three," "four," "six," "eight"

 接受四个参数:

your_words.splice(++std::begin(your_words), my_words, ++std::begin(my_words), 
 std::end(my_words));
//your_words: "seven", "four", "six", "eight", "nine"
//my_words: "three

接受两个参数(剪切所有元素):

my_words.splice(std::begin(my_words), your_words)
 2.5.5 访问元素

1.front() back() 返回引用,可能出错

2.begin() end()

可以对list使用基于范围的循环,当想要处理所有元素时,可以不用迭代器:

std::list<std::string> names {"Jane", "Jim", "Jules", "Janet"};
names.emplace_back("Ann");
std::string name("Alan");
names.emplace_back(std::move(name));
names.emplace_front("Hugo");
names.emplace(++begin(names), "Hannah");
for(const auto& name : names)
 std::cout << name << std::endl;

2.6 forward_list<T>正向链表

和list的区别:不能反向遍历只能从头到尾的遍历。操作比list容器快,占用内存更少。

没有back(),只有front()

没有size(),可以通过iterator头文件中的distance()函数得到元素的个数。

std::forward_list<std::string> my_words {"three", "six", "eight"};
auto count = std::distance(std::begin(my_words), std::end(my_words)); // Result is 3

 1.splice_after() 和 insert_after()

和cbefore_begin()和before_begin()配合使用可以在开始位置插入或黏贴元素。

也是三个版本:接受三个参数:

std::forward_list<std::string> my_words {"three", "six", "eight"};
std::forward_list<std::string> your_words {"seven", "four", "nine"};
my_words.splice_after(my_words.before_begin(), your_words, ++std::begin(your_words));
//mywords: nine three six eight

 接受四个参数:

my_words.splice_after(my_words.before_begin(), your_words,
 ++std::begin(your_words), std::end(your_words));
//mywords: "four", "nine", "three", "six", "eight"

接受两个参数:

my_words.splice_after(my_words.before_begin(), your_words);

 以下例子来源于cplusplus.com/reference/forward_list/forward_list/splice_after/

// forward_list::splice_after
#include <iostream>
#include <forward_list>

int main ()
{
  std::forward_list<int> first = { 1, 2, 3 };
  std::forward_list<int> second = { 10, 20, 30 };

  auto it = first.begin();  // points to the 1

  first.splice_after ( first.before_begin(), second );
                          // first: 10 20 30 1 2 3
                          // second: (empty)
                          // "it" still points to the 1 (now first's 4th element)

  second.splice_after ( second.before_begin(), first, first.begin(), it);
                          // first: 10 1 2 3
                          // second: 20 30

  first.splice_after ( first.before_begin(), second, second.begin() );
                          // first: 30 10 1 2 3
                          // second: 20
                          // * notice that what is moved is AFTER the iterator

  std::cout << "first contains:";
  for (int& x: first) std::cout << " " << x;
  std::cout << std::endl;

  std::cout << "second contains:";
  for (int& x: second) std::cout << " " << x;
  std::cout << std::endl;

  return 0;
}

2.sort() merge() remove() remove_if() unique() 和list相同。

2.7 自定义迭代器

 头文件iterator中iterator_traits模板:

template<class Iterator>
struct iterator_traits
{
typedef typename Iterator::difference_type difference_type;
typedef typename Iterator::value_type value_type;
typedef typename Iterator::pointer pointer;
typedef typename Iterator::reference reference;
typedef typename Iterator::iterator_category iterator_category;
};

满足STL要求的迭代器必须全部定义一下别名:

• difference_type—由两个MyIterator类型的迭代器之差得到的值的类型
• value_type——MyIterator类型的迭代器所指向的值的类型。
• pointer——MyIterator类型的迭代器所代表的指针类型。
• reference——从*MyIterator得到的引用类型。
• iterator_category——迭代器类别标签类类型之一:input_iterator_tag、output_iterator_tag、
Forward_iterator_tag、bidirectional_iterator_tag或random_access_iterator_tag。

//std::iterator_traits<MyIterator>::pointer
//std::iterator_traits<MyIterator>::value_type
template <typename Iter>
void my_swap(Iter a, Iter b)
{
 typename std::iterator_traits<Iter>::value_type tmp = *a;
 *a = *b;
 *b = tmp;
}

 课后练习

pe2_1:

#include<iostream>
#include<array>
#include<iterator>
using namespace std;
auto op = [](int x, int y) {return x + y; };
template<typename T, size_t N>
void show(const array<T,N>& Fib);
int main() {
		const array<long int, 50> FF = [&] {
		array<long int, 50> xx{0,1};
		auto iter = begin(xx);
		auto iter1 = iter + 1;
		auto iter2 = iter + 2;
		for (; iter2 < end(xx); iter++, iter1++,iter2++)  {
			*iter2 = *iter + *iter1;
		}
		return xx;
	}();
	//cout << aa << bb << endl;
	/*for (int& value : Fib) {
		value = op(a, b);
		a++;
		b++;
	}*/
	show(FF);
	return 0;
}
template<typename T, size_t N>
void show(const array<T, N>& Fib){
	for (size_t i = 0; i < Fib.size(); i++) {
		cout << Fib[i]<<" ";
		if ((i+1) % 8 == 0) cout << endl;
	}
	
}

pe2_2:

#include<iostream>
#include<vector>
#include<string>
#include<algorithm>
#include< iomanip >
using namespace std;
int main() {
	//vector<string> city(10);
	vector<string> city;

	cout << "Enter cities's name separated by spaces.Enter ctrl+Z on a new line to end:\n";
	copy(istream_iterator<string>{std::cin}, istream_iterator<string>{}, back_inserter(city));
	copy(begin(city), end(city), ostream_iterator<string>(cout, " "));
	cout << endl;
	sort(begin(city),end(city));
	copy(begin(city), end(city), ostream_iterator<string>(cout, " "));

	size_t maxlen = 0;
	for (auto value : city) {
		if (value.size() > maxlen) maxlen = value.size();
	}

	/*for (auto iter = begin(city); iter < end(city); iter++) {
		auto iter2 = iter + 1;
		char first = (*iter)[0];
		char second = (*iter2)[0];
		cout <<setw(maxlen)<< *iter << " ";
		if (first != second) cout << endl;
	}*/
	auto initialchar = ' ';
	for (auto value : city) {
		char first = value[0];
		if (initialchar != first) {
			cout << endl;
			initialchar = first;
		}
		cout << setw(maxlen) << value << " ";
	}
	return 0;
}

pe2_3(我这个方法好笨,但是我想不到聪明的0.0):

#include<iostream>
#include<vector>
#include<string>
#include<algorithm>
#include< iomanip >
#include<list>
using namespace std;
int main() {
	//vector<string> city(10);
	list<string> city;
	string cityname = "";
	string result = "";
	int tag = 1;
	cout << "Enter cities's name separated by $ .And use ! to end:\n";
	//copy(istream_iterator<string>{cin}, istream_iterator<string>{}, back_inserter(city));
	auto in = istream_iterator<string>{ cin};
	while (true)
	{
		string cityname = *in;
		if (cityname == "$") { 
			city.push_back(result);
			result = "";
			++in;
			tag = 1;
			continue;
		}
		if (cityname == "!") { 
			city.push_back(result);
			break; 
		}
		if (tag == 1) {
			result = result + cityname;
			tag++;
		}
		else if (tag != 1) {
			result = result +" " + cityname;
			tag++;
		}
		//city.push_back(result);
		++in;
	}
	copy(begin(city), end(city), ostream_iterator<string>(cout, " "));
	cout << endl;
	city.sort();
	copy(begin(city), end(city), ostream_iterator<string>(cout, " "));

	size_t maxlen = 0;
	for (auto value : city) {
		if (value.size() > maxlen) maxlen = value.size();
	}

	auto initialchar = ' ';
	for (auto value : city) {
		char first = value[0];
		if (initialchar != first) {
			cout << endl;
			initialchar = first;
		}
		cout << setw(maxlen) << value << " ";
	}
	return 0;
}

输出结果:

Enter cities's name separated by $ .And use ! to end:
wuhan $ xew york $ beijing $ xinjiang $ los angelos $ xinxiang $ jinan $ afuahn !
wuhan xew york beijing xinjiang los angelos xinxiang jinan afuahn
afuahn beijing jinan los angelos wuhan xew york xinjiang xinxiang
     afuahn
    beijing
      jinan
los angelos
      wuhan
   xew york    xinjiang    xinxiang

pe2_4:

#include<iostream>
#include<vector>
#include<string>
#include<algorithm>
#include< iomanip >
#include<list>
#include<deque>
using namespace std;
int main() {
	//vector<string> city(10);
	list<string> city;
	string cityname = "";
	string result = "";
	int tag = 1;
	cout << "Enter cities's name separated by $ .And use ! to end:\n";
	//copy(istream_iterator<string>{cin}, istream_iterator<string>{}, back_inserter(city));
	auto in = istream_iterator<string>{ cin };
	while (true)
	{
		string cityname = *in;
		if (cityname == "$") {
			city.push_back(result);
			result = "";
			++in;
			tag = 1;
			continue;
		}
		if (cityname == "!") {
			city.push_back(result);
			break;
		}
		if (tag == 1) {
			result = result + cityname;
			tag++;
		}
		else if (tag != 1) {
			result = result + " " + cityname;
			tag++;
		}
		//city.push_back(result);
		++in;
	}
	deque<string> CITY;

	copy(begin(city), end(city), front_inserter(CITY));
	copy(begin(CITY), end(CITY), ostream_iterator<string>(cout, " "));
	cout << endl;
	//city.sort();
	sort(begin(CITY),end(CITY));
	copy(begin(CITY), end(CITY), ostream_iterator<string>(cout, " "));

	size_t maxlen = 0;
	for (auto value : CITY) {
		if (value.size() > maxlen) maxlen = value.size();
	}

	auto initialchar = ' ';
	for (auto value : CITY) {
		char first = value[0];
		if (initialchar != first) {
			cout << endl;
			initialchar = first;
		}
		cout << setw(maxlen) << value << " ";
	}
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值