C++笔记十(STL与泛型编程)

本周内容
(1)一个万能的hash function
(2)Tuple用例
(3)Type traits以及traits 实现
(4)cout
(5)moveable元素对于容器速度效能的影响

一 一个万用的hash function

  • 但使用以hash table为底层的容器时,遇到一个难题,必须要为放的元素写一个hash function。正如东西是由原子分子组成一样,在计算机编程里面,所有的data也是由原始的整数、浮点数、字符串组成的,基本的数值型这些本身都有hash function,他们的hash function传回来的就是自己。有没有可能把一个设计出来的数据结构,一个元素,把它拆分开来,然后把它各自的hash code(hash function产生hash code)加起来,就变成这个元素的hash code,可以不可以这样做呢,好像言之成理。所谓的hash function是将将产生的hash code做到越乱越好,不要重复。
  • 下一段代码是hash function的写法形式:
#include
class Customer {
...
};
//-------------------------形式1成员函数---------------------
class CustomerHash
{
public:
    std::size_t operator()(const Customer& c) const {
    return...
};
//-----------------------------------------------------------
unordered_set custset;
//形式1模板参数只需要填元素类型和函数类型就可以,自动产生函数对象被调用
//-------------------------形式2一般函数---------------------
size_t customer_hash_func(const Customer& c){
    return...
};
//-----------------------------------------------------------
unordered_set
custset(20,customer_hash_func);
//形式2模板参数需要填元素类型和函数类型,创建容器时候还要把真正的函数地址放进来
  • 将每个hash code相加太过天真:元素容易碰撞,每个篮子挂的元素多,查找慢。从TR1开始有了下面1234帮你写hash function。typename...表示可以接受任意多的模板参数。
  • 借由variadie templates这种语法,可以放任意元素个数,任意类型的参数。
  • 9e3779b9是黄金比例的应用。


    C++笔记十(STL与泛型编程)_第1张图片
    一个万用的hash function.png
  • 还有第三种形式实现,那就是通过对自己的元素作一个偏特化版本实现hash function。G4.9以后有了string的偏特化版本。


    C++笔记十(STL与泛型编程)_第2张图片
    偏特化实现hash function.png

二 tuple用例

  • C++11中的tuple(元组)是一个固定大小的不同类型值的集合,是泛化的std::pair。我们可以把他当做一个通用的结构体来用,不需要创建结构体又获取结构体的特征,在某些情况下可以取代结构体使程序更简洁,直观。tuple可以指定任意类型元素。
  • 通过继承自己(尾部的部分)实现递归。
    GNC4.8 tuple实作如下:


    C++笔记十(STL与泛型编程)_第3张图片
    tuple实作.png

    tuple用例如下:


    C++笔记十(STL与泛型编程)_第4张图片
    tuple用例.png

三 Type traits以及traits 实现

  • G2.9的type traits写的非常简单,泛化版本(默认)六个typedef,拷贝构造、析构函数等都是重要的,意味着如果要拷贝一百万个数据,要调用一百万个拷贝构造函数、析构函数。如果你写出一个类型,知道那个类型不重要,那就可以为此写一个特化版本,说这些函数都不重要。
  • 设计一个复数,有实部和虚部,不必为他写析构函数、拷贝构造函数、拷贝赋值函数,因为不写的话编译器有一个默认版本,那个版本所作的功能已经够了,复数的拷贝是一个位一个位拷贝,不需要特别做事情,不需要写析构函数,因为他马上就“死掉了”。复数没有指针,这些都不重要。


    C++笔记十(STL与泛型编程)_第5张图片
    type traits.png

    以下是type traits测试:


    C++笔记十(STL与泛型编程)_第6张图片
    type traits测试.png
  • 一个类带有指针一定要写析构函数,不带指针多半不用写。
  • 以下是测试结果,将string丢到里面,右边是一些类型的反馈,1代表是,0代表不是。


    C++笔记十(STL与泛型编程)_第7张图片
    type traits测试.png
  • 多态(polymorphic)类是一种有声明或者继承了虚函数的类。
  • 新版本不用像旧版本一样还要写偏特化版本来配合它。

那么type traits是怎么实现的呢,以is_void为例,它先要将const和volatile(多线程用到,易挥发)拿掉,用remove_cv函数实现,remove_const和remove_volatile各用一个泛化和偏特化版本的函数来使得传入的是否有const(volatile)都会去掉这两个。然后再进入_is_void_helper,如果是void那么传回真,如果不是则传回假,也是用泛化和偏特化。


C++笔记十(STL与泛型编程)_第8张图片
type traits—is_void.png

四 cout

cout是一个类的对象,extern表示cout可以被外界看到,它能接受这么多类型是因为它作了大量的<<重载。如果你想写自己的类型,那么就要仿照写出<<的重载。


C++笔记十(STL与泛型编程)_第9张图片
cout.png

五 moveable元素对于容器速度效能的影响

buf分别用moveable和non-moveable以insert的方式放进来,红黑树和哈希表的容器,你只要insert,那么它就会落在该落的地方,但是vector、list、deque你要insert必须要告诉它哪里insert。stl里的insert提供插入位置选择,但对于关联式容器,就算指定插入位置,如果不合理,他还是落在它应该落在的地方。

  • vector放三百万个元素,而拷贝构造函数却调用了七百多万次,是因为vector的成长是两倍两倍的,在成长的过程中引发拷贝构造。如果一开始指定有足够大的vector,就不会有这么大的拷贝构造。
    • moveable和non-moveable的效率差别很大
    • move copy和copy效率差别很大
  • list、deque、关联式容器放三百万个元素,拷贝构造也调用三百万次,这是因为他们不像vector是连续的内存空间。
    • moveable和non-moveable的效率差别不大
    • move copy和copy效率差别不大
  • 虽然list、deque、关联式容器一开始放元素moveable和non-moveable的效率差别不大,但是后来的操作也会影响。

下面是moveable class源代码,move其实是一种浅拷贝,不过他多了一个将原来的指针打断的操作。


C++笔记十(STL与泛型编程)_第10张图片
moveable class源代码.png

C++笔记十(STL与泛型编程)_第11张图片
moveable class 源代码.png

用move版本的条件是:move以后原来的东西不再使用。下图代码中V1type(buf)是一个临时对象,放进去以后就消失了,所以会调用move版本,而c1不是临时对象,c11(c1)时不会调用move版本。如果有信心可以调用move版本,那就可以像下面代码一样加上std::move,但是必须确保接下来不会再用到c1。

C++笔记十(STL与泛型编程)_第12张图片
moveable测试.png
  • std::string有moveable版本的拷贝构造和拷贝赋值。

你可能感兴趣的:(C++笔记十(STL与泛型编程))