C++ 学习笔记(一些新特性总结 1)

C++ 学习笔记(一些新特性总结 1)

虽然我也用了 C++ 有挺多年了,但是一直本着够用就行的原则,没有特别深入的学习过C++ 的语法,所以好多高级的 C++ 特性都不了解。正好最近从网上找到了本书《C++ 14 Quick Syntax Reference》,挺薄的一本书,只有 100多页,但是覆盖了基本所有 C++ 的特性。这个小短文就是我看这本书时摘抄下来的一些我以前没有注意到的知识点。

文中所有代码都在 gcc version 5.3.0 (Rev1, Built by MSYS2 project) 上测试通过。

16进制、8进制、2进制表示

int myOct = 062;
int myHex = 0x32;
int myBin = 0b00110010;

其中 16进制、8进制表示法是很早就支持的特性。2进制表示是 C++14 才正式支持的。

除此之外,C++14 还引入了单引号作为数字表示的分隔符。方便我们阅读很长的数字。比如下面这个例子:

int longBin = 0b1010‘0101‘1010‘0101;

加了三个单引号作为分割,读起来就方便多了。

NULL 指针(nullptr)

早期的 C++ 中,我们用 0 或者 NULL 来表示无效的指针地址。C++11 专门引入了一个新的关键字 nullptr 来表示 NULL 指针。

int* p = nullptr; // ok

并且 nullptr 还是有类型的,类型为 nullptr_t:

nullptr_t mynull = nullptr; // ok

右值引用类型(C++11)

左值和右值都是针对表达式而言的,左值是指表达式结束后依然存在的持久对象,右值是指表达式结束时就不再存在的临时对象。

所谓右值引用就是引用一个右值对象(临时对象),比如下面的例子:

int&& ref = 1 + 2; // rvalue reference
ref += 3;
cout << ref; // "6"

ref 对应的是 1+2的结果3,这是个临时对象。传统的C++引用方式是无法引用到这个临时对象的。右值引用主要是解决效率问题,具体的方法可以搜索 “C++ 移动语义”。

原生字符串(raw string literals)

raw string 可以取消转义字符的作用,是 C++11 中添加的新特性。比如:

string escaped = "c:\\Windows\\System32\\cmd.exe";

可以简写为:

string raw = R"(c:\Windows\System32\cmd.exe)";

RAW String 开头要用大写的 R ,然后在双引号内要增加一对括号。

这个功能在其他的语言中早就有了。我们上边这个例子还不大能看出 raw string 的优势,但是如果常写正则表达式的话,就会感觉 raw string 太方便了。比如:

char str[] = R"((‘(?:[^\\‘]|\\.)*‘|"(?:[^\\"]|\\.)*")|)";

旧的写法是:

char oldstr[] = "(‘(?:[^\\\\‘]|\\\\.)*‘|\"(?:[^\\\\\"]|\\\\.)*\")|";

新的 char 类型

C++ 11 中引入了 char16_t 和 char32_t 两种类型。这两种字符类型可以分别用来存储 utf-16 和 utf-32 编码的字符。相应的 string 类型也有两个变种, u16string 和u32string。

string s3 = u8"UTF-8 string";
u16string s4 = u"UTF-16 string";
u32string s5 = U"UTF-32 string";

字符串前的 u8、u和 U也是新增的特性,分别用来支持 UTF-8、UTF-16和UTF-32 字符串。

for 循环

C++11 对 for 循环做了扩展,for 循环支持一种新的语法。

int a[3] = {1, 2, 3};
for (int &i : a)
{
    cout <<i; // "123"
}

auto 和 decltype 关键字

这两个关键字都是 C++ 11 中引入的。auto 关键字告诉编译器自动推导变量的类型。比如下面的代码:

auto i = 5; // int
auto d = 3.14; // double
auto b = false; // bool

如果我们希望推导出的类型是引用类型。那么需要在 auto 之后加个 &。比如下面这样:

int& iRef = i;
auto myAuto = iRef; // int
auto& myRef = iRef; // int&

用 auto 之后可以简化许多代码。比如下面这个代码:

vector<int> myVector { 1, 2, 3 };
for(vector<int>::size_type i = 0; i != myVector.size(); i++)
{
    cout << myVector[i];
}

用 auto 的话可以写为:

for(auto i = 0; i != myVector.size(); i++)
{
    cout << myVector[i];
}

当然,用上 for 的新语法,还可以写的更简便:

for (auto& x : myVector)
{
    cout << x << endl;
}

decltype 与 auto 有些类似,它可以用来推导一个表达式的类型,比如下面的例子:

int a = 0b10‘001‘000;
cout << a << endl;
decltype(a) c = a + 1; //int
cout << c << endl;

decltype(3) b = 3; // int&&

需要解释一下的是这个例子中 3 是个临时变量。所以推导出 b 的类型是一个 int 型的右值引用。但是作为函数返回值时,推导出的就不是右值引用了。

