C++ day37 标准模板库STL初识

红太狼 2023-02-18 08:07 116阅读 0赞

文章目录

  • 简介
    • 是什么:容器类模板,迭代器类模板,函数对象模板,算法模板的集合
      • STL不是面向对象编程,而是泛型编程!
    • 历史:1994年发布
  • 模板类vector
    • 示例1:创建vector对象,用[]随机访问元素
    • vector类的方法
      • 示例2:演示这些方法
  • STL的非成员函数:也是为了通用性
    • for_each()
    • random_shuffle():随机排列
    • sort()
    • 示例
  • 基于范围的for循环:专为STL设计
    • 示例1:简单,```for (double x : prices)```
    • 示例2:替换for_each
    • 和for_each的区别:基于范围的for循环可以修改容器的内容

简介

是什么:容器类模板,迭代器类模板,函数对象模板,算法模板的集合

standard template library

模板库,自然里面都是模板咯。

这些所有的模板的设计是一致的,都是基于泛型编程准则。

具体是关于什么的模板呢?

是表示容器,迭代器,函数对象,算法的模板。

  • 容器

是一个类似于数组的单元,可以存储若干个值。STL容器是同质的,即存储的若干个值的数据类型一样。

比如数组,队列,链表

  • 迭代器

是可以用来遍历容器的对象,类似于可以遍历数组的指针,所以迭代器是广义的指针,可以对他解引用,也可以进行递增递减操作,所以行为上很类似指针。

通过把指针广义化为迭代器,可以让STL为所有容器类提供统一的接口,这才是迭代器对于STL的巨大好处,通用!

每一个容器类都有一个迭代器,类型是用typedef定义的iterator,迭代器的作用域是整个类,即拥有类作用域。

  1. vector<int>::iterator p;//p是vector<int>类的一个迭代器
  2. vector<int> scores;
  3. p = scores.begin();
  4. *p = 23;//对迭代器解引用
  5. ++p;//对迭代器递增

而且这里C++的自动类型推断特别有用:

  1. auto p = scores.begin();//vector<int>::iterator p = scores.begin();
  • 函数对象

类似于函数,但是是对象。

函数名就是指针哈,一定不要忘记这个本质。字符串也是指针。

  • 算法

是完成特定任务,比如排序,查找的方法。

STL不是面向对象编程,而是泛型编程!

OOP和GP是不同的编程模式。

STL充分展示了泛型编程的有趣,学习STL可以领会到泛型编程的精神,学习的时候先建立感性认识,同时注意关注底层的设计理念。

想让类成为通用的,就得设计为模板类才行,这就是STL的设计理念。

在这里,通用即强大。毕竟方便嘛。

历史:1994年发布

在这里插入图片描述
然后ISO/ANSI C++委员会就投票同意把他作为C++标准的一部分了。

模板类vector

注意,这里vector不是数学矢量哦,只是数组而已,完全不是数学上那个有方向,可以进行内积和外积操作的量哈。
vector类实际上是数组的实现,其重点自然是随机访问啦。

  • 定义在头文件vector中,要记得包含。
  • vector类使用动态内存分配,所以可以用初始化参数指出需要的空间。

注意,所有STL容器的模板类都需要一个分配器对象来管理他们的内存,以达到动态分配内存的灵活性,string类也一样,他们的动态内存分配实际都是一个专门的分配器在做,专业的人做专业的事,分工很细致。
具体来说,STL容器的模板类有一个可选的参数,以指定到底用哪个分配器对象来管理它的内存:

  1. template<class T, class Allocator = allocator<T>>
  2. class vector{ ···}

如果不写这个参数,那就默认使用allocator<T>类,这个类使用new和delete。
难道别的分配器不用new和delete??那还能用啥??

  • vector类重载了[]运算符,所以可以用数组表示法随机访问vector对象的每个元素。

