经验首页 前端设计 程序设计 Java相关 移动开发 数据库/运维 软件/图像 大数据/云计算 其他经验
当前位置:技术经验 » 程序设计 » C++ » 查看文章
C++学习笔记之进阶编程 - time-flies
来源:cnblogs  作者:time-flies  时间:2021/6/15 9:19:41  对本文有异议

进阶编程

STL(Standard Template Library)

  • STL算法是泛型的(generic),不与任何特定数据结构和对象绑定,不必在环境类似的情况下重写代码;
  • STL算法可以量身定做,并且具有很高的效率;
  • STL可以进行扩充,可以编写自己的组件并且能与STL标准的组件进行很好的配合;

容器(container)

容器用于存放数据;STL的容器分为两大类:

  • 序列式容器(Sequence Containers):其中的元素都是可排序的(ordered),STL提供了vector,list,deque等序列式容器,而stack,queue,priority queue则是容器适配器
  1. struct Display
  2. {
  3. void operator()(int i)
  4. {
  5. cout << i << " ";
  6. }
  7. };
  8. int main()
  9. {
  10. int iArr[] = { 1, 2,3,4,5 };
  11. //序列式容器
  12. vector<int> iVector(iArr, iArr + 4);
  13. list<int> iList(iArr, iArr + 4);
  14. deque<int> iDeque(iArr, iArr + 4);
  15. //容器适配器
  16. queue<int> iQueue(iDeque); // 队列 先进先出
  17. stack<int> iStack(iDeque); // 栈 先进后出
  18. priority_queue<int> iPQueue(iArr, iArr + 4); // 优先队列,按优先权
  19. //序列式容器遍历——3种方法
  20. //序列式容器遍历 迭代器+仿函数遍历
  21. for_each( iVector.begin(), iVector.end(), Display() );
  22. cout << endl;
  23. //序列式容器遍历 迭代器+循环遍历
  24. for (auto it = iList.begin(); it != iList.end(); it++)
  25. {
  26. cout << *it << " ";
  27. }
  28. cout << endl;
  29. //序列式容器遍历 for (auto i : s)
  30. for (auto i:iDeque)
  31. {
  32. cout <<i<< " ";
  33. }
  34. //容器适配器遍历
  35. while ( !iQueue.empty() )
  36. {
  37. cout << iQueue.front() << " "; // 1 2 3 4
  38. iQueue.pop();
  39. }
  40. cout << endl;
  41. while (!iStack.empty())
  42. {
  43. cout << iStack.top() << " "; // 4 3 2 1
  44. iStack.pop();
  45. }
  46. cout << endl;
  47. while (!iPQueue.empty())
  48. {
  49. cout << iPQueue.top() << " "; // 4 3 2 1
  50. iPQueue.pop();
  51. }
  52. cout << endl;
  53. return 0;
  54. }

注:容器的遍历可参考 c++ vector容器遍历方式for(auto &i:s)和for(auto i:s)区别 ,仿函数参考 c++仿函数 functor

  • 关联式容器(Associative Containers):每个数据元素都是由一个键(key)和值(Value)组成,当元素被插入到容器时,按其键以某种特定规则放入适当位置;常见的STL关联容器如:set,multiset,map,multimap
  1. struct Display
  2. {
  3. void operator()(pair<string, double> info)
  4. {
  5. cout << info.first << ": " << info.second << endl;
  6. }
  7. };
  8. int main()
  9. {
  10. map<string, double> studentSocres;
  11. // []运算符作用:查找与Key匹配的元素(不存在则使用默认映射插入),返回的是Value的引用(意味着外部可以更新Value)
  12. studentSocres["LiMing"] = 95.0;
  13. //insert逻辑:如果存在则不插入
  14. studentSocres.insert(pair<string, double>("zhangsan", 100.0) );
  15. studentSocres.insert(map<string, double>::value_type("zhaoliu", 95.5) );
  16. for_each(studentSocres.begin(), studentSocres.end(), Display());
  17. cout << endl;
  18. //查找值
  19. map<string, double>::iterator iter;
  20. iter = studentSocres.find("zhaoliu");
  21. if (iter != studentSocres.end())
  22. {
  23. cout << "Found the score is: " << iter->second << endl;
  24. }
  25. else
  26. {
  27. cout << "Didn't find the key." << endl;
  28. }
  29. // 使用迭代器完成遍历查找的过程
  30. iter = studentSocres.begin();
  31. while (iter != studentSocres.end())
  32. {
  33. if (iter->second < 98.0) // 去除不是优秀的同学
  34. {
  35. studentSocres.erase(iter++); // 注意:迭代器失效问题
  36. }
  37. else
  38. {
  39. iter++;
  40. }
  41. }
  42. for_each(studentSocres.begin(), studentSocres.end(), Display());
  43. cout << endl;
  44. for (iter = studentSocres.begin(); iter != studentSocres.end(); iter++)
  45. {
  46. if (iter->second <= 98.5)
  47. {
  48. iter = studentSocres.erase(iter); // 注意:迭代器失效问题
  49. }
  50. }
  51. for_each(studentSocres.begin(), studentSocres.end(), Display());
  52. cout << endl;
  53. studentSocres.erase(studentSocres.begin(), studentSocres.end());
  54. for_each(studentSocres.begin(), studentSocres.end(), Display());
  55. cout << endl;
  56. return 0;
  57. }