decltype(5) getFive() { return 5; } // int

C++ 11 中 auto 和 decltype 还可以配合使用,用来推导函数的返回值类型。下面是个例子:

auto getValue(int x) -> decltype(x) { return x; } // int

这么写还是挺繁琐的, C++ 14 中做了简化。可以简单的写为:

auto getValue(int x) { return x; } // int

不过我感觉这两种函数写法作用都不大,因为我们没法在头文件中把函数声明写为:

auto getValue(int x);

因为没有函数体,根本无法做类型推导…

C++14 中还支持如下的写法:

decltype(auto) = 3; // int&&
decltype(auto) getRef(int& x) { return x; } // int&

这些写法知道也就行了,用处不大。

Lambda 函数

Lambda 函数的概念最早应该来源于 Lisp 语言,现在也被 C++ 11 吸收进来了。

Lambda 函数使得我们可以像定义一个变量一样定义一个函数。比如下面这样:

auto sum = [](int x, int y) -> int {return x + y;};
cout << sum(2, 3);

上面的函数还可以简写为:

auto sum = [](int x, int y) { return x + y; };

编译器会自动推导返回值的类型。

到了 C++ 14 Lambda 函数更是支持了泛型。

auto sum = [](auto x, auto y) {return x + y;};
cout << sum(2, 3) << endl;
cout << sum(2.2, 3.0) << endl;
cout << sum(2.2, 3) << endl;

Lambda 函数也可以作为函数的参数传递给函数。下面是个例子:

#include <iostream>
#include <functional>
using namespace std;
void call(int arg, function<void(int)> func)
{
    func(arg);
}
int main()
{
    auto printSquare = [](int x) { cout << x*x; };
    call(2, printSquare); // "4"
}

上面的例子其实还说明 Lambda 函数不是普通的函数。它是一种特殊类型的对象。比如下面的 Lambda 函数:

auto sum = [](int x, int y) {return x + y;};

写完整了应该是 :

function<int(int)> sum = [](int x, int y) {return x + y;};

Lambda 函数还有一些高级用法。比如Lambda 函数中的 “[]” 是有作用的。用它可以将所在域的其他变量引入到函数中。比如下面的例子:

void call(function<void()> func) { func(); }
int main()
{
    int i = 2;
    auto printSquare = [i]() { cout << i*i; };
    call(printSquare); // "4"
}

Lambda 函数中 [] 里按值传进去的参数是只读的。所以下面的代码是错的:

int a = 10;
int b = [a](int i) { a++; return a * i; } (5); // 50

我们可以添加一个 mutable 关键字使得 a 不是只读,但是 a 值的改变是不会影响函数外面的。

int a = 10;
int b = [a](int i) mutable { a++; return a * i; } (5);
cout << b << endl; // 55
cout << a << endl; // 10

Lambda 函数还可以是无名函数,这时定义函数的同时也要调用这个函数。否则因为这个函数没有名字,之后就没有调用的机会了。下面这两种写法结果是相同的。

cout << [](int i) { return i*i; } (101) << endl;

auto printSquare = [](int i) { return i*i; };
cout << printSquare(101) << endl;

利用 “[]” 也可以把结果传出来。下面这两种方法结果也是相同的。

int a = [](int i) { return i * i; } (11);
cout << a << endl;

[&a] (int i){ a = i * i;}(12);
cout << a << endl;

C++ 14 还支持一些新的特性,比如下面这样:

int a = 1;
[&, b = 2]() { a += b; }();
cout << a; // "3"

关于 Lambda 函数,知道这些也就差不多了。

第一篇先写这么多。下一篇写写关于类和对象的一些新特性。

时间: 2024-10-12 19:45:32

C++ 学习笔记(一些新特性总结 1)的相关文章

JavaScript学习--Item24 ES6新特性概览

ES6新特性概览 本文基于lukehoban/es6features ,同时参考了大量博客资料,具体见文末引用. ES6(ECMAScript 6)是即将到来的新版本JavaScript语言的标准,代号harmony(和谐之意,显然没有跟上我国的步伐,我们已经进入中国梦版本了).上一次标准的制订还是2009年出台的ES5.目前ES6的标准化工作正在进行中,预计会在14年12月份放出正式敲定的版本.但大部分标准已经就绪,且各浏览器对ES6的支持也正在实现中.要查看ES6的支持情况请点此. 目前想要

CSS3新特性(阴影、动画、渐变、变形、伪元素等) CSS3与页面布局学习总结——CSS3新特性(阴影、动画、渐变、变形、伪元素等)

