C++11新特性——The C++ standard library, 2nd Edition 笔记(一)
前言
这是我阅读《The C++ standard library, 2nd Edition》所做读书笔记的第一篇。这个系列基本上会以一章一篇的节奏来写,少数以C++03为主的章节会和其它章节合并,一些内容较多的章节也会抽出几个独立的章节。这些博文不分析具体的应用情景,仅仅用来记录C++11新增的知识点。关于C++11知识点的详细解析,请参考C++11 FAQ;关于C++03以及STL的较详尽解析,请参考相关著作或者网络资料;推荐《C++ Primer,4th edition》和《The C++ Standard Library》。
(原书第三章:New Language Features)
新语言特性
- 用模板作为模板参数不需要再在结尾">"处留空格:
vector<list<int>> vli; // OK
- nullptr 代替 NULL;前者的类型是 std::nullptr_t,后者是int。nullptr_t定义在<cstddef>中。
- 扩展auto关键字含义,编译器自动推导变量类型:
vector<VeryVeryLongLongType> foo; ... auto iter = foo.begin(); // iter是一个迭代器。
auto bar = [&](int)->bool{return ...}; // bar是一个lambda函数。可以这样调用:bar(3)。
- 一致初始化与初始化列表(uniform initialization & initialization lists):
int values[] { , , , , , , }; // 初始化列表不能进行变量的不精确转换int a {1.0};是错误的。
std::vector<std::string> vs {"c++", "is", "upgrading"};
std::initializer_list<int> il = {, , , }; // 用来接受{1, 2, 3, 4}的新类型,initializer_list。
class Foo { ... Foo(std::initializer_list<int>& lst); ...}; ... Foo foo {, , , }; // 注意:构造函数的参数列表可以隐式转换为initializer_list
- 基于区间(range-based)循环,它的伪代码格式是:for(var : container) { statments; }
for (int i : {, ,, , , , , , }) // 初始化列表格式
cout << i << endl; vector<int> vect; ...
for (auto& item : vect) // 要改变值,要用 auto&, 引用
item *= ;
- move语义和右值引用。支持move语义是C++11最重要的新特性。这个特性主要用来避免不必要的拷贝和临时变量。
- 右值引用可以指向匿名对象、临时对象,但不能是常量,因为它需要能被修改,以支持资源转移。右值的特性之一是它不可以取地址。
- 右值引用的记号是&&,比如 int&& a = 5; 注意,a 虽然是一个右值引用变量,但它本身确是一个左值,因为它可以取地址。要转换成右值引用的话,需要用到下面的move函数。
- move函数在<utility>头文件中,为了实现将普通变量转换为右值引用。从语义上讲,被move过的对象不再有效。但是move一个类似int,不管理资源,不涉及复杂状态的对象则毫无影响。其实,move只对定义了move constructor或者move assignment operator的对象真正有作用。move过的对象不再有效。
- move constructor 是以 T&&为参数的T类型的constructor,而move assignment operator是以T&&为参数的operator=的重载版本。再次提醒,这里的右值引用一定不会是const,因为move语义要改变右值引用对象的值,将它持有的资源搬移过来。
- 右值引用配合move语义,可以直接将资源从临时对象“剪切”并“粘贴”到目标对象上,提高效率。右值引用用来引出“剪切”和“粘贴”操作,具体的过程,则由move constructor和move assignment operator完成。
- 左值引用(lvr)和右值引用(rvr)的重载规则:
- 只实现了void foo(T&) : 不接受rvr,只接受lvr。
- 只实现了void foo(const T&) : 可以接收rvr和lvr。
- 实现了void foo(const T&)和void foo(T&&) : 可以区分出rvr和lvr两种调用。如果有move语义,就使用T&&格式;否则使用普通拷贝。
- 只实现了void foo(T&&) : 智能接受rvr。
- 返回值的rvr:
- 返回值不应该使用move函数,编译器会根据情况去调用。对于如下的代码段,以下的行为是可以保证的。
X foo()
{
X x; ... return x;
} - 如果定义了copy constructor或者move copy constructor,首先执行NRV(named return value)优化(参考《Inside the C++ object model》2.3节)。NRV在C++11之前已经被广泛支持,在C++11中把它规范化。
- 如果定义了move copy constructor,执行move语义。
- 否则,如果定义了copy constructor,执行copy语义。
- 否则,编译时错误。
- 注意!返回值类型不能是rvr,因为rvr始终还是引用。引用返回的局部变量是没有意义的。
- 返回值不应该使用move函数,编译器会根据情况去调用。对于如下的代码段,以下的行为是可以保证的。
- 关于右值引用的深入理解,可参考另一篇博文:C++右值引用
- 新的字符串字面值:
- Raw String Literal : 不需要转义的字符串字面值。
const char* pFile = R"(C:\Windows\Somepath.file)";// 等价于 "C:\\Windows\\Somepath.file";
const char* pName = R"nc(Alcohol"(wine)")nc"; // 如果字符串中出现"(或者)",可以加入分隔符,比如nc。
const char* pUtf8 = u8"utf8字符串"; // u8表示utf8字符串;
const char16_t* pChar16t = u"双字节字符串"; // u表示双字节字符串;
const char32_t* pChar32t = U"四字节字符串"; // U表示四字节字符串;
const wchar_t* pwchart = L"宽字符串"; // L表示宽字符串;注意,R格式的字面值,可以用u8,u,U,L修饰。
- noexcept关键字,有两层含义:
- 作为声明,表示函数不会抛出异常,例: void foo() noexcept; 相当于throw()。声明时可以附带参数。
- 作为运算符,返回某个函数是否会抛出异常,例: vector<int> vi; noexcept(vi.at[0]);// true。
- constexpr关键字
- 表示表达式可以在编译时求值,可以被当做常量处理。例如: std::numeric_limits<short>::max()就是一个被constexpr修饰的表达式。它可以用来初始化数组的长度了。
- 模板的新特性
- 模板参数个数可变(这个特性有点疯狂):
template <typename T, typename ... Types>
void print(const T& arg0, const Types&... args)
{
std::cout << arg0 <<std::endl; // 打印第一个值。
print(args...); // 打印剩余的值。递归吗?
}std::tuple<>大量使用这个特性。
- 模板别名 :
template<typename T>
using Vec = std::vector<T, MyAlloc<T>>; // Vec是一个别名模板,它是std::vector<T, MyAlloc<T>>的别名;
// 模板别名的声明格式: template<typename T> using alias = ...;
Vec<int> vec;- 其他特性 : 函数模板可以有默认参数,局部类型也可以作为模板参数等
- 模板参数个数可变(这个特性有点疯狂):
- lambda函数(匿名函数),比如 [=, &b](int a)->int{return a + b;},它是实现闭包的关键。
- =, &b叫做capture(捕获),它用来限定lambda可以访问的外部context中非静态变量的方式。
- [=, &b] 整个叫做lambda introducer(lambda引导器)。
- {...}中的内容为函数体。
- lambda可以有参数,mutable修饰符,exception说明,属性修饰符和返回值类型。所有都是可选的,但是只要出现一个,那么小括号()就一定要有。所以,lambda的格式就有两种:
- [...]{...}
- [...](...)mutableopt, throwopt ->returnTypeopt {...}
- 如果没有写返回值,lambda的返回值根据return的类型来自动推导。
- capture的格式,[=]表示所有外部变量都是传值的,外部变量只读;[&]表示所有外部变量都是传引用的,对外部变量读写;也可以为单个外部变量指定访问模式,比如[=, &a]表示除了a之外,所有其他变量都是传值的。
- lambda表达式都可以转换为仿函数。
- lambda的类型是匿名函数对象,每个lambda对象的类型都不一样。声明lambda的类型需要模板,或者auto关键字,或者decltype关键字。此外,也可以用std::function类模板,指定一种函数式编程(FP)的通用类型。
- decltype关键字
- decltype(exp) 表示 exp的类型,可以用在所有需要变量声明的地方。
- decltype的典型应用:声明返回值类型(见下一条);在声明容器,需要functor类型作为模板参数时,传递lambda的类型。
- 新的函数声明语法
- 当函数的返回值依赖于包含参数的表达式,比如:
template<typename T1, typename T2>
auto add(T1 x, T2 y)->decltype(x + y) {...} // 与lambda的声明类似。add的返回类型,是x + y的类型。但是在C++11之前是不可行的。
- 当函数的返回值依赖于包含参数的表达式,比如:
- 作用域受限的枚举
enum class Salution : char{mr, ms, co, none};
- 也叫做“强枚举”或者“枚举类”;
- 它与int之间的隐式转换是不可能的;
- 枚举值,比如mr,不在Salution声明的作用域中,Salution::mr才在;
- 可以用": type"指定构成枚举的数据类型,默认为int;
- 支持前向声明;
- 类型特性std::underling_type可以返回构成枚举值的类型。
- 新的基本数据类型
- char16_t 和 char32_t;
- long long 和 unsigned long long;
- std::nullptr_t
C++11新特性——The C++ standard library, 2nd Edition 笔记(一)的更多相关文章
- C++11的一般概念——The C++ standard library, 2nd Edition 笔记(二)
( 原书第四章,General Concepts) 只介绍新内容,关于头文件格式和后缀等C++03已经规范化的内容,不再赘述. namespace std:新的std子空间包括: std::tr1, ...
- C++ 11学习和掌握 ——《深入理解C++ 11:C++11新特性解析和应用》读书笔记(一)
因为偶然的机会,在图书馆看到<深入理解C++ 11:C++11新特性解析和应用>这本书,大致扫下,受益匪浅,就果断借出来,对于其中的部分内容进行详读并亲自编程测试相关代码,也就有了整理写出 ...
- C++11新特性之一——Lambda表达式
C++11新特性总结可以参考:http://www.cnblogs.com/pzhfei/archive/2013/03/02/CPP_new_feature.html#section_6.8 C++ ...
- C++11新特性总结 (二)
1. 范围for语句 C++11 引入了一种更为简单的for语句,这种for语句可以很方便的遍历容器或其他序列的所有元素 vector<int> vec = {1,2,3,4,5,6}; ...
- C++11新特性总结 (一)
1. 概述 最近在看C++ Primer5 刚好看到一半,总结一下C++11里面确实加了很多新东西,如果没有任何了解,别说自己写了,看别人写的代码估计都会有些吃力.C++ Primer5是学习C++1 ...
- C++ 11 新特性
C++11新特性: 1.auto 2.nullptr 3.for 4.lambda表达式 5.override ...
- [转载] C++11新特性
C++11标准发布已有一段时间了, 维基百科上有对C++11新标准的变化和C++11新特性介绍的文章. 我是一名C++程序员,非常想了解一下C++11. 英文版的维基百科看起来非常费劲,而中文版维基百 ...
- 在C++98基础上学习C++11新特性
自己一直用的是C++98规范来编程,对于C++11只闻其名却没用过其特性.近期因为工作的需要,需要掌握C++11的一些特性,所以查阅了一些C++11资料.因为自己有C++98的基础,所以从C++98过 ...
- C++11新特性——range for
很多编程语言都有range for语法功能,自C++11起,终于将这个重要功能加入C++标准中.range for语句,可以方便的遍历给定序列中的每个元素并对其执行某种操作. 1.基本语法 for(d ...
随机推荐
- git超详细教程留着当手册
GitHub操作流程 : 第一次提交 : 方案一 : 本地创建项目根目录, 然后与远程GitHub关联, 之后的操作一样; -- 初始化Git仓库 :git init ; -- 提交改变到缓存 :gi ...
- 18.scrapy中selector的用法
Selector是一个独立的模块. Selector主要是与scrapy结合使用的. 开启Scrapy shell: 1.打开命令行cmd 2.scrapy shell http://doc.scra ...
- spring security 配置xml 参考
https://blog.csdn.net/zsq520520/article/details/77880491
- task 03-27
To integrate the spring with jpa, Basically completed the jpa of study;To integrate the spring wi ...
- HTML5 Canvas ( 图片绘制 转化为base64 ) drawImage,toDataURL
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title> ...
- delphi常用函数和方法
uses ShellApi, ActiveX, ComObj, ShlObj; function HasText(Text: string; const Values: array of strin ...
- 【Linux】svn环境配置
Ubuntu 安装svn环境配置 1. 安装 sudo apt-get install subversion 安装过程需要数据[Y] 2. svn位置选择 安装完成之后,选择svn目录位置, 将其放在 ...
- Dubbo 分布式服务框架
要想了解Dubbo是什么,我们不防先了解它有什么用. 使用场景:比如我想开发一个网上商城项目,这个网上商城呢,比较复杂,分为pc端web管理后台,微信端销售公众号,那么我们分成四个项目,pc端网站,微 ...
- client / server端用户的登录
# 客户端 import socket import hashlib import json import os import struct sk = socket.socket() # 实例化 sk ...
- 使用ASP.NET AJAX 从脚本中调用Web 服务的应用方法
技能点:通过编写WebService,在页面js中调用WebService来进行数据查询. 网站开发,有些时候需要使用js在页面动态生成一些内容,但还有些数据要通过查询数据库才能获取的. 但由于诸如主 ...