示例1:创建vector对象,用[]随机访问元素

  1. #include <iostream>
  2. #include <vector>
  3. #include <string>
  4. const int NUM = 5;
  5. int main(){
  6. using std::string;
  7. using std::vector;
  8. using std::cin;
  9. using std::cout;
  10. vector<int> ratings(NUM);
  11. vector<string> titles(NUM);
  12. cout << "Enter " << NUM << " books and your ratings (0-10)\n";
  13. int i;
  14. for (i=0;i<NUM;++i){
  15. cout << "Enter title #" << i+1 << ": ";
  16. getline(cin, titles[i]);//会读取并扔掉换行符(默认分界符)
  17. cout << "Enter your ratings: \n";
  18. cin >> ratings[i];
  19. cin.get();//读取换行符
  20. }
  21. cout << "You entered the following:\n";
  22. for (i=0;i<NUM;++i){
  23. cout << titles[i] << ": " << ratings[i] << std::endl;
  24. }
  25. return 0;
  26. }
  27. Enter 5 books and your ratings (0-10)
  28. Enter title #1: The Cat Who Knows C++
  29. Enter your ratings:
  30. 10
  31. Enter title #2: Panic Oriented Programming
  32. Enter your ratings:
  33. 9
  34. Enter title #3: Warlords of Wonk
  35. Enter your ratings:
  36. 5
  37. Enter title #4: Don't Touch That Metaphor
  38. Enter your ratings:
  39. 7
  40. Enter title #5: Felonious Feliness
  41. Enter your ratings:
  42. 4
  43. You entered the following:
  44. The Cat Who Knows C++: 10
  45. Panic Oriented Programming: 9
  46. Warlords of Wonk: 5
  47. Don't Touch That Metaphor: 7
  48. Felonious Feliness: 4

很简单的示例,只是注意输入评分用cin读取后要处理换行符,而输入书名则不用,因为getline函数已经帮我们做了这件事。

vector类的方法

  1. 所有STL容器都有的方法:

    • size():返回容器中元素数目
    • swap():交换两个容器的内容
    • begin():返回一个指向容器的第一个元素的迭代器
    • end():返回一个指向超过容器尾(past-the-end)的迭代器,即指向容器最后一个元素的后面的那个元素。

    for (p=scores.begin();p!=scores.end();++p)

    1. cout << *p << endl;
  2. 部分STL容器才有的方法:

    • push_back():把元素添加到矢量末尾。这需要做内存管理,增加长度才能容纳的下嘛。
    • erase():删除容器中给定区间的元素。用两个迭代器指定要删除的区间。注意第一个迭代器指向区间第一个元素,而第二个指向区间最后一个元素的后一个元素。

    scores.erase(scores.begin(), scores.begin()+2);

    • insert()
      在这里插入图片描述
      在这里插入图片描述
      把new_v的除了第一个元素之外的所有元素,都插入到old_v的第一个元素前面。

示例2:演示这些方法

  1. #include <iostream>
  2. #include <string>
  3. #include <vector>
  4. struct Review{
  5. std::string title;
  6. int rating;
  7. };
  8. bool FillReview(Review & rr);
  9. void ShowReview(const Review & rr);
  10. int main(){
  11. using std::cout;
  12. using std::vector;
  13. vector<Review> books;
  14. Review temp;
  15. while (FillReview(temp))
  16. books.push_back(temp);
  17. int num = books.size();
  18. if (num > 0){
  19. cout << "Thank you. You entered the following:\n"
  20. << "Rating\tBook\n";
  21. for (int i=0;i<num;++i)
  22. ShowReview(books[i]);
  23. cout << "Reprising:\n" << "Rating\tBook\n";
  24. vector<Review>::iterator pr;
  25. for (pr=books.begin();pr!=books.end();++pr)
  26. ShowReview(*pr);
  27. vector<Review> oldlist(books);//复制构造函数
  28. if (num > 3){
  29. //删除两项
  30. books.erase(books.begin()+1, books.begin()+3);
  31. cout << "After erasure:\n";
  32. for (pr = books.begin();pr!=books.end();++pr)
  33. ShowReview(*pr);
  34. //插入1项
  35. books.insert(books.begin(), oldlist.begin()+1,
  36. oldlist.begin()+2);
  37. cout << "After insertion:\n";
  38. for (pr=books.begin();pr!=books.end();++pr)
  39. ShowReview(*pr);
  40. }
  41. books.swap(oldlist);
  42. cout << "Swapping oldlist with books:\n";
  43. for (pr=books.begin();pr!=books.end();++pr)
  44. ShowReview(*pr);
  45. }
  46. else
  47. cout << "Nothing entered, nothing gained!\n";
  48. return 0;
  49. }
  50. bool FillReview(Review & rr)//按引用传递结构体
  51. {
  52. std::cout << "Enter bool title (quit to quit):\n";
  53. std::getline(std::cin, rr.title);
  54. if(rr.title == "quit")
  55. return false;
  56. std::cout << "Enter book rating: ";
  57. std::cin >> rr.rating;
  58. if (!std::cin)//如果cin是空指针,则读取出错
  59. return false;
  60. while (std::cin.get()!='\n')//读取输入队列中的字符
  61. ;
  62. return true;
  63. }
  64. void ShowReview(const Review & rr)
  65. {
  66. std::cout << rr.rating << "\t" << rr.title << std::endl;
  67. }
  68. Enter bool title (quit to quit):
  69. The Cat Who Knows Vectors
  70. Enter book rating: 1
  71. Enter bool title (quit to quit):
  72. Candid Canies
  73. Enter book rating: 5
  74. Enter bool title (quit to quit):
  75. Warrior of Wonk
  76. Enter book rating: 9
  77. Enter bool title (quit to quit):
  78. Quantum Manners
  79. Enter book rating: 6
  80. Enter bool title (quit to quit):
  81. quit
  82. Thank you. You entered the following:
  83. Rating Book
  84. 1 The Cat Who Knows Vectors
  85. 5 Candid Canies
  86. 9 Warrior of Wonk
  87. 6 Quantum Manners
  88. Reprising:
  89. Rating Book
  90. 1 The Cat Who Knows Vectors
  91. 5 Candid Canies
  92. 9 Warrior of Wonk
  93. 6 Quantum Manners
  94. After erasure:
  95. 1 The Cat Who Knows Vectors
  96. 6 Quantum Manners
  97. After insertion:
  98. 5 Candid Canies
  99. 1 The Cat Who Knows Vectors
  100. 6 Quantum Manners
  101. Swapping oldlist with books:
  102. 1 The Cat Who Knows Vectors
  103. 5 Candid Canies
  104. 9 Warrior of Wonk
  105. 6 Quantum Manners

