STL分析(十 hash、function type_traits、cout、move )

hash function

假定存在一个Customer类

class Customer{
public:
    string fname, lname;
    int no;
};

其哈希函数存在三种方式

//方式一:创建可调用类型
class CustomerHash
{
public:
    std::size_t operator()(const Customer& c) const{
        return ......
    }
};
 
unordered_set<Customer, CustomerHash> custest;
 
 
//方式二:创建哈希函数
size_t customer_hash_func(const Customer& c){
    return ......
}
 
unordered_set<Customer, size_t(*)(const Customer&)> custest(20, customer_hash_func); //注意这里调用了构造函数的不同版本
 
 
//方式三:特化标准库提供的hash<T>
namespace std
{
template<>
struct hash<Customer>
{
    size_t
    operator()(const Customer& c) const noexcept
    { return ...... }
};
}
 
unordered_set<Customer> custest;

具体的hash function计算如下:标准库提供了一个hash_val的函数,可以计算出由基本类型组成的class的hash值,原理是使用了可变模板参数

template <typename... Types>
inline size_t hash_val(const Types&... args){        //提供给使用者的重载版本
    size_t seed = 0;
    hash_val(seed, args...);                         //实际调用的版本
    return seed;
}
 
template <typename T, typename... Types>
inline void hash_val(size_t& seed,                   //实际调用函数的主体
                     const T& val, const Types&... args){
    hash_combine(seed, val);
    hash_val(seed, args...);
}
 
template <typename T>
inline void hash_val(size_t& seed, const T& val){    //实际调用函数的边界条件
    hash_combine(seed, val);
}
 
template <typename T>
inline void hash_combine(size_t& seed, const T& val){
    seed ^= std::hash<T>()(val) + 0x9e3779b9
            + (seed<<6) + (seed>>2);
}![在这里插入图片描述](https://img-blog.csdnimg.cn/c7cdce1a463d409a9c81c2ffefb290f3.png)

 
 
 
//下面是对于 class Customer 的实际使用
class CustomerHash{
public:
    std::size_t operator()(const Customer& c) const{
        return hash_val(c.fname, c.lname, c.no);
    }
};
  • 此时可使用CustomerHash hh; hh(Customer("Ace", "Hou", 1L)) % 11来计算该对象应存放的hash bucket position。
  • 在使用该hash function创建unordered_set时可用unordered_set<Customer, CustomerHash> set3;方式调用。

tuple

在这里插入图片描述

注意:string的实现在各库中可能有所不同,但是在同一库中相同一点是,无论你的string里放多长的字符串,它的sizeof()都是固定的,字符串所占的空间是从堆中动态分配的,与sizeof()无关。 sizeof(string)=4可能是最典型的实现之一,不过也有sizeof()为28、32字节的库实现。 但是MS2015测试后sizeof(string)=40.还是跟编译器有关.

tuple大小为32而不是28的原因:tuple的大小必须是其中最大元素的倍数,complex< double>大小为16,tuple对齐后大小为32

原理

tuple原理是用可变模板参数实现的递归继承
在这里插入图片描述

this指针指向的是父类对象的位置,即head在下面,tail在前面。

type traits

type traits用法

在这里插入图片描述

type traits实现

is_void实现
在这里插入图片描述
实质上是对基本类型定义特化版本,先把volatile和const用模板特化去除掉,然后用__is_void_helper对void特化为true_type,其他泛化版本默认为false_type

类似的有is_integer
在这里插入图片描述
在复杂一点的is_move_assignable等实现中并没有找到源码,猜想是编译器帮助他们完成了底层的实现。
在这里插入图片描述

cout

在这里插入图片描述
cout是一个ostream类对象
重载输入输出运算符时要注意:
通常重载输入输出(运算符)时必须是非成员函数重载,主要是为了与iostream标准库兼容。为了与标准IO库一致,重载后的符号的左侧应该是流的引用,右侧是对象的引用。(如,cout<<“hello!”;或cin>>tmp;等。)但是如果重载为类的成员函数的话,运算符左侧是对象的引用,右侧是流的引用。(ps:事实的确如此,回想一下成员函数的重载都是对象在前运算符号在后。)也就是说其形式为:

Sales_data  data;
 
data  <<  cout;//如果operator<<是Sales_data的成员。

因此定义重载输入输出运算符中要在类中定义友元函数,如Complex类

class Complex{  
    public:
        Complex( double r=0, double i=0):real(r),imag(i){ };
        friend ostream & operator<<( ostream & os,const Complex & c);
        friend istream & operator>>( istream & is,Complex & c);
    private:
        double real,imag; 
};

在标准库中对cout的重载
在这里插入图片描述

move

详细见C++新特性(标准库)

简单来说,move assignment和move ctor只拷贝指针,再把原指针的指向对象和数据清零,为浅拷贝。拷贝构造和拷贝赋值函数需要创建一块新的空间,再将原来对象的数据添加到新空间中,为深拷贝

moveable测试函数

用到的class是C++新特性(标准库)中的move-aware-class
在这里插入图片描述

  • 其中Vltype(buf)为临时对象,insert()调用后该对象不再使用,因此会自动调用move版本的ctor。因此Mctor在初始化数据时已经调用了700w次,这是元素的copy。
  • 而在M c11(c1);时编译器不知道c1是不是临时对象,因此采用传统的copy ctor,花费了3500ms。这是容器的copy
  • 如果显式告诉编译器用move版本,即M c12(std::move(c1));,则采用Mctor。这也是容器的copy。

也可以写成模板模板参数形式

template <typename T,
          template <typename T>
              class Container
         >
class XCls
{
private:
    Container<T> c;
public:
    XCLs()
    {
        for(long i=0; i<SIZE; ++i)
            c.insert(c.end(), T());//也可以像上面一样用随机数生成
 
        output_static_data(T());//也可以像上面一样用随机数生成
        Container<T> c1(c);
        Container<T> c2(std::move(c));
        c1.swap(c2);
    }
};

//不得在function body之内声明
template<typename T>
using Vec = vector<T, allocator<T>>;
 
XCls<MyString, Vec> c1;

vector的copy ctor

在这里插入图片描述

vector的move ctor

在这里插入图片描述
对容器做move,只是swap了三根指针

std::string是否moveable?

在这里插入图片描述
string带有Move版本的assignment和ctor。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值