• C++模板与STL(一):模板基础与STL仿真复现


    注:本文仅作个人做笔记复习用,不具有教学意义! 

    目录

    模板的定义

    函数模板和模板函数

    函数模板不提供隐式类型转换 

    当函数模板和普通函数都符合调用规则时,优先使用普通函数

    hpp:h+cpp的结合体,声明与实现结合

     类模板

    类模板应用:Array容器的仿真复现

    模板类继承:

    模板类的嵌套

    左值与右值简介 

    转移函数和完美转发模板

    STL模板技术

    C++泛型机制的基石-数据类型表

    Traits1:利用typedef:类型萃取器

    Traits2:如果一个类模板中,全部的成员都是公有数据类型,这个模板就是一个独立的数据类型表,用来规范数据

    Traits3:非侵入式STL类型设计与数据类型

    Traits4:Traits的原理及应用:Iterator

    shared_ptr的仿真实现Shared_ptr:


    模板的定义

    • 模板把函数或类要处理的数据类型参数化,表现为参数的多态性,称为类属
    • 模板用于表达逻辑结构相同,但具体数据类型元素不同的数据
    • 对象的通用行为

    函数模板和模板函数

    1. #include
    2. using namespace std;
    3. //函数模板
    4. template<typename T>
    5. void myswap(T &a,T &b) {
    6. T temp;
    7. temp = a;
    8. a = b;
    9. b = temp;
    10. }
    11. int main() {
    12. int a = 1, b = 2;
    13. //模板函数,编译器根据传入参数生成的模板特例
    14. myswap(a, b);
    15. cout << a << " " << b << endl;
    16. double c = 2.2, d = 3.3;
    17. //模板函数,编译器根据传入参数生成的模板特例
    18. myswap(c, d);
    19. cout << c << " " << d << endl;
    20. return 0;
    21. }

     

    函数模板不提供隐式类型转换 

    1. template<typename T>
    2. void fun(T& a,T& b) {
    3. cout << "template被调用" << endl;
    4. }
    5. void fun(char c,int y) {
    6. cout << "普通函数被调用" << endl;
    7. }
    8. int main() {
    9. char ch = 'a';
    10. int data = 23;
    11. fun<int>(ch, data);//❌,函数模板不提供隐式类型转化,必须严格遵守T的类型定义
    12. fun(ch, data);//✔,调用普通函数
    13. return 0;
    14. }

    当函数模板和普通函数都符合调用规则时,优先使用普通函数

    • 因为普通函数在编译的期间就生成了函数体
    • 而模板函数的生成在调用的时候,编译器才会编译
    1. template<typename T>
    2. void fun(T& a,T& b) {
    3. cout << "template被调用" << endl;
    4. }
    5. void fun(int &a,int &b) {
    6. cout << "普通函数被调用" << endl;
    7. }
    8. int main() {
    9. int data1 = 32;
    10. int data2 = 23;
    11. fun<int>(data1, data2);//template被调用
    12. fun(data1, data2);//普通函数被调用
    13. return 0;
    14. }

    编译器在处理函数模板的时候能够生成任意类型的函数

    根据调用的实际产生不同的函数:

    编译器会对函数模板进行二次编译:这是参数化的基础,也是成为编译时多态的由来。

    在声明的地方对模板代码本身进行编译,在调用的地方对参数化以后的具体调用进行编译

    hpp:h+cpp的结合体,声明与实现结合

    【C++】模板声明与定义不分离_Yngz_Miao的博客-CSDN博客_c++模板声明

     

     类模板

    • 类模板用于实现类所需数据的类型参数化
    • 类模板在表示如数组、表、图等数据结构显得特别重要
    • 这些数据结构的表示和算法不受所包含的元素类型影响
    1. #include
    2. using namespace std;
    3. template<class T1,class T2>
    4. class MyClass {
    5. public:
    6. T1 _t1;
    7. T2 _t2;
    8. MyClass(T1 t1, T2 t2) :_t1(t1), _t2(t2) {}
    9. void print() {
    10. cout << _t1 << "," << _t2 << endl;
    11. }
    12. };
    13. int main() {
    14. MyClass<int, int>mc(1, 2);
    15. mc.print();
    16. return 0;
    17. }

    类模板应用:Array容器的仿真复现

    MyArray.hpp:

    1. #pragma once
    2. template<class T,int n>
    3. class MyArray {
    4. public:
    5. MyArray();
    6. MyArray(int length);
    7. ~MyArray();
    8. int size();
    9. T get(int num);
    10. T& operator[](int num);
    11. void set(T data, int num);
    12. public:
    13. T* pt;
    14. };
    15. template<class T, int n>
    16. inline MyArray::MyArray()
    17. {
    18. this->pt = new T[n];
    19. }
    20. template<class T, int n>
    21. inline MyArray::MyArray(int length)
    22. {
    23. this->pt = new T[length];
    24. }
    25. template<class T, int n>
    26. inline MyArray::~MyArray()
    27. {
    28. delete[]this->pt;
    29. }
    30. template<class T, int n>
    31. inline int MyArray::size()
    32. {
    33. return n;
    34. }
    35. template<class T, int n>
    36. inline T MyArray::get(int num)
    37. {
    38. if (num >= n || num < 0) {
    39. //exception;
    40. }
    41. else {
    42. return *(this->pt + num);
    43. }
    44. }
    45. template<class T, int n>
    46. inline T& MyArray::operator[](int num)
    47. {
    48. if (num >= n || num < 0) {
    49. }
    50. else {
    51. return *(pt + num);
    52. }
    53. }
    54. template<class T, int n>
    55. inline void MyArray::set(T data, int num)
    56. {
    57. if (num >= n || num < 0) {
    58. //
    59. }
    60. else {
    61. *(pt + num) = data;
    62. }
    63. }

    MyArray.cpp

    1. #include
    2. #include "MyArray.hpp"
    3. using namespace std;
    4. int main() {
    5. MyArray<int, 5>arr;
    6. for (int i = 0; i < arr.size(); i++) {
    7. arr.set(i * 10, i);
    8. cout << arr[i] << endl;
    9. }
    10. return 0;
    11. }

    模板类继承:

    模板类继承模板类:

    1. #include
    2. #include
    3. using namespace std;
    4. template<class T>
    5. class MyClass {
    6. public:
    7. T x;
    8. MyClass(T t) :x(t) {}
    9. virtual void print() = 0;
    10. };
    11. template<class T>
    12. class NewClass :public MyClass {
    13. public:
    14. T y;
    15. NewClass(T t1, T t2) :MyClass(t1), y(t2) {}
    16. void print() {
    17. cout << "x=" << x << " y=" << y << endl;
    18. }
    19. };
    20. int main() {
    21. MyClass<int>* p = new NewClass<int>(10, 8);
    22. p->print();
    23. return 0;
    24. }

     

    模板类继承普通类:

    1. class XYZ {
    2. public:
    3. int x, y, z;
    4. XYZ(int a, int b, int c) :x(a), y(b), z(c) {}
    5. virtual void print() {
    6. cout << x << " " << y << " " << z << " " << endl;
    7. }
    8. };
    9. template<class T>
    10. class GoodsXYZ :public XYZ {
    11. public:
    12. int t;
    13. GoodsXYZ(int t1, int a, int b, int c) :XYZ(a, b, c), t(t1) {}
    14. void print() {
    15. cout << "T 类型的值" << t << endl;
    16. cout << x << " " << y << " " << z << endl;
    17. }
    18. };

    普通类继承模板类

    1. class RunClass :public GoodsXYZ<int> {
    2. public:
    3. int dd = 1000;
    4. RunClass(int a2, int b2, int c2, int d2) :GoodsXYZ<int>(a2, b2, c2, d2) {}
    5. void print() {
    6. cout << dd << x << y << z;
    7. }
    8. };

    模板类的嵌套

    1. #include
    2. using namespace std;
    3. template<class T>
    4. class MyTestNestClass {
    5. public:
    6. class nClass {
    7. public:
    8. int num;
    9. };
    10. nClass nObj1, nObj2;
    11. template<class V>
    12. class RunClass {
    13. public:
    14. V v1;
    15. };//嵌套类模板,不能直接初始化
    16. RunClasst1;
    17. RunClass<double>t2;
    18. };
    19. int main() {
    20. MyTestNestClass<int>myObj1;
    21. myObj1.nObj1.num = 10;
    22. myObj1.t1.v1 = 13;
    23. myObj1.t2.v1 = 6.18;
    24. cout << myObj1.nObj1.num<< endl;
    25. cout << myObj1.t1.v1 << endl;
    26. cout << myObj1.t2.v1 << endl;
    27. return 0;
    28. }

    左值与右值:

    1. int a = 10;
    2. int b = 20;
    3. int c = a + b;
    4. __asm {
    5. mov eax,a
    6. mov ebx,b
    7. add eax,ebx
    8. mov c,eax
    9. }

    左值与右值简介 

    左值:

    • 可以出现在赋值运算符左边
    • 往往代表一个存储空间
    • 左值是一个有名字,有固定地址的表达式

    右值:

    • 所谓的数据
    • 由于计算式涉及到了计算机空间,仅仅在表达式运行过程中存在
    • 右值是一个与运算过程相匹配的临时对象,在对应语句执行完毕之后就销毁了
    • 所以无法从语法上取到右值地址
    • 右值仅仅是一个匿名,没有固定地址的对象

    右值引用:使得右值变成一个与左值完全相同的持久对象

    1. int x = 10, y = 20;
    2. int&& right = x + y;

     

    如果有一个临时对象,那么使用浅拷贝会有巨大的意义

    当我们需要具有”转移语意“的拷贝构造函数时,浅拷贝的意义就凸显了。

    1. #include
    2. using namespace std;
    3. class Foo {
    4. public:
    5. Foo(int x) {
    6. }
    7. Foo(const Foo& r) {
    8. //this->p = r.p;//浅拷贝,p和r.p指向了同一个地址
    9. p = new int;
    10. *p = *(r.p);//深拷贝
    11. }
    12. Foo(Foo&& r) {
    13. cout << "Foo(Foo&&)" << endl;
    14. p = r.p;//p和r.p指向了同一个地址
    15. r.p = nullptr;//源对象r放弃资源所有权
    16. }
    17. private:
    18. int* p;
    19. };
    20. Foo func() {
    21. Foo foo(100);
    22. return foo;
    23. }
    24. int main() {
    25. Foo f(func());//资源所有权发生转移,资源位置没有改变而所属对象变化
    26. return 0;
    27. }

     

    转移函数和完美转发模板

    1. #include
    2. using namespace std;
    3. void Func(int& x) {
    4. cout << "左值引用" << endl;
    5. }
    6. void Func(int&& x) {
    7. cout << "右值引用" << endl;
    8. }
    9. void Func(const int& x) {
    10. cout << "左值常引用" << endl;
    11. }
    12. void Func(const int&& x) {
    13. cout << "右值常引用" << endl;
    14. }
    15. template<typename T>
    16. void FuncT(T&& a) {
    17. Func(std::forward(a));//使用std::forward进行类型推导
    18. }
    19. int main() {
    20. FuncT(10);
    21. int a;
    22. FuncT(a);
    23. FuncT(std::move(a));
    24. const int b = 10;
    25. FuncT(b);
    26. FuncT(std::move(b));
    27. return 0;
    28. }

    STL模板技术

    C++泛型机制的基石-数据类型表

    Traits1:利用typedef:类型萃取器

    实现内嵌数据类型->从模板传入类型

    1. template<typename T>
    2. struct map {
    3. typedef T value_type;
    4. typedef T& reference;
    5. typedef T* pointer;
    6. };
    7. template<typename T,typename U>
    8. class A :public TypeTb1 {
    9. };
    10. int main() {
    11. map<int>::value_type a = 100;
    12. map<int>::reference b = a;
    13. map<int>::pointer c = &a;
    14. return 0;
    15. }

    Traits2:如果一个类模板中,全部的成员都是公有数据类型,这个模板就是一个独立的数据类型表,用来规范数据

    ①定义一个规范类模板类型的基类模板

    ②凡是继承了这个类型表的模板,它的访问类型就被确定

    STL库中,设计人员经常使用这种技巧:

    1. template<class _A1m,class _A2, class R>
    2. class binary
    3. {
    4. typedef _A1 Arg1;//第一个形参类型
    5. typedef _A2 Arg2;//第二个形参类型
    6. typedef R Rtn;//返回值类型
    7. };

    举例:

    1. template<typename TR,typename T1,typename T2>
    2. class Add :public binary {
    3. public:
    4. TR bFunction(const T1& x, const T2& y)const {
    5. return x + y;
    6. }
    7. };
    8. int main() {
    9. double a = 100.01, b = 20.2;
    10. Add<double, double, double>addObj;
    11. cout << addObj.bFunction(a, b) << endl;
    12. return 0;
    13. }

    因为Add包含了binary的类型数据表,因此系统中的其他模块就可以使用Add::Arg1,Add::Arg2,Add::Rtn这种方式和Add本身进行对接。

    这种数据类型的抽象,达到了多个系统模块之间的类型统一。

    Traits3:非侵入式STL类型设计与数据类型

    1. #include
    2. using namespace std;
    3. class Test1;
    4. class Test2;
    5. //两个类模板规范一个统一的接口
    6. template <typename T>
    7. class TypeTb1 {
    8. };
    9. //特化模板1
    10. template<>
    11. class TypeTb1 {
    12. public:
    13. typedef char ret_type;
    14. typedef int par1_type;
    15. typedef double par2_type;
    16. };
    17. //特化模板2
    18. template<>
    19. class TypeTb1 {
    20. public:
    21. typedef double ret_type;
    22. typedef double par1_type;
    23. typedef int par2_type;
    24. };
    25. template<typename T>
    26. class Test {
    27. public:
    28. typename TypeTb1::ret_type compute
    29. (
    30. typename TypeTb1::par1_type x,
    31. typename TypeTb1::par2_type y
    32. )
    33. {
    34. return x;
    35. }
    36. };
    37. int main() {
    38. Testt1;
    39. cout << t1.compute(65, 6.18) << endl;
    40. return 0;
    41. }

    Traits4:Traits的原理及应用:Iterator

    1. template<typename T>
    2. struct Traits{
    3. };
    4. template<typename T>
    5. struct Traits {
    6. typedef T value_type;
    7. typedef value_type* pointer;
    8. typedef value_type& reference;
    9. };
    10. int main() {
    11. Traits<double*>::value_type t2 = 4.44;
    12. cout << t2 << endl;
    13. return 0;
    14. }

    shared_ptr的仿真实现Shared_ptr:

    1. #include
    2. using namespace std;
    3. template<typename T>
    4. class Shared_ptr;
    5. template<typename T>
    6. class Res_ptr {
    7. private:
    8. T* res_p;
    9. int use_num;
    10. Res_ptr(T* p) :res_p(p), use_num(1) {
    11. cout << "res 构造函数" << endl;
    12. }
    13. ~Res_ptr() {
    14. cout << "res 析构函数" << endl;
    15. }
    16. friend class Shared_ptr;
    17. };
    18. template<typename T>
    19. class Shared_ptr {
    20. public:
    21. Shared_ptr(T* p) :ptr(new Res_ptr(p)) {
    22. cout << "Shared_ptr的构造函数 " << " use_num=" << ptr->use_num << endl;
    23. }
    24. Shared_ptr(const Shared_ptr& origin) :ptr(origin.ptr) {
    25. ++ptr->use_num;
    26. cout << "Shared_ptr的拷贝构造函数" << " use_num=" << ptr->use_num << endl;
    27. }
    28. ~Shared_ptr() {
    29. cout<<"Shared_ptr的析构函数"<< " use_num=" << ptr->use_num << endl;
    30. if (--ptr->use_num == 0) {
    31. delete ptr;
    32. }
    33. }
    34. private:
    35. Res_ptr* ptr;//指向计数类Res_ptr
    36. };
    37. int main() {
    38. {
    39. Shared_ptr<int>hpA = Shared_ptr<int>(new int(42));
    40. {
    41. Shared_ptr<int>hpB(hpA);
    42. Shared_ptr<int>hpC(hpB);
    43. Shared_ptr<int>hpD = hpA;
    44. }
    45. cout << "内层括号结束!" << endl;
    46. }
    47. cout << "中层括号结束!" << endl;
    48. return 0;
    49. }

     

  • 相关阅读:
    001 Creating your first app with PySide6
    轻游戏风格虚拟资源付费下载模板Discuz论坛模板
    HTML做一个简单的页面(纯html代码)地球专题学习网站
    AI内容生成时代:该如何和AI对话?
    选择器汇总
    SpringSecurity自定义多Provider时提示No AuthenticationProvider found for问题的解决方案与原理(四)
    FlyBird
    【学习笔记】CF1784F Minimums or Medians
    动态代理解决方案
    端到端图像压缩《Checkerboard Context Model for Efficient Learned Image Compression》
  • 原文地址:https://blog.csdn.net/Jason6620/article/details/126223543