仿函数(functor)

  • 仿函数一般不会单独使用,主要是为了搭配STL算法使用。
  • 函数指针不能满足STL对抽象性的要求,不能满足软件积木的要求,无法和STL其他组件搭配。
  • 本质就是类重载了一个operator(),创建一个行为类似函数的对象。

C++中仿函数/函数对象,函数指针的用法

  1. bool MySort(int a, int b) { return a < b; }
  2. void Display(int a) { cout << a << " "; }
  3. template<class T>
  4. inline bool MySortT(T const& a, T const& b) { return a < b; }
  5. template<class T>
  6. inline void DisplayT(T const& a) { cout << a << " "; }
  7. struct SortF
  8. {
  9. bool operator() (int a, int b) { return a < b; }
  10. };
  11. struct DisplayF
  12. {
  13. void operator() (int a) { cout << a << " "; }
  14. };
  15. // C++仿函数模板
  16. template<class T>
  17. struct SortTF
  18. {
  19. inline bool operator() (T const& a, T const& b) const { return a < b; }
  20. };
  21. template<class T>
  22. struct DisplayTF
  23. {
  24. inline void operator() (T const& a) const { cout << a << " "; }
  25. };
  26. int main()
  27. {
  28. // C++方式
  29. int arr[] = { 4, 3, 2, 1, 7 };
  30. sort(arr, arr + 5, MySort);
  31. for_each(arr, arr + 5, Display);
  32. cout << endl;
  33. // C++泛型
  34. int arr2[] = { 4, 3, 2, 1, 7 };
  35. sort(arr2, arr2 + 5, MySortT<int>);
  36. for_each(arr2, arr2 + 5, DisplayT<int>);
  37. cout << endl;
  38. // C++仿函数
  39. int arr3[] = { 4, 3, 2, 1, 7 };
  40. sort(arr3, arr3 + 5, SortTF<int>() );
  41. for_each(arr3, arr3 + 5, DisplayTF<int>());
  42. cout << endl;
  43. // C++仿函数模板
  44. int arr4[] = { 4, 3, 2, 1, 7 };
  45. sort(arr4, arr4 + 5, SortF());
  46. for_each(arr4, arr4 + 5, DisplayF());
  47. cout << endl;
  48. return 0;
  49. }

算法(algorithm)

STL 中算法大致分为四类(包含于<algorithm>、<numeric>、<functional>):

  • 非可变序列算法:指不直接修改其所操作的容器丙容的算法;
  • 可变序列算法:指可以修改它们所操作的容器内容的算法;
  • 排序算法:包括对序列进行排序和合并的算法、搜索算法以及有序序列上的集合操作;
  • 数值算法:对容器内容进行数值计算;查找,排序和通用算法,排列组合算法,数值算法,集合算法等算法;

最常见的算法包括:查找、排序和通用算法、排列组合算法、数值算法、集合算法等算法。

transform

