C++ 学习7 深入STL 1

    xiaoxiao2021-03-25  129

    OOP VS GP分配器容器之间实现关系与分类深入List迭代器设计原则与iterator traits作用

    深入Vector,Deuqe,Array

    OOP VS GP

    OOP 将Data 和method 结合在一起。 OOP 设计原则

    开闭原则 (open-close Principle, OCP) 一个软件应该对扩展开放,对修改关闭,即在原基础上可扩展不可修改。保证程序执行的一致性。这也是OOD的基石。里氏替换原则 (Liskov Substitution Principle. LSP)应当尽量从抽象基类继承而不从具体类继承。一般而言,如果有两个具体类A,B有继承关系,那么一个最简单的修改方案是建立一个抽象类C,然后让类A和B成为抽象类C的子类.即如果有一个由继承关系形成的登记结构的话,那么在等级结构的树形图上面所有的树叶节点都应当是具体类;而所有的树枝节点都应当是抽象类或者接口. 3.依赖倒置原则(Dependence Inversion Principle, DIP)抽象不应依赖细节,细节应当依赖抽象。一个具体的类应等只实现接口和抽象类中声明过的方法,而不应当给出多余的方法.联合使用接口和抽象类: 由于抽象类具有提供缺省实现的优点,而接口具有其他所有优点,所以联合使用两者就是一个很好的选择. 首先,声明类型的工作仍然接口承担的,但是同时给出的还有一个抽象类,为这个接口给出一个缺省实现.其他同属于这个抽象类型的具体类可以选择实现这个接口,也可以选择继承自这个抽象类.如果一个具体类直接实现这个接口的话,它就必须自行实现所有的接口;相反,如果它继承自抽象类的话,它可以省去一些不必要的的方法,因为它可以从抽象类中自动得到这些方法的缺省实现;如果需要向接口加入一个新的方法的话,那么只要同时向这个抽象类加入这个方法的一个具体实现就可以了,因为所有继承自这个抽象类的子类都会从这个抽象类得到这个具体方法.这其实就是缺省适配器模式(Defaule Adapter).接口隔离原则(Interface Segregation Principle)(1)一个类对另外一个类的依赖是建立在最小的接口上。 (2)使用多个专门的接口比使用单一的总接口要好.根据客户需要的不同,而为不同的客户端提供不同的服务是一种应当得到鼓励的做法.就像”看人下菜碟”一样,要看客人是谁,再提供不同档次的饭菜. (3)胖接口会导致他们的客户程序之间产生不正常的并且有害的耦合关系.当一个客户程序要求该胖接口进行一个改动时,会影响到所有其他的客户程序.因此客户程序应该仅仅依赖他们实际需要调用的方法. 5.合成/聚合复用原则)(Composite/Aggregate reuse Principle, CARP) 在一个新的对象里面使用一些已有的对象,使之成为新对象的一部分;新的对象通过这些向对象的委派达到复用已有功能的目的.这个设计原则有另一个简短的表述:要尽量使用合成/聚合,尽量不要使用继承. 6.最少知识原则(Least knowledge Principle),如果两个类不必彼此直接通信,那么这两个类就不应当发生直接的相互作用,如果其中的一个类需要调用另一个类的某一个方法的话,可以通过第三者转发这个调用。 7.单一职责原则(Simple Responsibility Principle. SRP)就一个类而言,应该仅有一个引起它变化的原因,如果你能想到多于一个的动机去改变一个类,那么这个类就具有多于一个的职责.应该把多于的指责分离出去,分别再创建一些类来完成每一个职责. 参考: http://blog.csdn.net/anders_zhuo/article/details/8949566

    分配器 Allocator

    In VC STL

    template <class _Ty, class _A = allocator<_Ty>> class vector{...}; template<class _Ty, class _A = allocator<_Ty>> list {...}; template<class _Ty, class _A = allocator<_Ty>> deque{...}; template<class _Ty, class _A = allocator<_Ty>> set{...}; template<class _Ty> class allocator { public: typedef _sizT size_type; typedef _PDFT difference_type; typedef _Ty _FARQ *pointer; typedef _Ty value_type; Pointer allocate(size_type _N, const void*) {return (_Allocate((difference_type)_N, (Pointer) 0));} void deallocate (void _FARQ *_P,size_type) {operator delete(_P);} }; ... #ifndef _FARQ #define _FARQ #define _PDFT ptrdiff_t #define _SIZT size_t #endif template<class _Ty> inline _Ty _FARQ *_Allocate(_PDFT _N,_Ty _FARQ*) {if (_N < 0) _N = 0; return ((_Ty _FARQ *) operator new ((_SIZET)_N * sizeof(_Ty)));}

    operator new /delete 分别调用 malloc/free GCC2.9 中容器的默认分配器为alloc 其实现为内存池GCC4.9 改为上述。alloc重命名为 _pool_alloc<_class _Tp > sizeof(std::allocator) = 1 sizeof(__gnu_cxx::array_allocator) = 8 含有一个ptr指向array和一个size_t

    容器结构与分类

    Array,vector,Deque均是连续存储空间有RandomAccessIterator 可以operator [] List /Forwaord——list 是链表结构set/multiset,map/multimap 是树型结构一般由红黑树实现。 unsorted set/multiset, unsorted Map/Multimap :由hashtable 实现。

    深入List

    sizeof(std::list) = 8

    template<class T> struct __list_node { typedef void* void_pointer; void_pointer prev; void_pointer next; T data; }; template <class T, class Alloc = alloc> class list{ protected: typedef __list_node<T> list_node; public: typedef list_node* link_type; typedef __list_iterator<T,T&,T*> iterator; protected: link_type node; ... }; template<class T, class Ref,class Ptr> struct __list_iterator { typedef T value_type; typedef Ptr pointer; typedef Ref reference; typedef __list_iterator<T, Ref, Ptr> self; typedef __list_node<T*> link_type typedef ptrdiff_t difference_type; link_type node refernce operator*() const {return (*node).data;} pointer operator->()const {return &(operator*());} self& operator++() { node = (link_type)((*node).next); return *this;} self operator++(int) {self tmp = *this;++*this;return tmp;} ... };

    可见list 有一个双向链表构成。双向链表的结尾有一个空白节点用于保证[..) 特性end() 迭代器指向此位置。当你对某个type实施operator->,而该type 不是built-in ptr时,编译器会找出user-defined 哦perator-〉并将它施行于该type之后,一直如此迭代直到触及 a pointer to a built-in type 然后进行成员存取。

    Iterator设计原则与Iterator Traits

    Iterator作为连接算法与容器的纽带 Iterator 需要回答算法三个问题 1. iterator_category() 2. difference_type 3. value_type 其实按C++标准还有两个reference 和Pointer

    template <class _Tp> struct __list_iterator { typedef std::bidirectional_iterator_tag iterator_category; typedef _Tp value_type; typedef _Tp* pointer; typedef _Tp& reference; typedef ptrdiff_t difference_type; ... }; template<typename I> inline void algorith(i first, i last) {... I::iterator_category I::Pointer I::reference I::value_type I::difference_type ...}

    可见对于一个通用算法 需要知道上述5 个问题。 一般来讲iterator 为class时都可以直接提取。但是当iterator 不是一个类时就需要一个中间层来来萃取这5个特征 即iterator_trait

    template <calss I> struct iterator_traits { typedef typename I:: value_type value_type; .... } ; 偏特化1. tempalte <class T> struct iterator_traits<T*> { typedef T value_type; ... }; 偏特化2. template <class T> struct iterator<const T*> { typdef T value_type;//此处是T而不是const T原因是此处typedef主要用于变量声明若为const则没有什么地方可以用。 ... }; //于是当需要知道 I 的value_type 时可以: template<typename I,...> void algorithm(...) { typename iterator_traits<I>::value_type v1; } //完整的iterator_traits template <class I> struct iterator_traits { typedef typename I::iterator_category iterator_category; typedef typename I::value_type value_type; typedef typename I::difference_type difference_type; typedef typename I::pointer pointer; typedef typename I ::reference reference; }; //partial for regular pointers template<class T> struct iterator_traits<T> { typedef random_access_iterator_tag iterator_category; typedef T value_type; typedef ptrdiff_t difference_type; typedef T* pointer; typedef T& reference; }; //partial speciallization for regular const pointers template <class T> struct iterator_traits<const T*> { typedef random_access_iterator_tag iterator_category; typedef T value_type; typedef ptrdiff_t difference_type; typedef const T* pointer; typedef const T& reference; };

    深入vector,deque, array

    1.vector:容器内有三个迭代器分别指向存储空间的start,finish,end_of_storage 当finish增长到>= end_of_strorage 出发存储空间2倍增长,原空间中数据复制到新空间。

    template<class T, Alloc = alloc> class vector { public: typedef T value_type; typedef value_type* iterator;//T* typedef value_type& reference; typedef size_t size_type; protected: iterator start; iterator finish; iterator end_of_storage; public: iterator begin() {return start;} iterator end() {return finish;} iterator size() const {return end() - start();} size_type capacity() coanst {return size_type(end_of_storage - begin());} bool empty() const {return begin() == end();} reference operator[] (size_type n) {return *(begin() + n);} reference front() {return *begin();} reference back() {return *(end() - 1);} void push_back(const T& x) { if(finish != end_of_storage){ construct(finish, x); ++finish; }else inset_aux(end(),x); } ... }; template<classclass Alloc> void vector<T, Alloc>::insert_aux(iterator position, const T& x ) { ... if(finish ! = end_ofstorage) construct(finish,*(finish -1)); ++finish; T x_copy = x; copy_backward(position, finish -2, finish - 1); *posotion = x_copy; } else { const size_type old_size = size(); const size_type len = old_size != 0?2*old_size:1; iterator new_start = data_allocator::allocate(len); iterator new_finish = new_start; try { //将原vector内容copy到新vector new_finish = uninitialized_copy(start, position, new_start); construct(new_finish,x); //为新元素设初值x ++new_finish; //copy安插点后面的内容 new_finish = unintialized_copy(position,finish, new_finish); } catch(...) { //rool_back destroy(new_start, new_finish); data_allocator::deallocate (new_start, len)l throw; } destroy(begin(), end()) deallocate(); start = new_start; finish = new_finish; end_of_storage = new_start + len; };

    array: 有固定的空间不可增长

    template<typename _Tp, std::size_t _Nm> struct array { typedef _Tp value_type; typedef _Tp* pointer; typedef value_type* iterator; value_type _M_instance[_Nm?_Nm:1]; iterator begin() {return iterator(&_M_instance[0]);} iterator end() {return iterator (&_M_instance[_Nm]);} }

    deque: 由一个简单的map构成, 一个可双向扩充数组,数组中美一个元素存储一个buffer地址。每一个buffer指向一个连续存储空间。

    template<class T, class Alloc = alloc, size_t BufSize = 0> class deque { public: typedef T value_type; typedef __deque_iterator<T,T&,T*,Buf_size> iterator; protected: iterator start; iterator finish; map_pointer map; size_type map_size; public: iterator begin() {return start;} iterator end() {return finish;} size_type size() const {return finish - start;} ... }; inline size_t __deque_buf_size(size_t n ,size_t sz) { return n! = 0?n:(sz<512?size_t(512/sz):size_t(1));//n不为0则传回n表示bufsize由用户自定否则判断是否小于512,不小于则返回1。 } template<class T class ref,class ptr, size_t BufSize> struct __deque_iterator { typedef random_access_iterator_tag itertaor_category; typedef T value_type; typedef Ptr pointer; typedef Ref reference; typedef size_t size_type; typedef ptrdiff_t difference_type; type T** map_pointer; typedef __deque_iterator self; T* cur; T* first; T* last; map_pointer node; ... };

    stack,和queue 都是由deque adaptor.着俩容器不提供iterator不允许遍历。stack可选vector, list,deque 作为底层元素。 queue不能选vector作为底层结构。 stack和queue 都不可选择set/map作为底层结构。

    转载请注明原文地址: https://ju.6miu.com/read-10506.html

    最新回复(0)