C++11高级语法糖笔记

C++11

一、std::function和bind绑定器

可调用对象

  • 是一个函数指针
  • 是一个具有operator()成员函数的类对象(仿函数)
  • 是一个可被转换为函数指针的类对象
  • 是一个类成员(函数)指针

可调用对象包装器std::funtion

  • 头文件<functional>
  • 可以容纳除了类成员(函数)指针之外的所有可调用对象
  • 可以用统一的方式处理函数、函数对象、函数指针,并允许保存和延迟执行它们
  • function比普通函数指针更灵活和便利

std::bind绑定器

  • 头文件<functional>
  • 接受一个可调用对象,生成一个新的可调用对象
  • std::bind用来将可调用对象与其参数一起进行绑定,绑定后的结果可以使用std::function进行保存,并延迟调用到任何我们需要的时候
  • 作用
    • 将可调用对象与其参数一起绑定成一个仿函数(functor)
    • 将多元(参数个数为n)可调用对象转成一元或者(n-1)元可调用对象,却只绑定部分参数
      std::bind(output, 1, 2)();  // 输出:1 2
      std::bind(output, std::placeholders::_1, 2)(1);  // 输出 :1 2
      std::bind(output, 2, std::placeholders::_1)(1);  // 输出 :2 1
      
  • std::bind的返回类型是一个stl内部定义的仿函数类型
  • std::placeholders::_1是一个占位符,代表这个位置将在函数调用时被传入的第一个参数所替代
  • 组合bind函数
    • std::bind(std::greater<int>(), std::placeholders::_1, 5);
      • 判断是否大于5的功能闭包

C++11通过提供std::function和std::bind统一了可调用对象的各种操作

二、lambda表达式

语法形式

  • [ capture ] ( params ) opt -> ret { body; };
  • C++11允许省略lambda表达式返回值定义,这样编译器就会根据return语句自动推导出返回类型
    • 初始化列表不能用于返回值的自动推导
  • lambda表达式在没有参数列表时,参数列表是可以省略的

捕获列表

  • []不捕获任何变量
  • [&]捕获外部作用域中所有变量,并作为引用在函数体中使用(引用捕获)
  • [=]捕获外部作用域中所有变量,并作为副本在函数体中使用(按值捕获)
  • [=, &foo]按值捕获外部作用域中所有变量,并按引用捕获foo变量
  • [bar]按值捕获bar变量,同时不捕获其他变量
  • [this]捕获当前类中的指针,让lambda表达式拥有和当前类成员函数同样的访问权限。如果已经使用了&或者=,就默认添加此选项
  • 如果希望去修改按值捕获的外部变量,需要显示指明lambda表达式为mutable,如:auto f2 = [=]() mutable {return a++; }

可以认为它是个带有operator()的类,即仿函数

  • 可以使用std::functionstd::bind来存储和操作lambda表达式
    std::function<int(int)> f1 = [](int a){ return a; };
    std::function<int(void)> f2 = std::bind([](int a){ return a; }, 123);
    
  • 对于没有捕获任何变量的lambda表达式,还可以被转换成一个普通的函数指针
    using func_t = int (*)(int);
    func_t f = [](int  a){ return a; };
    f(123);
    
  • 捕获变量的lambda表达式则不能转化为普通指针,若转换,lambda表达式本身的this指针就丢失掉了
  • lambda和std::function的效果是一样的,一般情况下可直接用lambda来代替function

三、tuple元组

定义

  • 可以把它当作一个通用的结构体来用,不需要创建结构体又获取结构体的特征
  • 在某些情况下可以取代结构体,使程序更简洁、直观
  • 如果用tuple来替代3个以上字段的结构体时就不太合适了,不直观,易读性降低(建议)

功能

  • 创建元组
    • tuple<const char*, int> tp = make_tuple(sendPack, nSendSize);
    • auto tp = return std::tie(1, "aa", 2); // tp的实际类型是:std::tuple<int&, string&, int&>
  • 获取元组的值
    const char* data = tp.get<0> ();  // 获取第一个值
    int len = tp.get<1> ();  // 获取第二个值
    
    • 通过std::tie解包tuple
      int x, y;
      string a;
      std::tie(x, a, y) = tp;
      
      • 如果只想了解某个位置的值时,可以用std::ignore占位符来表示不解某个位置的值
        std::tie(std::ignore, std::ignore, y) = tp; // 只解第3个值
  • 创建右值引用元组
    • forward_as_tuple
      std::map<int, std::string> m;
      m.emplace(std::forward_as_tuple(10, std::string(20, 'a')));
      
      • 创建了类似于std::tuple<int&&, std::string&&>类型的tuple
  • 连接多个tuple
    • tuple_cat

四、shared_ptr共享的智能指针

概念

  • 希望多个智能指针管理同一个资源就用shared_ptr
  • std::shared_ptr使用引用计数,每一个shared_ptr的拷贝都指向相同的内存
  • 在最后一个shared_ptr析构的时候,内存才会被释放