transform函数的作用是将某操作应用于指定范围的每个元素,参考 C++学习transform函数的应用 ,示例如下:

  1. int main()
  2. {
  3. // transform和lambda表达式
  4. int ones[] = { 1, 2, 3, 4, 5 };
  5. int twos[] = { 10, 20, 30, 40, 50 };
  6. int results[5];
  7. transform(ones, ones + 5, twos, results, std::plus<int>()); // 数组元素依次相加并返回
  8. for_each(results, results + 5, [ ](int a)->void { cout << a << endl; } ); // lambda表达式(匿名函数)
  9. cout << endl;
  10. return 0;
  11. }

查找

查找、统计相关的算法如下:

  1. int main()
  2. {
  3. int arr[] = { 0, 1, 2, 3, 3, 4, 4, 5, 6, 6, 7, 7, 7, 8 };
  4. int len = sizeof(arr) / sizeof(arr[0]);
  5. vector<int> iA(arr + 2, arr + 6); // {2,3,3,4}
  6. // 统计6的个数
  7. cout << count(arr, arr + len, 6) << endl;
  8. // 统计<7的个数, bind2nd已经启用
  9. cout << count_if(arr, arr + len, bind2nd(less<int>(), 7) ) << endl;
  10. // 9找不到,二分法查找
  11. cout << binary_search(arr, arr + len, 9) << endl;
  12. // 查找子序列,不存在时返回负值
  13. cout << *search(arr, arr + len, iA.begin(), iA.end()) << endl;
  14. cout << endl;
  15. return 0;
  16. }

上面使用了“arr + len”,但数组名不是指针,只是可以转换为指向其指代实体的指针,参考 C/C++中数组名退化为指针的情况
注:binder1stbinder2nd在 c + + 11 中已弃用,在 c + + 17 中删除。

全排列

输入一个不存在重复字符的字符串,打印出字符串中字符的全排列,比如:
输入: "123" 321 = 3!
输出: 123 132 213 231 321 312

  1. //f(123) = 1+f(23), f(23) = 2+f(3), f(3) = 3 递归
  2. void swap(char* a, char* b)
  3. {
  4. char temp = *a;
  5. *a = *b;
  6. *b = temp;
  7. }
  8. void Permutation(char* pStr, char* pPostion)
  9. {
  10. // 基准点
  11. if (*pPostion == '\0')
  12. {
  13. cout << pStr << endl;
  14. }
  15. else
  16. {
  17. for (char* pChar = pPostion; *pChar != '\0'; pChar++)
  18. {
  19. // 依次和后面的字符交换
  20. swap(*pChar, *pPostion);
  21. Permutation(pStr, pPostion + 1);
  22. // 换回来
  23. swap(*pChar, *pPostion);
  24. }
  25. }
  26. }
  27. int main()
  28. {
  29. char test[] = "132";
  30. Permutation(test, test);
  31. cout << endl;
  32. // 用STL输出全排列
  33. // 注意:必须要保证数组顺序,从小到大
  34. do
  35. {
  36. cout << test[0] << test[1] << test[2] << endl;
  37. } while (next_permutation(test, test + 3));
  38. cout << endl;
  39. char test2[] = "321";
  40. // 注意:必须要保证数组顺序,从大到小
  41. do
  42. {
  43. cout << test2[0] << test2[1] << test2[2] << endl;
  44. } while (prev_permutation(test2, test2 + 3));
  45. return 0;
  46. }

迭代器(iterator)

迭代器是一种smart pointer,用于访问顺序容器和关联容器中的元素,相当于容器和操纵容器的算法之间的中介。
迭代器按照定义方式分成以下四种:

  • 正向迭代器:iterator
  • 常量正向迭代器:const_iterator
  • 反向迭代器:reverse_iterator
  • 常量反向迭代器:const_reverse_iteratorr
  1. int main()
  2. {
  3. list<int> v;
  4. v.push_back(3);
  5. v.push_back(4);
  6. v.push_front(2);
  7. v.push_front(1); // 1, 2, 3, 4
  8. list<int>::const_iterator it;
  9. for (it = v.begin(); it != v.end(); it++)
  10. {
  11. //*it += 1;
  12. cout << *it << " ";
  13. }
  14. cout << endl;
  15. // 注意:迭代器不支持<
  16. //for (it = v.begin(); it < v.end(); it++)
  17. //{
  18. // cout << *it << " ";
  19. //}
  20. cout <<v.front() << endl;
  21. v.pop_front(); // 从顶部去除
  22. list<int>::reverse_iterator it2;
  23. for (it2 = v.rbegin(); it2 != v.rend(); it2++)
  24. {
  25. *it2 += 1;
  26. cout << *it2 << " "; // 5 4 3
  27. }
  28. cout << endl;
  29. return 0;
  30. }

