C++11中与Boost库相关的部分新特性总结

改进单例模式

在c++11之前,实现一个通用的泛型单例模式时,会遇到一个问题:这个泛型单例要能够创建所有的类型对象,但是这些类型的构造函数形参可能不尽相同,参数个数和参数类型可能都不同,这导致我们不容易做一个所有类型都通用的单例。现在c+11帮助我们解决了这个问题,解决这个问题靠的是c++11的可变模板参数。C++11版本的通用单例模式的实现,没有了重复的模板定义,支持任意个数参数的类型创建,不用担心模板函数定义得不够,还支持完美转发,无论是左值还是右值都能转发到正确的构造函数中,通过右值引用的移动语义还能进一步提高性能,简洁而优雅。

使用c++改进程序性能

C++11中引入了右值引用和移动语义,可以避免无谓的复制,提高程序性能。相应的,C++11的容器还增加了一些右值版本的插入函数。

Move–>右值引用

通过这些函数我们可以避免不必要的拷贝,提高程序性能。move是将对象的状态或者所有权从一个对象转移到另一个对象,只是转移,没有内存的搬迁或者内存拷贝。如图所示是深拷贝和move的区别。
这里写图片描述
这种移动语义是很有用的,比如我们一个对象中有一些指针资源或者动态数组,在对象的赋值或者拷贝时就不需要拷贝这些资源了。定义拷贝构造函数和赋值函数的更有效的方法是直接交换a和临时对象中的资源指针,然后让临时对象的析构函数去销毁a原来拥有的资源。 如果不用std::move,拷贝的代价很大,性能较低。使用move几乎没有任何代价,只是转换了资源的所有权。如果一个对象内部有较大的对内存或者动态数组时,很有必要写move语义的拷贝构造函数和赋值函数,避免无谓的深拷贝,以提高性能。

Functional/Forward–>新的标准库std::forward

右值引用类型是独立于值的,一个右值引用参数作为函数的形参,在函数内部再转发该参数的时候它已经变成一个左值了,并不是它原来的类型了。因此,我们需要一种方法能按照参数原来的类型转发到另一个函数,这种转发被称为完美转发。所谓完美转发(perfect forwarding),是指在函数模板中,完全依照模板的参数的类型,将参数传递给函数模板中调用的另外一个函数。c++11中提供了这样的一个函数std::forward,它是为转发而生的,它会按照参数本来的类型来转发出去,不管参数类型是T&&这种未定的引用类型还是明确的左值引用或者右值引用。

unordered_map、unordered_set

C++11增加了无序容器unordered_map/unordered_multimap和
unordered_set/unordered_multiset,由于这些容器中的元素是不排序的,因此,比有序容器map/multimap和set/multiset效率更高。

无序集合(Unordered Set)容器是一个存储唯一(Unique,即无重复)元素的关联容器(Associative container),容器中的元素无特别的次序关系。该容器允许基于值地快速元素检索。
在一个 unordered_set 容器中,元素的值同时可以用来标志对应的元素(即值是自身的主键),每个值必须是唯一的。主键是不可修改的,因此在 unordered_set 中的元素不能被逐个修改(所有元素保持恒定),但是可以删除某个元素或插入新的元素。
在 unordered_set 内部,元素不会按任何顺序排序,而是通过元素值的 hash 值将元素分组放置到各个槽(Bucket,也可译成“桶”)中,这样就能通过元素值快速地访问各个对应的元素(平均耗时为一个常量,即时间复杂度为 O(1))。
除了排序(std::sort 等)、堆操作(std::make_heap、std::sort_heap 等)、第n个元素(std::nth_element),适用于 std::vector 的例子基本上都适用于 std::unordered_set,与unordered_set相关的算法可参考vector相关的算法。

顺序容器库

Array—>新的标准库std::array
C++11中引入了array容器,array是序列容器的一种。array很类似于一般的数组,例如,array在栈上分配连续的内存来储存元素,并且array的大小是不可以改变的,这也就是说,可以修改array中元素的值,但不能向array中插入和删除元素。
array具有序列容器的特点,同时又具有一般数组的特点,实际上,在程序中需要使用数组的地方,都可以使用array来代替,使用array更为安全,除了具有一般数组的灵活性,还具有与一般数组相近的性能优势。
Array内部只存储所包含的数据,哪怕是大小也只不过是个模板参数。和普通使用‘[]’语法申明的数组相比,只不过显得更加高效(操作高效),因为这个类添加了一系列的全局成员函数用来操作这些元素。

