关于STL与泛型编程学习感想五(博览网)

2017-06-22  本文已影响0人  hjsadam

体系结构与内核分析第四讲

万用的hash function

hash function就是把任意长的输入字符串变化成固定长的输出字符串的一种函数。输出字符串的长度称为hash函数的位数。

使用以hash table为底层的容器时,必须要为放的元素写一个hash function。在计算机编程里面,所有的data也是由原始的整数、浮点数、字符串组成的,基本的数值型这些本身都有hash function,他们的hash function传回来的就是自己。有没有可能把一个设计出来的数据结构,一个元素,把它拆分开来,然后把它各自的hash code(hash function产生hash code)加起来,就变成这个元素的hash code。hash function是将产生的hash code做到越乱越好,不要重复。将每个hash code相加太过天真:元素容易碰撞,每个篮子挂的元素多,查找慢。从TR1开始有了下面1234帮你写hash function。typename...表示可以接受任意多的模板参数。

hash function设计原则:产生的hash code尽可能减少冲突, 使元素能够尽可能多的落在不同的篮子里。

计算hash code时,0x9e3779b9是借用的黄金比例。

hash function的写法形式有三种:

1.形式1模板参数只需要填元素类型和函数类型就可以,自动产生函数对象被调用。

2.形式2模板参数需要填元素类型和函数类型,创建容器时候还要把真正的函数地址放进来。

3.第三种实现形式是通过对自己的元素作一个偏特化版本实现hash function。G4.9以后有了string的偏特化版本。

代码:

#include

class Customer {

...

};

//-------------------------形式1成员函数---------------------

class CustomerHash

{

public:

std::size_t operator()(const Customer& c) const {

return...

};

//-----------------------------------------------------------

unordered_set custset;

//-------------------------形式2一般函数---------------------

size_t customer_hash_func(const Customer& c){

return...

};

//-----------------------------------------------------------

unordered_set

custset(20,customer_hash_func);

unordered_set两种使用方法:一种是针对需要存放的元素类型,定义泛函数。另一种是定义一个hash_function。

tuple

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

eg. tuple> t; //sizeof(t) = 32.?

tuple t1(41, 6.3, "nice");

get<0>(t1)取t1的第0个元素。get<1>(t1)取t1的第1个元素...

auto t2 = make_tuple(22, 44, "stacy"); //创建一个tuple,并写入元素。

get<1>(t1) = get<1>(t2); //assign value

tuple之间可以比较大小。

tie绑定,将tuple中对应的各个元素绑定到tie中。

tuple_size获取tuple中value个数。

tuple_element获取tuple中第几个元素的类型。

tuple会自动递归,把元素分隔为head和tail, tail会再分隔为head和tail, 直到tail只有一个元素为止。层层继承, tail作为基类,head作为数据成员。

type traits 类型萃取器

type_traits:回答class中的默认构造、拷贝构造、拷贝赋值、析构函数重要不重要、是否是POD(plain old data, c风格的结构,没有成员函数)等,默认是false。对于自己定义的类型,可以自己定义__type_traits的特化版本。泛化版本(默认)六个typedef。

设计一个复数,有实部和虚部,不必为他写析构函数、拷贝构造函数、拷贝赋值函数,因为不写的话编译器有一个默认版本。

使用:string的析构函数不是虚函数,string的设计上是不打算让用户继承的。has_virtual_destructor是0.,is_polymorphic(是否有多态)是0。

Zoo(const Zoo&) = delete; 不要编译器默认的。

Zoo(Zoo&&) = default; 要编译器默认的搬移构造函数, 和用户不写意义相同。

Zoo& operator=(const Zoo&) = default;

Zoo& operator=(const Zoo&&) = delete; //不要编译器默认的搬移赋值函数。

萃取器可以得知以上这四个函数是否需要编译器给的。

traits实现原理

is_void的实现:is_void类模板继承自__is_void_helper类模板,首先对类型去除const、volatile(多线程用到,易挥发)属性,用remove_cv函数实现,remove_const和remove_volatile各用一个泛化和偏特化版本的函数来使得传入的是否有const(volatile)都会去掉这两个。再传给__is_void_helper,利用它的泛化和特化void,判断是否是void。

is_integral的实现:也是先除去const和volatile属性,再利用__is_integral_helper的泛化和偏特化判断,如果不是和某种特化版本匹配的类型,那么就会使用泛化版本,泛化版本的回答是false。

有些type_traits的实现找不到源代码,是由编译器实现的。

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

moveable元素

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

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

两种拷贝方式:

eg. M c11(c1);

M c12(std::move(c1));

vector放三百万个元素,而拷贝构造函数却调用了七百多万次,是因为vector的成长是两倍两倍的,在成长的过程中引发拷贝构造。如果一开始指定有足够大的vector,就不会有这么大的拷贝构造。

1.moveable和non-moveable的效率差别很大,2.move copy和copy效率差别很大。

list、deque、关联式容器放三百万个元素,拷贝构造也调用三百万次,这是因为他们不像vector是连续的内存空间。

虽然list、deque、关联式容器一开始放元素moveable和non-moveable的效率差别不大,但是后来的操作也会影响。

string是具有moveable的功能。

上一篇下一篇

猜你喜欢

热点阅读