基本用法

  • 初始化
    • 可以通过使用构造函数、std::make_shared<int>辅助函数和reset方法来初始化shared_ptr
      std::shared_ptr<int> p(new int(1));  // 使用动态初始化,后加一个括号为值初始化
      std::shared_ptr<int> p2 = p;
      std::shared_ptr<int> ptr;
      ptr.reset(new int(1));
      
    • 不能将一个原始指针直接赋值给智能指针
    • 当智能指针中有值的时候,调用reset会使引用计数减1
  • 获取原始指针
    • 可以通过get方法来返回原始指针
    std::shared_ptr<int> ptr(new int(1));
    int* p = ptr.get();
    
  • 指定删除器
    • 智能指针初始化可以指定删除器
    • 当p的引用计数为0时,自动调用删除器DeleteIntPtr来释放对象的内存
    void DeleteIntPtr(int* p)
    {
        delete p;
    }
    std::shared_ptr<int> p(new int, DeleteIntPtr);
    
    • 也可以用lambda表达式指定删除器
      • std::shared_ptr<int> p(new int, [](int* p) { delete p; });
    • 当我们用shared_ptr管理动态数组时,需要指定删除器,因为std::shared_ptr的默认删除器不支持数组对象
    • 也可以将std::default_delete作为删除器

注意事项

  • 1)不要用一个原始指针初始化多个shared_ptr
  • 2)不要在函数实参中创建shared_ptr
    • 可能会因为发生异常而泄露内存
  • 3)通过shared_from_this()返回this指针
    • 因为this指针本质上是一个裸指针,因此这样可能会导致重复析构
    • 解决方法
      • 让目标类通过派生std::enable_shared_from_this<T>类,然后使用基类的成员函数shared_from_this来返回this的shared_ptr
  • 4)要避免循环引用
    • 智能指针最大的一个陷阱就是循环引用,循环引用会导致内存泄露
    • 导致意外延长对象的生命期
    • 解决方法
      • 使用weak_ptr

五、unique_ptr独占的智能指针

概念

  • 希望只有一个智能指针管理资源或者管理数组就用unique_ptr
  • unique_ptr是一个独占型的智能指针
  • 它不允许其他的智能指针共享其内部的指针
  • 不允许通过赋值将一个unique_ptr赋值给另外一个unique_ptr
  • 可以通过std::move来转移到其他的unique_ptr
    unique_ptr<T> myPtr(new T);
    unique_ptr<T> myOtherPtr = std:: move(myptr);
    

注意事项

  • 如果是数组,则判断是否是定长数组
    • 若为定长数组则编译不通过(不能这样调用make_unique<T[10]>(10))
    • 若为非定长数组,则获取数组中的元素类型,再根据参数size创建动态数组的unique_ptr
      • unique_ptr<int[]> ptr5 = make_unique<int[]>(10);
  • unique_ptr可以指向数组(而shared_ptr这么做不合法)
    std::unique_ptr<int []> ptr(new int[10])
    ptr[9] = 9;         // 设置最后一个元素值为9
    
  • unique_ptr指定删除器的时候需要确定删除器的类型
    • std::unique_ptr<int, void(*)(int*)> ptr(new int (1), [](int* p) { delete p; });
    • lambda表达式在没有捕获变量的情况下是可以直接转换为函数指针的,一旦捕获了就无法转换了
      • std::unique_ptr<int, void(*)(int*)> ptr(new int (1), [&](int* p) { delete p; }); // 错误,捕获了变量
    • 如果希望unique_ptr删除器支持lambda可以这么写
      • std::unique_ptr<int, std::function<void(int*)>> ptr(new int(1), [&](int* p) { delete p; }

六、weak_ptr弱引用的智能指针

概念

  • weak_ptr是用来监视shared_ptr的,不会使引用计数加1
  • 它不管理shared_ptr内部的指针,主要是为了监视shared_ptr的生命周期
  • 它的构造不会增加引用计数,它的析构也不会减少引用计数

基本用法

  • 1)通过use_count()方法来获得当前观测资源的引用计数
    shared_ptr<int> sp(new int(10));
    weak_ptr<int> wp(sp);
    cout << wp.use_count() << endl;  // 结果将输出1
    
  • 2)通过expired()方法来判断所观测的资源是否已经释放(true为无效)
    if (wp.expired())
        std::cout << "weak_ptr无效,所监视的智能指针已经被释放";
    else
        std::cout << "weak_ptr有效";
    
  • 3)通过lock()方法来获取所监视的shared_ptr
    auto sp = std::make_shared<int> (42);
    std::weak_ptr<int> gw = sp;
    auto spt = gw.lock();  // 获取所监视的shared_ptr
    cout << *spt << endl;
    

weak_ptr返回this指针

  • std::enable_shared_from_this类中有一个weak_ptr,这个weak_ptr用来观测this智能指针
  • 调用shared_from_this()方法时,会调用内部这个weak_ptr的lock()方法,将所观测的shared_ptr返回
  • 获取自身智能指针的函数仅在shared_ptr<T>的构造函数被调用之后才能使用,因为enable_shared_from_this内部的weak_ptr只有通过shared_ptr才能构造

可以通过weak_ptr解决循环引用问题

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值