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;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: C++ 标准模板(STL)是一个强大的工具,为程序员提供了许多可以直接使用的容器、算法和迭代器。《C++ 标准模板编程实战》这本书集中讲解了 STL 的使用方法和实战技巧,旨在帮助读者开发出高效且易于维护的 C++ 程序。 这本书共分为四个部分。第一部分介绍了 STL 的基础知识,主要包括容器、迭代器、算法、函数对象等内容。第二部分重点讲解了序列容器和关联容器,以及它们常见的应用。第三部分主要是算法,详细介绍了 STL 中常用的算法,并且通过实例演示了其使用方法。第四部分主要是 STL 的高级应用,如智能指针、异常处理、多线程等。 此外,这本书还提供了大量的实战案例,这些案例既包括独立的小应用程序,也包括较为完整的项目代码。通过这些案例,读者可以深入了解 STL 的使用和设计思路,并掌握一些实用的编程技巧。 总的来说,这本《C++ 标准模板编程实战》是一本非常实用的书籍,不仅适合初学者入门,也适合有一定经验的开发者进一步提高自己的编程技能。建议读者在学习这本书时,可以边读边动手实践,更好地理解和掌握其中的内容。 ### 回答2: c++标准模板编程实战是一本非常经典、详实的c++ STL实战教材,主要讲解了STL的各种容器、算法和迭代器的常用操作和实现原理,并且通过大量的实例演示了STL在真实项目中的实际应用。 本书总共分为10个章节,前两章是介绍STL的基础知识和核心组件,包括迭代器、容器、算法等;第三章是介绍序列容器,主要包括vector、list、deque、stack、queue、heap、priority_queue和bitset等;第四章是介绍关联容器,主要包括set、multiset、map、multimap等;第五章是介绍迭代器,包括迭代器分类,迭代器实现方式和应用场景等;第六章是介绍函数对象,包括函数对象的定义、STL内置函数对象、自定义函数对象和函数对象适配器等;第七章是介绍算法基础,包括常用算法和自定义算法的实现;第八章是介绍字符串,在字符串操作方面,STL提供了string和wstring类,以及一些与之相关的算法;第九章是介绍STL的高级用法,包括元编程、策略模式、继承体系、嵌套类和allocator等;第十章是介绍STL和相关技术的未来发展趋势和发展方向。 总的来说,c++标准模板编程实战是一本非常好的STL实战教材,既可以作为初学者入门的指南,也可以作为中高级程序员巩固和深入学习STL的参考书。无论是学习STL的基础知识、习惯性使用STL容器和算法,还是在项目中灵活高效地应用STL,都会受益匪浅。 ### 回答3: c标准模板(STL)是一组C++模板类和函数的集合,可以让程序员使用一些高效的算法和数据结构,从而降低了开发者的工作量,提高了C++程序的效率和可维护性。 《C++标准模板编程实战》是一本介绍STL的经典教材,全书共分为25个章节,内容涉及到STL的迭代器、算法、容器、函数对象、适配器等方面。可谓是STL入门的重要读物。 该书的编写思路以工程实践为导向,讲解一些常用的数据结构和算法的实现过程,并给出了一些标准中经典的函数的代码实现。例如,生成随机数的代码、字符串排序的代码、实现二叉堆的代码等等。这些代码可以帮助开发者更好地理解STL中的模板类和函数的实现原理和效率。 此外,该书对STL的算法进行了详细介绍,包括容器、迭代器、函数对象等方面的应用。为了方便程序员,书中还提供了一些实用的STL程序的代码,例如STL的多个容器和关联式容器,还有STL中提供的适配器等。 总之,《C++标准模板编程实战》是学习STL的必备参考书,不仅深入浅出地讲解了STL的实现原理和应用,更是教会了我们如何将STL运用到工程中,将编程变得更加高效和简单。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值