让程序更简洁更现代

C++11的一些特性可以使程序更简洁易读,也更现代。通过这些新特性,可以更方便和高效地撰写代码,并提高开发效率。

Typeof—>auto, decltype,推断变量或表达式的数据类型ResultOf—>新的标准库std::result_of

  • 在C++11中auto关键字被重新利用了,用作类型推导,比如通常我们定义一个变量:int i = 0; //强类型定义 改用auto关键字的话就可以 auto i= 20;这样定义了,我们就不需要关系对于每个值定义成什么类型,编译器自动会去根据初始化表达式的值的类型去推导变量的具体类型,因此也就意味着,编译器要推导出变量的类型,那么该变量就必须初始化,否则会编译失败。
    auto的使用限制为:
    1、auto不能作为函数参数。
    2、auto不能修饰类或者结构体中的非静态成员变量
    3、auto不能定义数组
    4、auto不能推导出模板参数
  • C++11新增了一个关键字decltype,用于在编译时期推导出一个表达式的类型,而不用初始化,其语法格式有点像sizeof:decltype(exp)
    其中,exp表示一个表达式(expression)。
    从格式上看,decltype很像sizeof——用来推导表达式类型大小的操作符。与sizeof类似,decltype推导表达式类型也是在编译时期就完成的,并不会真正的计算表达式的值。
  • result_of
    通过函数调用表达式推断返回类型。

Valueinitialized—>List-initialization

在C++98/03中我们只能对普通数组和POD(plain old data,简单来说就是可以用memcpy复制的对象)类型可以使用列表初始化,如下:
数组的初始化列表: int arr[3] = {1,2,3}
在C++11中初始化列表被适用性被放大,可以作用于任何类型对象的初始化。
让人惊奇的是在C++11中可以使用列表初始化方法对堆中分配的内存的数组进行初始化,而在C++98/03中是不能这样做的。
C++11中增加了std:fuction和std::bind,不仅让我们使用标准库函数时变得更加方便,而且还能方便地实现延迟求值。
标准库函数bind()和function()定义于头文件中(该头文件还包括许多其他函数对象),用于处理函数及函数参数。类模版 std::function 是一种通用、多态的函数封装。

  • Function新的标准库std::function
    std::function 是可调用对象的包装器。它是一个模板类,它的实例可以对任何可以调用的目标进行存储、复制、和调用操作,这些目标包括函数、lambda 表达式、绑定表达式、以及其它函数对象等。通过指定它的模板参数,可以用统一的方式处理函数、函数对象、函数指针,并允许保存和延迟执行它们。

  • std::bind用来将可调用对象与其参数一起进行绑定。绑定后的结果可以使用std::function进行保存,并延迟调用到任何我们需要的时候。
    主要有两大作用:
    1) 将可调用对象与其参数一起绑定成一个仿函数。
    2) 将多元(参数个数为n,n>1)可调用对象转成一元或者(n-1)元可调用对象,即只绑定部分参数。

BOOST_LambdaLambda表达式

lambda表达式是C++11最重要也最常用的一个特性之一。lambda来源于函数式编程的概念,也是现代编程语言的一个特点。
lambda表达式有如下优点:
1).声明式编程风格:就地匿名定义目标函数或函数对象,不需要额外写一个命名函数或者函数对象。以更直接的方式去写程序,好的可读性和可维护性。
2).简洁:不需要额外再写一个函数或者函数对象,避免了代码膨胀和功能分散,让开发者更加集中精力在手边的问题,同时也获取了更高的生产率。
3).在需要的时间和地点实现功能闭包,使程序更灵活。

  • lambda表达式的语法归纳如下:
    [ caputrue ] ( params ) opt -> ret { body; };
    1).capture是捕获列表;
    2).params是参数表;(选填)
    3).opt是函数选项;可以填mutable,exception,attribute(选填)
    mutable说明lambda表达式体内的代码可以修改被捕获的变量,并且可以访问被捕获的对象的non-const方法。
    exception说明lambda表达式是否抛出异常以及何种异常。
    attribute用来声明属性。
    4).ret是返回值类型。(选填)
    5).body是函数体。
  • 捕获列表:lambda表达式的捕获列表精细控制了lambda表达式能够访问的外部变量,以及如何访问这些变量。
    1).[]不捕获任何变量。
    2).[&]捕获外部作用域中所有变量,并作为引用在函数体中使用(按引用捕获)。
    3).[=]捕获外部作用域中所有变量,并作为副本在函数体中使用(按值捕获)。
    4).[=,&foo]按值捕获外部作用域中所有变量,并按引用捕获foo变量。
    5).[bar]按值捕获bar变量,同时不捕获其他变量。
    6).[this]捕获当前类中的this指针,让lambda表达式拥有和当前类成员函数同样的访问权限。如果已经使用了&或者=,就默认添加此选项。捕获this的目的是可以在lamda中使用当前类的成员函数和成员变量。

