vector容器部分源码实现
2016-05-22 15:15
387 查看
STL中vector部分源码实现
本次作业要求自己模仿实现STL中vector部分函数
为了检测内存的管理机制是否是像源码一样,额外写了一个test类,通过输出来检测是否一样。
test代码如下:
为了防止一些人投机取巧使用#define myVector std::vector
所以额外写出了一个类base来防止这种情况
在类myVector中要继承自定义的Base,这样就可以防止#define myVector std::vector的情况
因为STL的vector是不可能继承自自定义的Base的,在测试函数中有一句是检查是否继承自Base
接下来放上具体代码,并解释其中一些写的过程中觉得比较难懂的知识点
下面是测试函数:
最后,说明一下在写的过程中个人比较迷惑最后弄懂了的知识点:
首先,一般来说出了作用域所有变量都会被析构。
而临时对象被析构的时间比较早,是在创建它的语句执行完之后,立刻被析构!
比如上面函数中的Alloc().allocate(),Alloc()是一个类的默认构造函数,它创建一个对象并且使用了类的成员函数allocate()。
执行完这个语句之后,这个刚刚被创建的对象就会被销毁。所以上面的代码不会出现类Alloc的对象堆积如山的情况。
为什么上面的代码要这么写呢?因为我们需要用到testAllocator这个类中的函数,又不想声明太多的testAllocator对象(很浪费),所以采用了临时变量的方法。
至于如何判断临时对象,一个简单的方法是看看它被构造之后有没有引用指向它,即,构造的时候有没有左值!
比如下面两行:
temp = Alloc();这里创建的不是一个临时变量,所以temp的析构是发生在退出作用域时。
Alloc();这个创建了一个临时变量,在执行完该语句之后立刻被析构。(调用析构函数~Alloc())
还有一种情况是for语句,如:
for (int i = 0; i < 5; ++i) {}
在for语句执行完之后,临时变量i即被销毁。
下面是两个相关的例子,感受一下其中细微的差别:
本次作业要求自己模仿实现STL中vector部分函数
为了检测内存的管理机制是否是像源码一样,额外写了一个test类,通过输出来检测是否一样。
test代码如下:
#ifndef __TEST_H__ #define __TEST_H__ #include <memory> #include <iostream> class testAllocator : public std::allocator<int> { public: //这个函数用来申请内存,但是并不在上面创建新的变量,其中size是要申请的内存大小 typedef std::allocator<int> Base; int * allocate(std::size_t size) { std::cout << "Test Allocator: allocate" << std::endl; return Base::allocate(size); //调用了memory库中的allocate函数,下面同理 } //这个函数用来释放内存,并不涉及调用在这片内存上面变量的析构函数,其中p是这片内存的首地址,size是这片地址的大小 void deallocate(int * p, std::size_t size) { std::cout << "Test Allocator: deallocate" << std::endl; Base::deallocate(p, size); } //在已经申请了的内存上面创建一个新的变量,其中p是一个指针,指向我们要创建新的变量的地址,val是变量的值 void construct(int * p, int val) { std::cout << "Test Allocator: Construct, value: " << val << std::endl; Base::construct(p, val); } //在指针p指向的内存上,销毁掉变量,但是并不释放内存 void destroy(int * p) { std::cout << "Test Allocator: Destroy, value: " << *p << std::endl; Base::destroy(p); } }; #endif
为了防止一些人投机取巧使用#define myVector std::vector
所以额外写出了一个类base来防止这种情况
#ifndef __BASE_H__ #define __BASE_H__ #define vector NOT_ALLOWED #define define NOT_ALLOWED class Base {}; #endif
在类myVector中要继承自定义的Base,这样就可以防止#define myVector std::vector的情况
因为STL的vector是不可能继承自自定义的Base的,在测试函数中有一句是检查是否继承自Base
接下来放上具体代码,并解释其中一些写的过程中觉得比较难懂的知识点
// // vector.h: // C++ // // Created by 舒俊淮 on 16/5/22. // Copyright © 2016年 Shujh. All rights reserved. // #ifndef __VECTOR_H__ #define __VECTOR_H__ #define A Alloc() #include "base.h" #include "memory" #include "test.h" using namespace std; /*typename Alloc = std::allocator<T>这是一个模板变量,类型名叫Alloc,接受分配器,如这里的testAllocator “= std::allocator<T>”的意思是,默认调用T类型的allocator,比如写的是myVector<int>,没有提供第二个参数,就使用默认值*/ template< typename T, typename Alloc = std::allocator<T> > class myVector : public Base { public: typedef T* iterator; typedef const T* const_iterator; myVector() { /*这里用A.allocate()而不是直接allocate()的原因是allocate是一个类中的公共函数,不能直接调用 A.allocate()其实就是Alloc().allocate()先用Alloc()这个默认构造函数构造一个临时对象,之后在调用它的成员函数allocate() 这种做法是可行的,而且因为创建的是临时变量,这种做法是合理的*/ _data = A.allocate(1); _capacity = 1; _size = 0; } myVector(const size_t &n, const T &ele, Alloc _A = Alloc()) { //Alloc在这个例子被实例化成testAllocator,Alloc _A = Alloc()就是创建一个testAllocator对象 _data = _A.allocate(n); _capacity = n; for (int i = 0; i < n; ++i) _A.construct(_data + i, ele); _size = n; } template<typename InputIterator> myVector(InputIterator begin, InputIterator end, Alloc _A = Alloc()) { _size = end - begin; _data = _A.allocate(_size); for (int i = 0; i < _size; ++i) _A.construct(_data + i, *(begin + i)); _capacity = _size; } myVector(const myVector &other) { _size = other._size; _capacity = other._capacity; _data = A.allocate(_capacity); for (int i = 0; i < _size; ++i) A.construct(_data + i, *(other._data + i)); } ~myVector() { if (_data) { for (int i = 0; i < _size; ++i) A.destroy(_data + i); A.deallocate(_data, _capacity); } } myVector & operator=(const myVector &other) { if (other._data != _data) { if (_data) { for (int i = 0; i < _size; ++i) A.destroy(_data + i); A.deallocate(_data, _capacity); } _size = other._size; _capacity = other._capacity; _data = A.allocate(_capacity); for (int i = 0; i < _size; ++i) A.construct(_data + i, *(other._data + i)); } return *this; } iterator begin() {return _data;} const_iterator begin() const {return _data;} iterator end() {return _data + _size;} const_iterator end() const {return _data + _size;} // Capacity size_t size() const {return _size;} void resize(const size_t &num) { if (num > _size) { if (num > _capacity) { int new_cap = _capacity * 2; while (new_cap < num) new_cap *= 2; reserve(new_cap); } for (int i = _size; i < num; ++i) A.construct(_data + i, T()); _size = num; } else { for (int i = num; i < _size; ++i) A.destroy(_data + i); _size = num; } } void resize(const size_t &num, const T &n) { if (num > _size) { if (num > _capacity) { int new_cap = _capacity * 2; while (new_cap < num) new_cap *= 2; reserve(new_cap); } for (int i = _size; i < num; ++i) A.construct(_data + i, n); _size = num; } else { for (int i = num; i < _size; ++i) A.destroy(_data + i); _size = num; } } size_t capacity() const {return _capacity;} bool empty() const {return _size == 0 ? true : false;} void reserve(const size_t &newCap) { if (newCap > _capacity) { T* tmp = A.allocate(newCap); for (int i = 0; i < _size; ++i) { A.construct(tmp + i, _data[i]); A.destroy(_data + i); } A.deallocate(_data, _capacity); _capacity = newCap; _data = tmp; } } // Element Access T & operator[](const size_t &index) { if (index < _size) return *(_data + index); } const T & operator[](const size_t &index) const { if (index < _size) return *(_data + index); } T & front() {return *(_data);} const T & front() const {return *(_data);} T & back() {return *(_data + _size - 1);} const T & back() const {return *(_data + _size - 1);} T * data() {return _data;} const T * data() const {return _data;} // Modifiers template<typename InputIterator> void assign(InputIterator begin, InputIterator end) { clear(); _size = end - begin; if (_size < _capacity) { for (int i = 0; i < _size; ++i) A.construct(_data + i, *(begin + i)); } else { A.deallocate(_data, _capacity); _capacity = _size; _data = A.allocate(_capacity); for (int i = 0; i < _size; ++i) A.construct(_data + i, *(begin + i)); } } void assign(const size_t &size, const T &n) { clear(); _size = size; if (_size < _capacity) { for (int i = 0; i < _size; ++i) A.construct(_data + i, n); } else { A.deallocate(_data, _capacity); _capacity = _size; _data = A.allocate(_capacity); for (int i = 0; i < _size; ++i) A.construct(_data + i, n); } } void push_back(const T &n) { if (_size < _capacity) { A.construct(_data + _size, n); _size++; } else { int N = _size; int arr ; for (int i = 0; i < _size; ++i) arr[i] = *(_data + i); A.deallocate(_data, _capacity); _capacity *= 2; _data = A.allocate(_capacity); for (int i = 0; i < _size; ++i) A.construct(_data + i, arr[i]); A.construct(_data + _size, n); _size++; } } void pop_back() { if (_size) { A.destroy(_data + _size - 1); _size--; } } void clear() { if (_size) { for (int i = 0; i < _size; ++i) A.destroy(_data + i); _size = 0; } } private: iterator _data; size_t _size, _capacity; }; #endif
下面是测试函数:
#include <iostream> #include "test.h" #include "base.h" #include "vector.h" int main() { typedef myVector<int, testAllocator> v; //这个函数,利用一个Base指针来检查myVector是否继承自Base,如若不是,那么便过不了编译 Base * test = new v; delete static_cast<v *>(test); v * p1, * p2; int t; std::cout << "Test Constructor1:" << std::endl; p1 = new v; std::cout << "Size: " << p1->size() << std::endl; delete p1; std::cout << "Test Constructor2 and operator[]:" << std::endl; p1 = new v(static_cast<std::size_t>(6), 6); std::cout << "Size: " << p1->size() << std::endl; std::cout << "Content:"; for (int i = 0; i < 2; ++i) std::cout << ' ' << (*p1)[i]; std::cout << std::endl; std::cin >> t; std::cout << "Content after change:"; (*p1)[0] = t; const v & r(*p1); for (int i = 0; i < 2; ++i) std::cout << ' ' << r[i]; std::cout << std::endl; std::cout << "Test Constructor3 and iterators, including begin(), end():" << std::endl; p2 = new v(r.begin(), r.end()); delete p1; std::cout << "Content:"; for (v::iterator it = p2->begin(); it != p2->end(); ++it) std::cout << ' ' << *it; std::cout << std::endl; std::cout << "Test Constructor4:" << std::endl; *(p2->begin()) = 0; p1 = new v(*p2); delete p2; std::cout << "Content:"; for (std::size_t i = 0; i < p1->size(); ++i) std::cout << ' ' << (*p1)[i]; std::cout << std::endl; std::cout << "Test operator=:" << std::endl; p2 = new v(static_cast<std::size_t>(8), 8); *p2 = *p1; *p2 = *p2; delete p1; std::cout << "Content:"; for (std::size_t i = 0; i < p2->size(); ++i) std::cout << ' ' << (*p2)[i]; std::cout << std::endl; std::cout << "Test resize1:" << std::endl; p2->resize(2); std::cout << "Content:"; for (std::size_t i = 0; i < p2->size(); ++i) std::cout << ' ' << (*p2)[i]; std::cout << std::endl; std::cout << "Test resize2:" << std::endl; p2->resize(8, 8); std::cout << "Content:"; for (std::size_t i = 0; i < p2->size(); ++i) std::cout << ' ' << (*p2)[i]; std::cout << std::endl; std::cout << "Test reserve and capacity:" << std::endl; p2->reserve(33); std::cout << "Capacity: " << p2->capacity() << std::endl << "Size: " << p2->size() << std::endl; p2->reserve(2); std::cout << "Capacity: " << p2->capacity() << std::endl << "Size: " << p2->size() << std::endl; std::cout << "Test clear and empty:" << std::endl; if (p2->empty()) std::cout << "True" << std::endl; else std::cout << "False" << std::endl; p2->clear(); if (p2->empty()) std::cout << "True" << std::endl; else std::cout << "False" << std::endl; std::cout << "Capcaity: " << p2->capacity() << std::endl << "Size: " << p2->size() << std::endl; int * arr = new int[5]; for (int i = 0; i < 5; ++i) arr[i] = i+1; std::cout << "Test assign:" << std::endl; p2->assign(arr, arr+5); std::cout << "Content:"; for (v::const_iterator it = p2->begin(); it != p2->end(); ++it) std::cout << ' ' << *it; std::cout << std::endl << "Size: " << p2->size() << std::endl << "Capacity: " << p2->capacity() << std::endl; delete [] arr; delete p2; return 0; }
最后,说明一下在写的过程中个人比较迷惑最后弄懂了的知识点:
首先,一般来说出了作用域所有变量都会被析构。
而临时对象被析构的时间比较早,是在创建它的语句执行完之后,立刻被析构!
比如上面函数中的Alloc().allocate(),Alloc()是一个类的默认构造函数,它创建一个对象并且使用了类的成员函数allocate()。
执行完这个语句之后,这个刚刚被创建的对象就会被销毁。所以上面的代码不会出现类Alloc的对象堆积如山的情况。
为什么上面的代码要这么写呢?因为我们需要用到testAllocator这个类中的函数,又不想声明太多的testAllocator对象(很浪费),所以采用了临时变量的方法。
至于如何判断临时对象,一个简单的方法是看看它被构造之后有没有引用指向它,即,构造的时候有没有左值!
比如下面两行:
temp = Alloc();这里创建的不是一个临时变量,所以temp的析构是发生在退出作用域时。
Alloc();这个创建了一个临时变量,在执行完该语句之后立刻被析构。(调用析构函数~Alloc())
还有一种情况是for语句,如:
for (int i = 0; i < 5; ++i) {}
在for语句执行完之后,临时变量i即被销毁。
下面是两个相关的例子,感受一下其中细微的差别:
相关文章推荐
- 从源码安装Mysql/Percona 5.5
- 【DevOps】为什么我们永远疲于奔命?
- 网络管理之IP地址篇
- IE7降低内存和降低CPU的几个技巧
- 文件的读出 编辑 管理
- 如何高效的使用内存
- DOS下内存的配置
- 浅析Ruby的源代码布局及其编程风格
- XP/win2003下发现1G的内存比512M还慢的解决方法
- SQL Server 2008 R2 应用及多服务器管理
- PowerShell实现动态获取当前脚本运行时消耗的内存
- C#实现把dgv里的数据完整的复制到一张内存表的方法
- SQL语句实现查询SQL Server内存使用状况
- C语言内存对齐实例详解
- 浅析STL中的常用算法
- STL区间成员函数及区间算法总结
- 深入学习C语言中memset()函数的用法
- VC下通过系统快照实现进程管理的方法
- 全局变量与局部变量在内存中的区别详细解析
- VB读取线程、句柄及写入内存的API代码实例