目录 一.阴影 1.1.文字阴影 1.2.盒子阴影 二.背景 2.1.背景图像尺寸 2.2.背景图像显示的原点 三.伪元素 3.1.before 3.2.after 3.3.清除浮动 四.圆角与边框 4.1.border-radius 圆角 4.2.边框图片border-image 五.变形 transform 5.1.rotate()2D旋转 5.2.设置原点 transform-origin 5.3.平移 translate() 5.4.缩放 scale 5.5.斜切扭曲skew 六.渐变

Linux学习笔记:bash特性之多命令执行,shell脚本

今天我们学习了bash特性多命令执行包括各个命令之间的逻辑关系.其中包含"与""或""非"命令的执行.下面即为我们所学习的这些逻辑命令关系之间的关系. 选择执行结构: 逻辑运算: 与:逻辑乘法,&& 0:成功执行 -->true 1-255:失败 -->false true && true =true true && false = false false && true

java第七周学习 jdk1.5新特性

eclipse中常用的快捷键 Alt + /   提示 Ctrl + / 单行注释 / 取消 Ctrl + Shift + / 多行注释 Ctrl + Shift + \ 取消多行注释 Ctrl + 1  快速修复 Ctrl + Shift + o 导包 Alt+上下键       代码位置调换 1.Ctrl + D            删除当前行 重置透视图:window->reset perspective 当eclipse 的Java视图变的很乱的时候,就可以重置透视图,还原为最初的界面

AngularJs学习笔记7——四大特性之模块化设计

模块化设计 1.引用自定义模块并调用 自定义模块中,如果有一些服务.封装好笑模块,在另外一个模块中(声明的时候,在依赖列表中加入要引入的模块) var app02 = angular.module('fan02',['ng','fan']); 就调用fan02模块中所定义的东西 2.ng内置的模块的用法 (1) ngRoute模块(路由模块) AngularJS是一个开源的js框架.用在数据操作比较频繁的场景下,用于SPA应用 单页面应用的工作原理: 1.页面url:http://127.0.0

Linux学习笔记&lt;四&gt;——bash特性

shell:人机交换的接口,将相关命令处理后递交给内核,内核再通过系统调用驱动硬件执行. shell的分类: GUI:Gnome,KDE,Xfce CLI:sh,csh,ksh,bash,tcsh,zsh 程序的概念: 程序是经编译后形成的二进制可执行文件 进程的概念: 进程是程序的副本,是程序执行实例,在每个进程看来,当前主机上只存在内核和当前进程 bash的特性(或者说功能): 1.命令行历史.命令行补全 2.命令行编辑 3.命令别名 4.命令替换 5.文件名通配 6.管道,I/O重定向 7

AngularJs学习笔记2——四大特性之MVC

angularJs的四大特性 ①.采用MVC的设计模式 ②.双向数据绑定 ③.依赖注入 ④.模块化设计 现在细说一下MVC的设计模式: MVC: Model(模型)--项目中的数据 View(视图)--数据的呈现 Controller(控制器)--获取模型数据,选择视图加以呈现. 整个MVC的流程也就是上图的样子,用户行为触发控制器,然后改变模型数据,经过模型的处理,更新相关的视图.形成MVC的环流.下面具体说一下AngularJS中如何实现MVC的步骤的. 使用ng的MVC的基本步骤: ①声明

Python学习笔记4 高级特性_20170618

# 切片(获取list / tuple / 字符串 中指定的元素) l = list(range(10)) l[0:3] l[:3] # 0可以省略 l[:] # 全部 l[3:] # 最后的可以省略 l[-2:-1] # 负数下标,见python笔记2中介绍 l[-10:] # 取最后10个数 l[::2] # 所有数,每个两个取出 # 迭代 通过 for ... in ... 可迭代对象:list.tuple.字符串已经在pythonb笔记2中提到了. 判断是否可迭代 from collec

ES6学习笔记二 新的声明方式和变量的解构赋值!

新的声明方式 在ES5的时候,我们只有一个声明方式,var!但是在es6中,声明进行了扩展,我们加上ES5的var的申明方式,我们有了三种声明方式: var:它是variable的简写,可以理解成变量的意思 let:它在英文中是让的意思,也可以理解成一种申明方式 const:它在英文中是常量的意思,在ES6中用来声明常量,常量可以理解成不变的量=>这里的不变不是值得不变,是内存地址的指针不变!!! var 声明: var在ES中是用来升级成全局变量的,我们可以做一个简单实例,声明一个变量,用co

C#学习笔记第三发---进阶特性

一.异常处理机制 编写的程序在编译不报错之后并不是就不会出错了,在运行时由于逻辑问题或者别的原因还是可能出现各种异常,异常处理机制就是为了处理这种情况.异常处理中需要用到三个关键字,try.catch.finally.其中try下的大括号内写可能出现异常的代码块,catch下的大括号写异常的处理方式(catch需要有输入参数,参数就是异常类,在不确定异常种类时可以用Exception类,因为这是所有异常类的父类),finally下大括号写可能异常的代码块运行结束后的代码,比如释放空间等,写在fi