Tuple~新的标准库std::tuple

tuple是一个固定大小的不同类型值的集合,是泛化的std::pair。和c#中的tuple类似,但是比c#中的tuple强大得多。我们也可以把他当做一个通用的结构体来用,不需要创建结构体又获取结构体的特征,在某些情况下可以取代结构体使程序更简洁,直观。

  • 基本用法
tuple<const char*, int>tp = make_tuple(sendPack,nSendSize); 
\\等价于
struct A
{
    char* p;
    int len;
};
  • tuple和vector比较:
    vector只能容纳同一种类型的数据,tuple可以容纳任意类型的数据;
    tuple和variant比较:
    二者都可以容纳不同类型的数据,但是variant的类型个数是固定的,而tuple的类型个数不是固定的,是变长的,更为强大。
    虽然tuple可以用来代替简单的结构体,但是用tuple来替代3个以上字段的
    结构体会导致代码的易读性降低,因此建议对于多个字段的结构体时不要使用tuple。

基于Range的for循环替代

C++ 定义了许多”范围 (range) “的概念。范围表现有如受控制的列表 (list),持有容器中的两点。有序容器是范围概念的超集 (superset),有序容器中的两个迭代器 (iterator) 也能定义一个范围。这些概念以及操作的算法,将被并入 C++11 标准程序库。为了在遍历容器时支持”for_each”用法,C++11扩展了for语句的语法。用这个新的写法,可以遍历C类型的数组、初始化列表以及任何重载了非成员的begin()和end()函数的类型。如果你只是想对集合或数组的每个元素做一些操作,而不关心下标、迭代器位置或者元素个数,那么这种foreach的for循环将会非常有用。不过 C++11 将会以语言层次的支持来提供范围概念的效用。
简单用法如下:

int my_array[5] = {1, 2, 3, 4, 5};
for (int &x : my_array)
{
  x *= 2;
}

上面 for 述句的第一部份定义被用来做范围迭代的参数,就像被声明在一般 for 循环的参数一样,其作用域仅只于循环的范围。而在”:”之后的第二区块,代表将被迭代的范围。

消除重复,提高代码质量

type_traits

Type Traits就是“类型的特征”的意思。在C++编程中,使用Type Traits来判断类型的特性,并根据这些类型信息选择应有的操作。通过type_traits可以实现在编译期计算、查询、判断、转换和选择,增强了泛型编程的能力,也增强了我们程序的弹性,使得我们在编译期就能做到优化改进甚至排错,能进一步提高代码质量。type_traits在一定程度上可以消除冗长的swich-case或者if-else的语句,降低程序的圈复杂度,提高代码可维护性。

解决内存泄漏的问题

新的标准库std::unique_ptr、std::shared_ptr、std::weak_ptr