容器适配器(adapter)

  • stack 堆栈:一种”先进后出”的容器,底层数据结构是使用的deque;
  1. #include<stack>//定义栈所需的头文件
  2. using namespace std;
  3. stack<int> s;//定义一个元素为int型的栈
  4. int a=10;
  5. s.push(a);//将a入栈
  6. s.pop();//出栈一个元素
  7. s.empty();//返回栈是否为空
  8. s.size();//返回栈的大小
  9. s.top();//返回栈顶元素
  • queue队列:一种”先进先出”的容器,底层数据结构是使用的deque;
  1. #include<queue>//定义队列所需的头文件
  2. using namespace std;
  3. queue<int> s;//定义一个元素为int型的栈
  4. int a=10;
  5. q.push(a);//将a队尾插入一个元素
  6. q.pop();//删除队头的元素
  7. q.empty();//返回队列是否为空,是的话返回1,不是返回0
  8. q.size();//返回队列的大小
  9. a=q.front();//返回队首元素
  10. a=q.back();//返回队尾元素
  • priority_queue 优先队列:一种特殊的队列,它能够在队列中进行排序(堆排序),底层实现结构是vector或者deque;
  1. priority_queue<int> pq; // 默认是最大值优先
  2. //priority_queue<int, vector<int>, less<int> > pq2; // 最大值优先
  3. //priority_queue<int, vector<int>, greater<int> > pq3; // 最小值优先
  4. pq.push(2);
  5. pq.push(1);
  6. pq.push(3);
  7. pq.push(0);
  8. while (!pq.empty())
  9. {
  10. int top = pq.top();
  11. cout << " top is: " << top<< endl;
  12. pq.pop();
  13. }
  14. cout << endl;

注:用法可参考 C++栈和队列(stack,queue,priority_queue)

空间配置器(allocator)

从使用的角度来看,allocator隐藏在其他组件中默默工作,不需要关心,但是从理解STL实现角度来看,它是需要首先分析的组件。 allocator的分析可以体现C++在性能和资源管理上优化思想。

  1. #include "jjalloc.h"
  2. #include <vector>
  3. using namespace std;
  4. int main()
  5. {
  6. int ia[5] = { 0, 1, 2, 3, 4 };
  7. unsigned int i;
  8. //为vector配置自定义的空间配置器
  9. vector<int, JJ::allocator<int> > iv(ia, ia + 5);
  10. for (i = 0; i < iv.size(); i++)
  11. {
  12. cout << iv[i] << " ";
  13. }
  14. cout << endl;
  15. return 0;
  16. }

注:《STL源码剖析》侯捷,SGISTL版本的可读性较强。

STL总结

  • STL的六大组件给软件编程带来了新的多态和复用,是现代C++语言高效的精髓;
  • 泛型和STL的学习路线很陡,建议初学者先学会基本使用和简单扩展;
  • 掌握了一定基础的情况下,可以通过进一步学习和分析源码,编写自己的组件来提升能力;

关于Boost库

Boost库是为C++语言标准库提供扩展的一些C++程序库的总称,由Boost社区组织开发、维护,Boost库可以与C++标准库完美共同工作,并且为其提供扩展功能。

Boost可为大致为20多个分类:字符串和文本处理库、容器库、算法库、函数对象和高阶编程库、综合类库等等。

具体见:https://www.boost.org/

个人认为如果C++标准库可以满足日常开发的需求,Boost库没有学习必要。

多线程

线程基础