STL的非成员函数:也是为了通用性

non-member function

非成员函数不是任何容器类的成员,但是却可以用于所有的容器类。这也是为了避免重复工作的理念。一个非成员函数可以解决所有容器类的需求。比如:
-find():查找算法的实现函数。

但是注意,对于某些操作来说,非成员函数的效率要差一些,这种情况就会定义成员函数,因为对于一个特定的类来说,可以有优化的点,使得效率更高。比如,vector类的swap函数的效率比非成员函数的swap函数高。
但是非成员函数还有个好处:比如swap函数,可以交换不同容器的内容。

下面讲三个最具代表性的STL函数。

for_each()

  • 可以用于任何容器类。
  • 有三个参数,前两个定义了容器的区间,第三个是函数指针,普遍说是函数对象。注意函数对象指向的函数并不可以修改容器元素的值
  • 可以代替for循环。

比如

  1. vector<Review>::iterator pr;
  2. for (pr=books.begin();pr!=books.end();++pr)
  3. ShowReview(*pr);

可以改为

  1. for_each(books.begin(), books.end(), ShowReview);
  2. //没有显式使用迭代器变量

random_shuffle():随机排列

在这里插入图片描述

  • 只能用于可以随机访问的容器类。比如vector,但是list就不可以。

    random_shuffle(books.begin(), books.end());

sort()

  • 也只能用于可以随机访问的容器类。比如vector,但是list就不可以。
  • 第一个版本有2个参数,定义区间。

    vector a;
    sort(a.begin(), a.end());

  • 排序函数需要用到<运算符,如果容器类传入的参数是内置数据类型,则使用内置的<;如果容器类传入的参数是自己定义的类,则自己还需要定义可以处理这个类的对象的<运算符,才可以排序。

比如上面的Review结构体,是一个成员为公有的类,我们要给他重载<运算符:

  1. bool operator<(const Review & r1, const Review & r2)
  2. {
  3. //按照字母顺序排列
  4. if (r1.title < r2.title)
  5. return true;
  6. if (r1.title == r2.title && r1.rating < r2.rating)
  7. return true;
  8. return false;
  9. }
  10. sort(books.begin(), books.end());//升序排列
  11. //编译器会自己去找Review类的operator<函数??
  • 第二种版本的sort函数。接受3个参数,前两个还是指定区间的迭代器,第三个是函数对象。

比如:

  1. bool WorseThan(const Review & r1, const Review & r2)
  2. {
  3. //只按照评分
  4. if (r1.rating < r2.rating)
  5. return true;
  6. return false;
  7. }
  8. sort(books.begin(), books.end(), WorseThan);//按rating的升序排列