C++11中有unique_ptr、shared_ptr与weak_ptr等智能指针,可以对动态资源进行管理
unique_ptr“唯一”拥有其所指对象,同一时刻只能有一个unique_ptr指向给定对象(通过禁止拷贝语义、只有移动语义来实现)。
unique_ptr指针本身的生命周期:从unique_ptr指针创建时开始,直到离开作用域。离开作用域时,若其指向对象,则将其所指对象销毁(默认使用delete操作符,用户可指定其他操作)。unique_ptr指针与其所指对象的关系:在智能指针生命周期内,可以改变智能指针所指对象,如创建智能指针时通过构造函数指定、通过reset方法重新指定、通过release方法释放所有权、通过移动语义转移所有权。

  • unique_ptr的使用场景:
    (1)动态资源的异常安全保证(利用其RAII特性):
    (2) 返回函数内创建的动态资源
    (3) 可放在容器中(弥补了auto_ptr不能作为容器元素的缺点)管理动态数组,因为unique_ptr有unique_ptr<>重载版本,销毁动态对象时调用delete[]

  • auto_ptr 对象将会绑定一个指定类型的指针,并且负责该指针资源的释放。一般用法是auto_ptrpStruct(new MyStruct)定义了一个智能指针对象 pStruct, 该对象用于管理一个类型为 MyStruct 的指针。在auto_ptr类里,有一个指针成员变量,由该成员变量保存指针的地址。

  • shared_ptr 使用引用计数的方式来实现对指针资源的管理。同一个指针资源,可以被多个 shared_ptr 对象所拥有,直到最后一个 shared_ptr 对象析构时才释放所管理的对象资源。可以说,shared_ptr 是最智能的智能指针,因为其特点最接近原始的指针。不仅能够自由的赋值和拷贝,而且可以安全的用在标准容器中。shared_ptr 和 auto_ptr 一样,也是重载了-> 和 * 操作符,使用 get() 函数可以得到原始的指针,auto_ptr 提供了隐式的bool类型转换,可以直接用于判断auto_ptr对象是否绑定了指针资源。 use_cout() 用于返回当前资源的引用计数。

数值库

新的标准库std::ratio

分子:描述精确的有理数分数。绝对值必须在 intmax_t 类型可描述的范围内。intmax_t 是最宽的带符号整数类型。
分母:绝对值必须在 intmax_t 类型可描述的范围内,且不能为 0。
intmax_t 是最宽的带符号整数类型。

Random

提供了一个非确定性随机数生成设备。并把随机数抽象成随机数引擎和分布两部分.引擎用来产生随机数,分布产生特定分布的随机数(比如平均分布,正太分布等).

诊断库

static_assert

C++0x中引入了static_assert这个关键字,用来做编译期间的断言,因此叫做静态断言。
其语法很简单:static_assert(常量表达式,提示字符串)。
如果第一个参数常量表达式的值为真(true或者非零值),那么static_assert不做任何事情,就像它不存在一样,否则会产生一条编译错误,错误位置就是该static_assert语句所在行,错误提示就是第二个参数提示字符串。

  • static_assert与assert的不同:
    assert(表达式):运行时断言,表达式为false,在运行时打印固定的错误信息,并终止程序。
    static_assert(表达式,“想让编译器显示的出错信息”):编译时断言,表达式为false,在编译时显示给定的出错信息。
    表达式为true,两者都不做任何事。

其他

std::enable_if

enable_if 的主要作用就是当某个 condition 成立时,enable_if可以提供某种类型。enable_if在标准库中通过结构体模板实现的。
但是当 condition 不满足的时候,enable_if<>::type 就是未定义的,当用到模板相关的场景时,只会 instantiate fail,并不会编译错误

std::mem_fn

mem_fn里面的mem就是指类的成员member, 而fn就是指function, 加在一起就是说member function,即mem_fn是用来适配类的成员函数的,将成员函数(Member function)转化成函数对象(指针(Pointer)版)。

std::ref、std::cref

用从参数推导出的类型确定的模板参数,创建一个 reference_wrapper 对象。即返回std::reference_wrapper(类似于指针)。

std::swap

交换两个对象的值。此处的对象不仅是变量还可以是数组,或任意类型的对象。

regex

正则表达式(regular expression)描述了一种字符串匹配的模式,可以用来检查一个串是否含有某种子串、将匹配的子串做替换或者从某个串中取出符合某个条件的子串等。主要的算法为regex_search, regex_match, regex_replace.
1、regex_match 判断一个正则表达式(参数 e)是否匹配整个字符序列 str. 它主要用于验证文本。注意,这个正则表达式必须匹配被分析串的全部,否则函数返回 false.如果整个序列被成功匹配,regex_match 返回 True.
2、regex_search 类似于 regex_match, 但它不要求整个字符序列完全匹配。
3、regex_replace 在整个字符序列中查找正则表达式e的所有匹配。这个算法每次成功匹配后,就根据参数fmt对匹配字符串进行格式化。缺省情况下,不匹配的文本不会被修改,即文本会被输出但没有改变。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值