17 C++新功能
17.1
数位分隔符
允许使用单引号(’)作为数为分隔符
my_func(1'123'445,33'566,100);
数位分隔符在需要写大数字时很好用
long long int big_int=100'000'000'000;
除了整肃,还可应用于小数
double pi=3.141'592'653;
编译器实际会忽略数字中的单引号。和注释一样,数为分隔符支队阅读和维护代码的人有用。
字符串字面值后缀
向字符串字面值应用s后缀,使其称为真正的string对象而不是C字符串(字符数组)
"text"s;
原来,string sname=“Jone”+“ Adams“ 这样的语句是错误的,因为C字符串本身(具有char*类型)没有定义+操作符的行为。相反必须使用strcat函数来列娜姐
加了s后缀的字符串字面值称为真正的string对象,能合法写成+语句
string sname="Jone"s + "Adams"s;
s后缀有助于澄清是想要返回一个真正的string对象,而不是char数组
return "Hello!"s; // 作为string对象返回
二进制字面值
C++14允许使用0b或0B写二进制字面值
0bdigits
例如:
cout<<0b110+0b001; //打印7(=0b111)
写掩码更为方便:
cout << data | 0b1111; //低4位开
cout << data & 0b1111; //将除了第四位的其他所有位都遮掩掉(置0)
位操作符
操作符 | 名称 | 说明 |
---|---|---|
& | 按位AND(与) | 两个操作数对应的都是1,就将该位设为1.其他位设为0 |
| | 按位OR(或) | 两个操作数对应的位任何一个是1,就将该位设为1,其他设为0 |
^ | 按位XOR(异或) | 两个操作数对应的不同,就将该位设为1,否则设为0 |
~ | 按位NOT(取反) | 一元操作符。反转操作符每一位的值,例如1变成0,反之亦然 |
可以同时使用单引号数位分割符
cout << ob1111'0000 |0b0000'1111 <<endl;
//结果是255
要打印二进制,一个简单的方式是使用bitset模板。该模板不要指定基础类型,而要指定一个固定位数。bitset打印时会生成一个包含0和1的数字字符串
#include <bitset>
using namespace std;
bitset<8> my_bit(0b1111'0000 & 0b1100'0000);
cout << my_bit <<endl;
//结果位11000000
17.2 C++11引入的功能
long long 类型
64位,限制40亿的平方
(long 32位,short 16位,char 8位)
均有无符版本,无符号类型不能存储负数,但能存储两倍大的正数
除了int本身,其他整形在声明时都可去掉int关键字
使用64位字面值
用LL后缀存储大数字
用ULL后缀表示unsigned long long
//long long n=123000123000456;报错
long long n=123000123000456LL;
long long n=12300123000456ULL;
long long n=123'000'123'000'456ULL;
接收long long输入
之前使用atoi函数将字符串转会为整数
atoll将char* 字符串
char *input_string[MAX_WIDTH+1];
cin.get(input_string,MAX_WIDTH);
long long n=atoll(input_string);
若是输入使用了分隔符,例如123,145,677,789,987,就在转换成数字前剥离这些字
支持atoll函数需要包含<cstdlib>
#define GROUP_SEP ','
long long reat_formatted_input(string s){
for(int i=0;i<s.size();++i){
if(s[i]==GROUP_SEP)
s.erase(i,1);
}
return atoll(s.c_str());
}
格式化long long 数字
用正确数位分隔符打印格式化数字
用到STL stringstream类:该特殊类允许向字符串写入,类似于向控制台或文件写入,要包含一下文件
#include <string>
#include <sstream>
然后创建并使用一个“字符串流”,完成对流的写入后,用str成员函数把他转换为 实际字符串
stringstream s_out;
s_out << "i的值是" << i <<endl;
string s=s_out.str();
现在可以写一个函数来获取long long 作为输入,返回一个格式化字符串
#define GROUP_SEP ','
#define GROUP_SIZE 3
string output_formatted_input(long long num){
//将数据读入字符串s
stringstream temp,out;
temp<<num;
string s=temp.str();
//在第一个分隔符(GROUP_SEP)前写第一组字符
int n=s.size()%GROUP_SIZE;
int i=0;
if(n>0 && s.size()>GROUP_SIZE){
out<<s.substr(i,n)<<GROUP_SEP;
i+=n;
}
//处理其他分组
n=s.size()/GROUP_SIZE-1;
while(n-->0){
out<<s.substr(i,GROUP_SIZE)<<GROUP_SEP;
i+=GROUP_SIZE;
}
out<<s.substr(i);
return out.str();
}
substr函数
第一个参数是起始位置(基于0),第二个参数是从起始位置开始选取的字符数
substr函数返回从起始位置到所需个数的字符串
本地化数字
#define 符号名称 替代文本
基于范围的for(For Each)
处理容器中的每一项,而不必关心从哪里开始或者从哪里结束
两种常规语法(区别是**&**)
for (基类型& 变量 : 容器) //传引用
语句
for(基类型 变量 : 容器) //传值
语句
第一种形式,变量是引用类型,意味着能修改原始数据,要修改容器中的值,推荐使用该类型
第二种形式只能访问值的拷贝
int my_array[10];
for(int& i : my_array){
i=0;
}
//将my——arr的每个成员设为5
for(int& i : my_array){
i=5;
}
如果不打算修改任何值,就拿掉&符号保护数据
for(int i : my_array){
cout<<i<<endl;
}
保留&符号也能保护值,但是要将循环变量声明位const
for(const int& i : my_array){
cout<<i<<endl;
}
基于范围的for,容器可以是下面的任意一种:
- 任何数组
- STL string对象(string对象中的元素就是单独的字符),基类型是char
- 定义了迭代器的STL类的实例,如list和vector
- 初始化列表
基于范围的for支持大括号中的初始化列表,如一下打印大量数字的最简方式
for(int& i : {1,1,2,3,5,8,13,21,34,55,89,144}){
cout<<i<<endl;
}
int j=0;
for(int& i : array){
i=j++;
}
关键字auto和decltype
auto关键字可以指定自动(基于栈的)存储类,除非特都声明为静态,否则局部变量默认都属于该存储类
一旦auto关键字声明变量,变量类型就有上下文决定(由初始化它的东西决定),一旦固定,变量的类型就不能改变。auto并不是用来定义可变数据类型
auto x1=5;
假设w是一个指针数组,包含指向Fraction对象的指针的指针可以这样写:
for (Fraction& **x : w)...
for (auto& x : w)...
for (auto& 变量 : 容器) //传引用
语句
for(auto 变量 : 容器) //传值
语句
关键字decltype返回实参的类型
decltype(x) y;//声明y具有和x一样的类型
关键字nullptr
nullptr:不指向任何地方,指针专用
任何指针设为空(nullptr),作为条件测试是就等价于false
强类型枚举
关键字enum,将元素创建成符号常量,并自动赋予连续的整数0,1,2…(默认从0开始)
enum和class合并使用,不仅创建符号名称,还创建一个类,
这会创建一个独立的命名空间,其中的值除非进行强制类型转换,否则不能赋给其他整型,或从其他整型赋值
扩展enum语法:控制存储
enum class 枚举类型 : 存储类型{
符号
};
例如,可指定C++将你的符号作为unsigned long来实现
enum class Choice : unsighned long{
符号
};
可选择为特定符号指定值
枚举默认从0开始,不显式赋值,每个符号默认是上一个符号的值加1
原始字符串字面值
传统C++要表示,'等字符,必须使用转义字符
C++11规范新的“原始字符串”规范:R“( 和 **)"**之间的一切被视为字符串的一部分,无需对字符进行转义
char s[]=R"(The "file" is C:\\users\\1.txt.)";
R前缀告诉编译器这是一个原始字符串字面值
R"(原始字符串文本)"
还可添加另一个字符(或最长16个字符的字符串)来进一步界定字符串
下面例子使用*做定界符
char s[]=R"*(The "file" is C:\\users\\1.txt.)*";