示例

  1. #include <vector>
  2. #include <string>
  3. #include <iostream>
  4. #include <algorithm>
  5. struct Review{
  6. int rating;
  7. std::string title;
  8. };
  9. bool operator<(const Review & r1, const Review & r2);
  10. void ShowReview(const Review & rr);
  11. bool FillReview(Review & rr);
  12. bool worseThan(const Review & r1, const Review & r2);
  13. int main(){
  14. using namespace std;
  15. vector<Review> books;
  16. Review temp;
  17. while (FillReview(temp))
  18. books.push_back(temp);
  19. if (books.size() < 0)
  20. cout << "Nothing entered, nothing gained!\n";
  21. else{
  22. cout << "You entered the following:\n"
  23. << "Ratings\tBooks\n";
  24. for_each(books.begin(), books.end(), ShowReview);
  25. sort(books.begin(), books.end());
  26. cout << "Sorted by title:\nfRating\tBook\n";
  27. for_each(books.begin(), books.end(), ShowReview);
  28. sort(books.begin(), books.end(), worseThan);
  29. cout << "Sorted by rating:\nRating\tBook\n";
  30. for_each(books.begin(), books.end(), ShowReview);
  31. random_shuffle(books.begin(), books.end());//随机排列
  32. cout << "After shuffling:\nRating\tBook\n";
  33. for_each(books.begin(), books.end(), ShowReview);
  34. }
  35. return 0;
  36. }
  37. bool operator<(const Review & r1, const Review & r2)
  38. {
  39. if (r1.title < r2.title)
  40. return true;
  41. else if (r1.title == r2.title && r1.rating < r2.rating)
  42. return true;
  43. return false;
  44. }
  45. bool worseThan(const Review & r1, const Review & r2)
  46. {
  47. if (r1.rating < r2.rating)
  48. return true;
  49. return false;
  50. }
  51. bool FillReview(Review & rr)
  52. {
  53. std::cout << "Enter book title:\n";
  54. std::getline(std::cin, rr.title);
  55. if (rr.title == "quit")
  56. return false;
  57. std::cout << "Enter your rating(0-10): ";
  58. std::cin >> rr.rating;
  59. if (!std::cin)
  60. return false;
  61. while (std::cin.get() != '\n')
  62. ;
  63. return true;
  64. }
  65. void ShowReview(const Review & rr)
  66. {
  67. std::cout << rr.rating << "\t" << rr.title << std::endl;
  68. }
  69. Enter book title:
  70. The Cat Who Knows Vectors
  71. Enter your rating(0-10): 7
  72. Enter book title:
  73. Candid Canies
  74. Enter your rating(0-10): 5
  75. Enter book title:
  76. Warrior of Wonk
  77. Enter your rating(0-10): 2
  78. Enter book title:
  79. Quantum Manners
  80. Enter your rating(0-10): 3
  81. Enter book title:
  82. quit
  83. You entered the following:
  84. Ratings Books
  85. 7 The Cat Who Knows Vectors
  86. 5 Candid Canies
  87. 2 Warrior of Wonk
  88. 3 Quantum Manners
  89. Sorted by title:
  90. fRating Book
  91. 5 Candid Canies
  92. 3 Quantum Manners
  93. 7 The Cat Who Knows Vectors
  94. 2 Warrior of Wonk
  95. Sorted by rating:
  96. Rating Book
  97. 2 Warrior of Wonk
  98. 3 Quantum Manners
  99. 5 Candid Canies
  100. 7 The Cat Who Knows Vectors
  101. After shuffling:
  102. Rating Book
  103. 2 Warrior of Wonk
  104. 3 Quantum Manners
  105. 7 The Cat Who Knows Vectors
  106. 5 Candid Canies

基于范围的for循环:专为STL设计

示例1:简单,for (double x : prices)

  1. #include <iostream>
  2. int main(){
  3. using namespace std;
  4. double prices[4]= { 1.2, 3.5, 9.8, 5.2};
  5. for (double x : prices)//x的类型和容器内元素的类型相同,x变量依次访问容器的每一个元素
  6. cout << x << endl;
  7. return 0;
  8. }

不是只有在STL中才叫容器,一般数组,链表,栈,队列这些存放多个同类型元素的单元,都可以叫做容器,和在不在STL中没有关系的。

  1. 1.2
  2. 3.5
  3. 9.8
  4. 5.2

示例2:替换for_each

  1. for_each(books.begin(), books.end(), ShowReview);

改为:

  1. for(auto x : books)//编译器会自动推断出books的类型是vector<Review>,循环会依次把每一个Review对象传给ShowReview方法
  2. ShowReview(x);

和for_each的区别:基于范围的for循环可以修改容器的内容

但是必须要用一个引用参数!!

  1. void Add(Review & r)
  2. {
  3. r.rating++;
  4. }
  5. for (auto & x : books)
  6. Add(x);

发表评论

表情:
评论列表 (有 0 条评论,116人围观)

还没有评论,来说两句吧...

相关阅读

    相关 C++标准模板STL学习总结

    STL提供了一组表示容器、迭代器、函数对象和算法的模板。 STL是一种泛型编程。面向对象编程关注的是编程的数据方面,而泛型编程关注的是算法。它们之间的共同点是创建可重用的代