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

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

作者: hjsadam | 来源:发表于2017-06-22 23:27 被阅读0次

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

    万用的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的功能。

    相关文章

      网友评论

          本文标题:关于STL与泛型编程学习感想五(博览网)

          本文链接:https://www.haomeiwen.com/subject/mgjqcxtx.html