• 深入C++04:模板编程


    📕模板编程

    函数模板#

    模板意义:对类型也进行参数化;

    函数模板:是不编译的,因为类型不知道

    模板的实例化:函数调用点进行实例化,生成模板函数

    模板函数:这才是要被编译器所编译的

    函数模板、模板的特例化、非模板函数(普通函数):函数模板只是一个模板,模板特例化是明确类型,还有普通函数;三者不是重载关系;

    模板代码是不可以在一个文件中定义,在另一个文件中使用的;模板代码调用之前,一定要看到模板定义的地方,这样的化,模板才能够进行正常的实例化,产生能够被编译器编译的代码;所以,模板代码都是放在头文件中,然后源文件当中直接进行#include包含(#include会展开代码)

    类模板#

    利用类模板实现的顺序栈

    Copy
    #include<iostream> #include<cstring> template<typename T> class SeqStack { public: SeqStack(int size = 10) //构造函数 : _pstack(new T[size]) , _top(0) //top一开始就指向空的地方,所以后面也是一直要指向空的地方 , _size(size) {} ~SeqStack() { //析构函数 delete []_pstack; _pstack = nullptr; } SeqStack<T>(const SeqStack<T> &stack) { //拷贝构造函数 _pstack = new T[stack._size]; _top = stack._top; _size = stack._size; for (int i = 0; i < _size; i++) { _pstack[i] = stack._pstack[i]; } } SeqStack<T> operator=(const SeqStack<T> &stack) { //赋值函数重载 if (stack == *this) return *this; delete _pstack; T *_newstack = new T[stack._size]; for (int i = 0; i < stack._size; i++) { _newstack[i] = stack._pstack[i]; } _pstack = _newstack; _size = stack._size; _top = stack._top; return *this; } void push(const T &val) { if (full()) resize(); _pstack[_top++] = val; } void pop() { if (empty()) return; --_top; } T top() const { if (empty()) throw "stack is empty!";//抛出异常也属于函数逻辑结束,不需要和返回值一致 return _pstack[_top - 1]; } bool full() const { return _top == _size; } bool empty() const { return _top == 0; } private: T *_pstack; int _top; int _size; void resize() {//扩容函数 T *newstack = new T[_size * 2]; for (int i = 0; i < _size; i++) { newstack[i] = _pstack[i]; } _size = _size * 2; delete []_pstack; _pstack = newstack; } }; int main() { SeqStack<int> test; for (int i = 0; i < 20; i++) { test.push(i); } for (int i = 0; i < 20; i++) { std::cout << test.top() << std::endl; test.pop(); } return 0; }

    实现vector模板#

    Copy
    #include<iostream> template<typename T> class vector { public: vector<T>(int size = 10) : _first(new T[size]) , _last(_first) , _end(_first + size) {} ~vector<T>() { delete []_first; _first = _last = _end = nullptr; } vector<T>(const vector<T> &other) { int size = other._end - other._first; _first = new T[size]; int len = other._last - other._first; for (int i = 0; i < len; i++) { _first[i] = other._first[i]; } _last = _first + len; _end = _first + size; } vector<T>& operator=(const vector<T> &other) { if (*this = other) return *this; delete []_first; int size = other._end - other._first; _first = new T[size]; int len = other._last - other._first; for (int i = 0; i < len; i++) { _first[i] = other._first[i]; } _last = _first + len; _end = _first + size; return *this; } void push_back(const T &val) { //向容器末尾添加元素 if (full()) resize(); *_last = val; _last++; } void pop_back() {//从容器末尾删除元素 if (empty()) return; _last--; } T back() const { //返回容器末尾元素 if (empty()) throw "no value"; return *(_last - 1); } bool full() const { return _last == _end; } bool empty() const { return _first == _last; } int size() const {return _last - _first;} private: T *_first;//数组起始位置 T *_last;//数组中有效元素的后继位置 T *_end;//数组中有效空间的后继位置 void resize() {//2倍扩容操作 int len = _end - _first; T *newvector = new T[len * 2]; for (int i = 0; i < len; i++) { newvector[i] = _first[i]; } delete []_first; _first = newvector; _last = _first + len; _end = _first + len * 2; } }; int main() { vector<int> test; for (int i = 0; i < 20; i++) { test.push_back(i); } for (int i = 0; i < 20; i++) { std::cout << test.back() << std::endl; test.pop_back(); } return 0; }

    空间配置器#

    上述vector模板的两个问题:

    ①在创建类test作为vector的对象的时候,系统默认使用了十次所存对象的构造函数(默认构造十个对象),new不仅开辟了空间,同时也生成了对象!(这样很浪费,比如用户要初始化vector一万次,实际只用一个,浪费了创建的9999个对象额外的内存和时间(内存指的是对象指向的堆内存,而存对象所需要的内存在vector里面是必须一开始就要申请的))

    ②创建了多少个对象就会析构多少次,上述创建那么多个对象,肯定也会析构那么多次,浪费时间;而如果把内存呢开辟和对象构造分开处理,析构的时候直接采用delete的话:只析构_first,那么只析构了这个指针所指的对象;而析构[] _first,析构会死循环,后面有内存但是不是对象;

    应该:

    image-20220321220725553

    ③push_back的时候,里面原本就有对象了的,只是更改了指向,原存在的对象会丢弃,造成内存泄漏

    ④pop_back的时候只是_last--,没有析构对象,对象中可能还有堆空间,会找不到的;(应该要调用对象的析构函数);但是如果单纯的使用delete,不仅不会调用析构函数析构该位置的对象,还会删除该位置的内存

    容器的空间配置器四件事情:内存开辟/内存释放,对象构造/对象析构;

    代码:

    Copy
    #include<iostream> using namespace std; //实现自己的空间配置器 template<typename T> class Allocator { //class public: T *allocate(size_t size) {//分配内存 return (T*)malloc(sizeof(T) * size); } void deallocate(T *p) {//释放内存 free(p); } void construct(T *p, const T &val) {//对象构造,明确是传入指针和引用 new (p) T(val); //定位new,指定new的区域,同时采用拷贝构造函数生成对象; } void destroy(T *p) {//对象析构 p->~T(); //~T()代表了T类型的析构函数; } }; template<typename T, typename Alloc = Allocator<T> > class vector { public: vector(int size = 10) //构造函数 : _first(_allocator.allocate(size) ) , _last(_first) , _end(_first + size) {} ~vector() { for (T *p = _first; p != _last; p++) { _allocator.destroy(p);//析构有效对象 } _allocator.deallocate(_first);//释放所有空间 _first = _last = _end = nullptr; } vector(const vector<T> &other) { //拷贝构造函数 int size = other._end - other._first; // _first = new T[size]; _allocator.allcoate(size); int len = other._last - other._first; for (int i = 0; i < len; i++) { // _first[i] = other._first[i]; _allocator.construct(_first + i, other._first[i]); //构造对象 } _last = _first + len; _end = _first + size; } vector& operator=(const vector<T> &other) { if (*this = other) return *this; // delete []_first; //和析构的一样 for (T *p = _first; p != _last; p++) { _allocator.destory(p);//析构有效对象 } _allocator.deallocate(_first);//释放所有空间 //和拷贝构造的一样 int size = other._end - other._first; int len = other._last - other._first; _allocator.allcoate(size); for (int i = 0; i < len; i++) { _allocator.construct(_first + i, other._first[i]); } _last = _first + len; _end = _first + size; return *this; } void push_back(const T &val) { //向容器末尾添加元素 if (full()) resize(); _allocator.construct(_last, val); _last++; } void pop_back() {//从容器末尾删除元素 if (empty()) return; _allocator.destroy(--_last); } T back() const { //返回容器末尾元素 if (empty()) throw "no value"; return *(_last - 1); } bool full() const { return _last == _end; } bool empty() const { return _first == _last; } int size() const {return _last - _first;} private: T *_first;//数组起始位置 T *_last;//数组中有效元素的后继位置 T *_end;//数组中有效空间的后继位置 Alloc _allocator;//定义容器空间配置器对象!!!!! void resize() {//2倍扩容操作 int len = _end - _first; // T *newvector = new T[len * 2]; T *newvector = _allocator.allocate(2 * len); for (int i = 0; i < len; i++) { // newvector[i] = _first[i]; _allocator.construct(newvector + i, _first[i]); } // delete []_first; for (T *p = _first; p != _last; p++) { _allocator.destroy(p); } _allocator.deallocate(_first); _first = newvector; _last = _first + len; _end = _first + len * 2; } }; struct Test { Test() { cout << "Test()" << endl; } ~Test() { cout << "~Test()" << endl; } Test(const Test &t) { cout << "Test(const Test&)" << endl; } Test& operator=(const Test& t) { cout << "operator=(const Test&)" << endl; } }; int main() { Test t1; Test t2; vector<Test> vec; vec.push_back(t1); vec.push_back(t2); cout << "===========================" << endl; vec.pop_back(); cout << "===========================" << endl; return 0; }

    image-20220322154051435

  • 相关阅读:
    计算机毕设(附源码)JAVA-SSM基于Java的医疗器械销售系统
    Rocketmq--消息发送和接收演示
    【学习记录】动态数组(vector)的基本操作,包括插入、删除、扩容、输出、释放内存等。用C语言编写
    Net6Configuration & Options 源码分析 Part3 IOptionsMonitor 是如何接收到配置文件变更并同步数据源的
    YOLOV7改进-具有隐式知识学习的Efficient解耦头
    数据质量校验
    云耀服务器L实例部署JumpServer堡垒机|华为云云耀云服务器L实例评测使用体验
    2022年中总结关键词:裁员、年终奖、晋升、涨薪、疫情
    纯js实现html指定页面导出word
    [1007]魔法少女小Scarlet
  • 原文地址:https://www.cnblogs.com/D-booker/p/16351308.html