C++之全特化和偏特化
全特化和偏特化全特化全特化的模板参数列表应该是为空, 函数和类都可以实现全特化.
1234567891011121314#include <iostream>using namespace std;template <class T> void fun(T a) { cout << "fun a = " << a << endl; }template <> void fun(int a) { cout << "fun1 a = " << a << endl; }int main() { fun(3.3); fun(3); exit(0);}
输出:
12fun a = 3.3fun1 a = 3
这就是函数全特化, 根据传入的参数让编译器自动推导参数的类型来调用其特殊的函数.
函数的全特化不是重载, 不是重载.
全特化的参数列表要为空, 为空
第二点成立是因为我们要实现 ...
C++之traits萃取器
traits萃取器前面我们分析了迭代器的五类, 而迭代器所指向对象的型别被称为value type. 传入参数的类型可以通过编译器自行推断出来, 但是如果是函数的返回值的话, 就无法通过value type让编译器自行推断出来了. 而traits就解决了函数返回值类型. 同样原生指针不能内嵌型别声明,所以内嵌型别在这里不适用, 迭代器无法表示原生指针(int *, char *等称为原生指针). 这个问题就通过traits偏特化技术解决的. 这一篇我们就主要探讨traits是怎么实现这些没有能解决的问题.
iterator_traits结构iterator_traits结构体就是使用typename对参数类型的提取(萃取), 并且对参数类型在进行一次命名, 看上去对参数类型的使用有了一层间接性. 以下就是它的定义.
12345678template <class Iterator>struct iterator_traits { typedef typename Iterator::iterator_category iterator_category; //迭 ...
C++之typename与class
typename与class相同之处一般对模板参数类型typename和class认为是一样的. 这两者在参数类型中确实是一样的. 你可以写成
12template<class T> class point {};
也可以写成
12template<typename T>class point {};
这两者都是一样的, 没有区别. 两者typename和class在参数类型中没有不同
既然相同又为什么定义这两个符号呢?
最开始定义定义模板的方法就是template , 但是class毕竟都认为是一个类, 在使用时难免会有些点混淆, 也就定义了typename来标志参数类型
最重要关于 typename可以使用嵌套依赖类型, 也就是类型可以嵌套使用. 这也是两个的不同之处.
不同之处typename可以用在嵌套依赖中, 并且表示其类型, 而class并没有这样的功能.
什么是嵌套依赖? 我们以一个简单的实例来看
1234567891011121314151617181920212223242526272829tem ...
C++之迭代器
迭代器迭代器是将算法和容器两个独立的泛型进行调和的一个接口. 使我们不需要关系中间的转化是怎么样的就都能直接使用迭代器进行数据访问. 而迭代器最重要的就是对operator *和operator->进行重载, 使它表现的像一个指针.
类型迭代器根据移动特性和实施操作被分为5类
input iterator(输入迭代器) : 迭代器所指的内容不能被修改, 只读且只能执行一次读操作.
output iterator(输出迭代器) : 只写并且一次只能执行一次写操作.
forward iterator(正向迭代器) : 支持读写操作且支持多次读写操作.
bidirectional iterator(双向迭代器) : 支持双向的移动且支持多次读写操作.
random access iterator(随即访问迭代器) : 支持双向移动且支持多次读写操作. p+n, p-n等.
源码分析category的五类迭代器以及继承关系
12345struct input_iterator_tag {};struct output_iterator_tag { ...
C++之空间配置器
空间配置器“new”的实现这里直接我们直接来看STL的construct实现吧
123456// 这里的construct调用的是placement new, 在一个已经获得的内存里建立一个对象template <class T1, class T2>inline void construct(T1* p, const T2& value){ new (p) T1(value);}
可以明白这里就只是一个placement new的调用, 只是用了泛型来实现一个对象分配的模板, 并实现初始化.
既然已经看到了对象的分配, 那再接再厉看看空间的分配, 充分了解STL是怎么将new分开执行的. allocate函数实现空间的申请, 但是这里有一点看不出来, 申请内存是有分为一级配置器和二级配置器, 分配的空间小于128字节的就调用二级配置器, 大于就直接使用一级配置器, 一级配置器直接调用malloc申请, 二级使用内存池.
123456789template <class T> inline T *allocate(ptrdiff_ ...
C++空间构造器的内存池
内存池static data template的初始化123456template <bool threads, int inst>char *__default_alloc_template<threads, inst>::start_free = 0; // 内存池的首地址template <bool threads, int inst>char *__default_alloc_template<threads, inst>::end_free = 0; // 内存池的结束地址template <bool threads, int inst>size_t __default_alloc_template<threads, inst>::heap_size = 0; // 多次调用内存池, 就会更多的是给链表分配内存, 这就是一个增量.
内存池的大小大于需要的空间, 直接返回起始地址(nobjs默认设置为20, 所以每次调用都会给链表额外的19个内存块)
内存池的内存不足以马上分配那么多内存, 但是还能满足 ...
C++之二级配置器
二级配置器第一级是直接调用malloc分配空间, 调用free释放空间, 第二级三就是建立一个内存池, 小于128字节的申请都直接在内存池申请, 不直接调用malloc和free. 本节分析第二级空间配置器, STL将第二级配置器设置为默认的配置器, 所以只要一次申请的空间不超过128字节就默认在内存池中申请空间, 超过才会调用第一级配置器.
首先先来介绍3个常量
__ALIGN: 以8字节进行对齐
__MAX_BYTES : 二级分配器最大分配的内存大小
__NFREELISTS : 128字节能分配的的链表个数, 并且从每个链表保存的内存大小都是8的倍数, 而且都比前一个大8字节, 也就是分别是8, 16, 32…128字节
1234// 二级配置器enum {__ALIGN = 8}; // 设置对齐要求. 对齐为8字节, 没有8字节自动补齐enum {__MAX_BYTES = 128}; // 第二级配置器的最大一次性申请大小, 大于128就直接调用第一级配置器enum {__NFREELISTS = __MAX_BY ...
C++之一级配置器
一级配置器一级配置器的类. 它无template型别参数. 这里我将public定义的函数和私有成员函数成对分离出来讲解
1234567891011121314// 一级配置器template <int inst> class __malloc_alloc_template { // 这里private里面的函数都是在内存不足的时候进行调用的private: static void *oom_malloc(size_t); // 分配不足 static void *oom_realloc(void *, size_t); // 重新分配不足#ifndef __STL_STATIC_TEMPLATE_MEMBER_BUG static void ( *__malloc_alloc_oom_handler)(); // 内存不足设置的处理例程, 默认设置的是0, // 表示没有设置处理例程, ...
C++之new实现
new实现new operator用法其实new operator我们经常在使用, 就是我们直接向堆申请一块内存大小, 然后对该内存进行构造和析构.
12345template <class T> class point { T x; T y;};point<int> *p = new point<int>[3];
这就是new operator的用法. 其实在使用它的时候, 它会做两步事情.
向堆申请一块大小的内存.
对其有构造函数的执行构造函数
其实剩下的两种用法就是将 new operator 的两个功能分开做.
operator new用法operator new申请一块空间, 但是申请完了就什么都不做. 这感觉就很像malloc函数啊. 对, 没错. 其实operator new就是间接性的调用了 malloc函数. 我们直接来看源码部分
123456789101112131415void *__CRTDECL operator new(size_t const size) { for (;;) & ...
C++之template之非类型模板参数
template之非类型模板参数非类型参数, 可用在模板中自定义为整型类型, 指针或引用, 不能定义为浮点数等其他类型.
非类型模板参数在编译期间就已经实例化, 所以其模板实参必须是常量表达式
12template<int N>; // N是编译时就确定的常量表达式template<size_t N, size_t M>; // N,M是编译时就确定的常量表达式
可能就是会觉得没有用, 毕竟使用模板就是要用他的模板参数类型啊, 没有这个那还怎么用. 这里就来先看一个例子.
要求: 实现一个函数返回一个数组的真实大小, 如 : int a[100]; ArrSize(a);返回100
讲道理传入函数中a就转换为指针了, 怎么用指针能获取其表示范围? 这里就要用到template的非类型参数
1234567891011#include <iostream>// 这里的N是编译时期就知道了, 所以可以加上constexpr关键字template <class T, std::size_t N>constexpr std::size_t ...