使用C++11中Thread时注意线程安全问题,可以使用mutex等锁:

  1. #include <thread>
  2. #include <mutex>
  3. #include <iostream>
  4. using namespace std;
  5. mutex g_mutex;
  6. void T1()
  7. {
  8. g_mutex.lock();
  9. cout << "T1 Hello" << endl;
  10. g_mutex.unlock();
  11. }
  12. void T2(const char* str)
  13. {
  14. g_mutex.lock();
  15. cout << "T2 " << str << endl;
  16. g_mutex.unlock();
  17. }
  18. int main()
  19. {
  20. thread t1(T1);
  21. thread t2(T2, "Hello World");
  22. t1.join();
  23. //t2.join();
  24. t2.detach();
  25. cout << "Main Hi" << endl;
  26. return 0;
  27. }

join()、detach()可参考
C++11多线程join()和detach()的理解

  • join()函数是一个等待线程完成函数,主线程需要等待子线程运行结束了才可以结束;
  • detach()函数称为分离线程函数,使用detach()函数会让线程在后台运行,即说明主线程不会等待子线程运行结束才结束;

互斥锁mutex可以配合lock_guardunique_lock使用,参考 c++11中的lock_guard和unique_lock使用浅析
原子操作atomic的也可用于线程同步,常用于变量资源,参考 C++原子操作 atomic的使用及效率

线程交换

  1. thread tW1([]()
  2. {
  3. cout << "ThreadSwap1 " << endl;
  4. });
  5. thread tW2([]()
  6. {
  7. cout << "ThreadSwap2 " << endl;
  8. });
  9. cout << "ThreadSwap1' id is " << tW1.get_id() << endl;
  10. cout << "ThreadSwap2' id is " << tW2.get_id() << endl;
  11. cout << "Swap after:" << endl;
  12. swap(tW1, tW2);
  13. cout << "ThreadSwap1' id is " << tW1.get_id() << endl;
  14. cout << "ThreadSwap2' id is " << tW2.get_id() << endl;
  15. tW1.join();
  16. tW2.join();
  1. ThreadSwap1
  2. ThreadSwap2
  3. ThreadSwap1' id is 26136
  4. ThreadSwap2' id is 26612
  5. Swap after:
  6. ThreadSwap1' id is 26612
  7. ThreadSwap2' id is 26136

线程移动

  1. thread tM1( []() { ; } );
  2. //tM1.join();
  3. cout << "ThreadMove1' id is " << tM1.get_id() << endl;
  4. cout << "Move after:" << endl;
  5. thread tM2 = move(tM1);
  6. cout << "ThreadMove2' id is " << tM2.get_id() << endl;
  7. cout << "ThreadMove1' id is " << tM1.get_id() << endl;
  8. tM2.join();
  1. ThreadMove1' id is 17940
  2. Move after:
  3. ThreadMove2' id is 17940
  4. ThreadMove1' id is 0

推荐基本C++的书籍

  • 入门:
    《C++Primer》Stanley B.Lippman

  • 最佳实践:
    《C++高质量编程》林锐
    《Effective C++》候捷(译)
    《More Effective C++》候捷(译)
    《Effective STL》潘爱民(译)
    《The C++Programming Language》Bjarne Stroustrup

  • 深入:
    《STL源码剖析》候捷
    《COM本质论》Don Box
    《Exceptional C++》Addsion.Wesley
    《Inside the C++Object Model》Stanley B.Lippman
    《The Design and Evolution of C++》Bjarne Stroustrup

原文链接:http://www.cnblogs.com/timefiles/p/CppStudyNotesAdvancedProgramming.html

 友情链接:直通硅谷  点职佳  北美留学生论坛

本站QQ群:前端 618073944 | Java 606181507 | Python 626812652 | C/C++ 612253063 | 微信 634508462 | 苹果 692586424 | C#/.net 182808419 | PHP 305140648 | 运维 608723728

W3xue 的所有内容仅供测试,对任何法律问题及风险不承担任何责任。通过使用本站内容随之而来的风险与本站无关。
关于我们  |  意见建议  |  捐助我们  |  报错有奖  |  广告合作、友情链接(目前9元/月)请联系QQ:27243702 沸活量
皖ICP备17017327号-2 皖公网安备34020702000426号