C++11的新特性简单汇总介绍 (一)

什么是C++11

C++11是曾经被叫做C++0x,是对目前C++语言的扩展和修正,C++11不仅包含核心语言的新机能,而且扩展了C++的标准程序库(STL),并入了大部分的C++ Technical Report 1(TR1)程序库(数学的特殊函数除外)。

C++11包括大量的新特性:包括lambda表达式,类型推导关键字auto、decltype,和模板的大量改进。

1. 概述

最近在看C++ Primer5 刚好看到一半,总结一下C++11里面确实加了很多新东西,如果没有任何了解,别说自己写了,看别人写的代码估计都会有些吃力。C++ Primer5是学习C++11的比较好的书籍。这篇文章仅总结关于C++11中的新东西,老的东西不再赘述。本文的所有代码仅仅值列出关键代码,并且所有特性都已经用编译器验证过,我的编译环境 gcc 5.3.1  g++ 5.3.1 ,据说 4.7以上的版本已经支持大部分C++11的特性,VS系列的编译器对C++11的支持情况不甚了解,如果没有合适的编译器,可以点击这里 C++shell 这是一个在线的C++编译系统,里面有多个选项可以选择C++98,C++11,C++14等,可以在这里面验证C++11的正确性。

2. long long 类型

long long 类型实际上没有在C++ 98中存在,而之后被C99标准收录,其实现在市面上大多数编译器是支持 long long 的,但是这个类型正式成为C++的标准类型是在C++11中。标准要求long long至少是64位也就是8个字节。一个字面常量使用LL后缀表示long long类型,使用ULL后缀表示unsigned long long 类型。

3. 列表初始化

C++11中全面加入了列表初始化的功能,包括对vector,map,值类型,struct等等都可以使用列表初始化,还可以在函数中返回一个花括号括起来的列表,而在这之前我们只能对数组进行列表初始化:

//数组列表初始化
int xx[5]={1,2,3,4,5};
int yy[]={6,7,8,9,0};

//值类型进行初始化
int a{10};
int b={10};
int c={10.123}; // 编译器报错,g++ 5.3.1当列表初始化用于值类型的时候,如果有精度损失,编译器会报错。

//列表初始化还可以用结构体
typedef struct Str{
  int x;
  int y;
}Str;
Str s = {10,20};

//列表初始化类,必须是public成员,如果含有私有成员会失败
class Cls{
public:
  int x;
  int y;
};
Cls c = {10,20};

//vector不仅可以使用列表初始化,还可以使用列表进行赋值,数组不能用列表赋值
vector<int>v1={1,2,3,4,5,6,7,8,9}; // 初始化
vector<int>v2;
v2={3,4,5,6,7}; //赋值

//map列表初始化
map<string ,int> m = {
   {"x",1},
   {"y",2},
   {"z",3}
};

//用函数返回初始化列表只展示关键代码,相关头文件自行添加
//同理结构体,类,map的返回也可以使用初始化列表返回
vector<int> getVector()
{
 return {1,2,3,4,5};
}

int main()
{
 vector<int> v = getVector();
 cout<<v[0]<<v[1]<<v.size()<<endl;
 return 0 ;
}

4. nullptr 空指针

C++11中新加入的字面值表示不指向任何对象的空指针,以前我们常常用一个预定义的宏NULL来表示空指针,实际上NULL的值是0,新标准推荐使用nullptr而不是NULL

5. constexpr变量

我们在定义常量的时候一般使用const来定义,一个常量必须在定义的时候进行初始化,并且之后不可更改。一个常量必须使用一个常量表达式进行初始化,并且在编译期间就可以得到常量的值,但是如何确定一个表达式就是常量表达式呢,这个通常是由程序员自己确定的,例如:

const int a =20;
//20是一个字面值,当然也是一个常量表达式,所以用20来为a赋值是没有问题的
//然而下面的代码也可以通过编译,g++ 5.3.1
int a = 20 ;
const int x = a;
int b[x]={0};

为常量x赋值的是一个变量a,这样做应该是不合理的,但是编译器没有报告任何错误,当然这种错误是显而易见的,但是在复杂的系统中如何判断一个表达式是否是常量表达式是很困难的,例如这里的a我们一眼就可以判断其并不是一个常量表达式。为此C++11提供了一个新的关键字constexpr,使用该关键字定义的常量,由编译器检查为其赋值的表达式是否是常量表达式,例如上面的代码改成:

int a = 20 ;
constexpr int x = a;

编译器编译的时候就会报错说a并不是常量。显然constexpr关键字将常量表达式的检查转交给编译器处理,而不是程序员自己,所以使用constexpr定义常量要比const安全。

6. constexpr函数

普通的函数一般是不能用来为constexpr常量赋值的,但是C++11允许定义一种constexpr的函数,这种函数在编译期间就可以计算出结果,这样的函数是可以用来为constexpr赋值的。定义constexpr函数需要遵守一些约定,函数的返回类型以及所有形参的类型都应该是字面值,一般情况下函数体中必须有且只有一条return语句。

constexpr int size()
{
  return 42;
}

constexpr int si = size();

执行初始化的时候编译器将函数的调用替换成结果值,constexpr函数体中也可以出现除了return之外的其他语句,但是这些语句在运行时不应该执行任何操作,例如空语句,using声明等。constexpr函数允许其返回值并非是一个字面值,例如:

constexpr int size(int s)
{
  return s*4;
}

int a = 20;
const int b = 30;
constexpr int c = 40;
constexpr int si = size(a); //error a是一个变量所以函数返回的是一个可变的值
constexpr int si1 = size(20); //ok 函数返回的实际上是一个常量
constexpr int si2 = size(b); //ok
constexpr int si3 = size(c); //ok

由上可知constexpr函数并不一定返回常量,如果应用于函数的参数是一个常量表达式则返回常量,否则返回变量,而该函数调用到底是一个常量表达式还是非常量表达式则由编译器来判断。这就是constexpr的好处。

7. using类型别名

类型别名其实早在C语言中就有了,一般情况下我们使用关键字typedef来声明一个类型的别名,在C++11中增加了另一种声明类型别名的方法就是使用using关键字,using关键字在C++11以前一般用来引用命名空间。

typedef int INT; // 右侧符号代表左侧
using INT2 = int; // 左侧符号代表右侧

INT a = 20;
INT2 b = 30;

8. auto类型指示符

我们定义一个变量的时候首先必须确定该变量的类型,而很多时候并不是我们先需要一个变量然后为该变量赋值合适的数据,而是我们有一个值但是我们却不知道该用什么类型的变量存储它,特别是C++的模版使用的非常广泛,有时候要定义一个变量,其类型是很复杂的会带有模版的类型参数,例如一个最常见的例子:

map<string ,int> m ;
map<string,int>::iterator it = m.begin();

上面的例子中我们定义了一个map<string,int>::iterator类型的变量来存放 m.begin()的值,这个例子相对来说还不算困难但是我在开始使用map容器的时候也曾经被搞晕过,如果map是一个 map<string,double> 类型则需要定义一个  map<string.double>::iterator it 来存放了。特别是如果map和vector之间互相嵌套的情况就更容易弄错了。定义这种类型变量的另一个缺点就是一个类型的名字往往会很长,试想一下程序代码中通篇都是这种变量声明,恐怕不会有几个人看着舒服吧。不过没关系C++11为我们定义了一个新的关键字 auto 用来定义变量,而变量的类型由编译器自动根据赋值的表达式推导出来,不需要我们显示定义了。因为auto定义的变量的类型由编译器根据赋值的表达式推导,所以auto定义的变量必须有初始值,否则编译器没法确定该变量的类型。

auto x = 20; // x 是int
auto y = 3.14; // y 是double
map<string ,int> m ;
auto it = m.begin(); //it 是map<string,int>::iterator

这样是不是方便了不少,而且程序看起来更加简洁了
auto可以在一条语句中声明多个变量,但是要保证语句中的基础数据类型只有一个,例如:

auto i=10,*p=&i; // OK i是int,p是int*
auto a=10,b=3.14; // Error 类型是int还是double ?

9. decltype类型指示符

有时候会有这样的需求,我们需要知道一个表达式的类型,并使用该类型去定义一个变量,例如:

int a = 10;
int b = 20;
auto c = a + b; // OK a+b的类型是int,此时c的类型是int,并且c的值是 a+b

auto可以解决部分问题,例如我们定义的变量的类型就是表达式 a+b 的类型,但是如果我们仅仅需要定义一个与表达式 a+b 的类型相同的变量,但是我们又不希望将表达式a+b的值赋值给刚刚定义的变量,我们希望赋另外一个值或者是仅仅定义变量而不赋值呢。 这就需要用到C++11 提供的另一个类型说明符 decltype了。decltype作用于一个表达式,并且返回该表达式的类型,在此过程中编译器分析表达式的类型,并不会计算表达式的值。例如

int a = 10;
int b = 20;
decltype(a+b) c = 50; // OK c的类型就是 a+b 的类型int

对于引用类型decltype有一些特别的地方:

int a = 20 ;
int &b = a;
decltype(b) c ; // Error c是引用类型必须赋值
decltype(b) d = a; // OK d是引用类型,指向a

可以看到decltype如果作用于一个引用类型,其得到的还是一个引用类型。我们知道一个引用类型在使用的时候一般会当作其关联的那个变量的同义词处理,例如如果使用 cout<<b<<endl; 其中b实际上相当于a,但是decltype作用于引用类型的时候会保留引用性质。

如果一个表达式是一个解指针引用的操作,decltype得到的也是一个引用类型:

int a = 20 ;
int *p = &a;
decltype(*p) c = a; // c的类型是int&
c = 50;
cout<<a<<endl; // 输出50

当decltype作用于一个变量的时候,变量加不加括号是有区别的,例如:

int a = 20;
decltype(a) b = 30; //ok b的类型是 int
decltype((a)) c = a ; // ok c的类型是int& 其关联变量 a

加上括号之后编译器会把(a)当作是一个表达式处理,而变量是一种可以作为赋值语句左值的表达式,所以会解释成引用类型。

(0)

相关推荐

  • 结合C++11新特性来学习C++中lambda表达式的用法

    在 C++ 11 中,lambda 表达式(通常称为 "lambda")是一种在被调用的位置或作为参数传递给函数的位置定义匿名函数对象的简便方法. Lambda 通常用于封装传递给算法或异步方法的少量代码行. 本文定义了 lambda 是什么,将 lambda 与其他编程技术进行比较,描述其优点,并提供一个基本示例. Lambda 表达式的各部分 ISO C++ 标准展示了作为第三个参数传递给 std::sort() 函数的简单 lambda: #include <algorit

  • 结合C++11的新特性来解析C++中的枚举与联合

    枚举 枚举是用户定义的类型,其中包含一组称为枚举器的命名的整型常数. 语法 // unscoped enum: enum [identifier] [: type] {enum-list}; // scoped enum: enum [class|struct] [identifier] [: type] {enum-list}; // Forward declaration of enumerations (C++11): enum A : int; // non-scoped enum mu

  • c++11新增的便利算法实例分析

    C++是一门应用非常广泛的程序设计语言,而c++11则新增加了一些便利的算法,这些新增的算法使我们的代码写起来更简洁方便,本文列举一些常用的新增算法,算是做个总结分析,更多的新增算法读者可以参考:http://en.cppreference.com/w/cpp/algorithm. 算法库新增了三个用于判断的算法all_of.any_of和none_of,定义如下: template< class InputIt, class UnaryPredicate > bool all_of( Inp

  • C++ 11实现检查是否存在特定的成员函数

    问题提出 最近工作中遇到这样一个需求:实现一个ToString函数将类型T转换到字符串,如果类型T中含有同名方法ToString则直接调用. 这样一个ToString实现可以使用std::enable_if来做到,但是这里的难点在于如何判断类型T中存在这样一个ToString方法,以便可以放入enable_if中做SFINAE. 检查类中是否存在特定成员 相同的问题在知乎上有人提出过,@孙明琦的答案提供了一个用于检测特定检测子U在类型T下是否有效的检测器is_detected_v.其中用到了一个

  • C++第11版本中的一些强大的新特性小结

    Auto Type Deduction 自动类型推导 auto 关键字让用户得以使用 C++ 内置的类型推导特性. std::string something = somethingthatreturnsastring.getString(); auto something = somethingthatreturnsastring.getString(); Auto 关键字会对上述自变量(something)进行自动推导,得出其应该是 string 类型的结论,并在 auto 出现的地方用正确

  • C++11的新特性简单汇总介绍 (二)

    1. 范围for语句 C++11 引入了一种更为简单的for语句,这种for语句可以很方便的遍历容器或其他序列的所有元素 vector<int> vec = {1,2,3,4,5,6}; for(int x: vec) { cout<<x<<endl; } 2. 尾置返回类型 要想引入尾置类型,我们还得从复杂的类型声明说起.如果我们需要定义一个含有10个int元素的数组,一般是这样的: int arr[10] = {0}; 如果要定义指向这个数组的指针呢: 复制代码 代

  • C++11的新特性简单汇总介绍 (一)

    什么是C++11 C++11是曾经被叫做C++0x,是对目前C++语言的扩展和修正,C++11不仅包含核心语言的新机能,而且扩展了C++的标准程序库(STL),并入了大部分的C++ Technical Report 1(TR1)程序库(数学的特殊函数除外). C++11包括大量的新特性:包括lambda表达式,类型推导关键字auto.decltype,和模板的大量改进. 1. 概述 最近在看C++ Primer5 刚好看到一半,总结一下C++11里面确实加了很多新东西,如果没有任何了解,别说自己

  • PHP 7.1新特性的汇总介绍

    一.可空类型 可空类型主要用于参数类型声明和函数返回值声明. 主要的两种形式如下: <?php function answer(): ?int { return null; //ok } function answer(): ?int { return 42; // ok } function say(?string $msg) { if ($msg) { echo $msg; } } 从例子很容易理解,所指的就是通过 ? 的形式表明函数参数或者返回值的类型要么为指定类型,要么为 null. 此

  • JS ES新特性 扩展运算符介绍

    一.扩展运算符 扩展运算符是三个点... , 允许将一个表达式原地展开,当需要多个参数(比如函数的调用时) 或者多个值(比如数组)它会将其转为用逗号分隔的参数序列. 示例代码如下所示: // 定义一个数组 let arr = [1, 2, 3, 4, 5, 6] // 使用 ... 扩展运算符展开 console.log(...arr); // 1 2 3 4 5 6 // 定义一个函数 function fun(...item) { console.log(...item); } // 调用函

  • PHP 7的一些引人注目的新特性简单介绍

    1. ?? 运算符(NULL 合并运算符) 把这个放在第一个说是因为我觉得它很有用.用法: $a = $_GET['a'] ?? 1; 它相当于: <?php $a = isset($_GET['a']) ? $_GET['a'] : 1; 我们知道三元运算符是可以这样用的: $a ?: 1 但是这是建立在 $a 已经定义了的前提上.新增的 ?? 运算符可以简化判断. 2. 函数返回值类型声明 官方文档提供的例子(注意 ... 的边长参数语法在 PHP 5.6 以上的版本中才有): <?php

  • SQL Server新特性SequenceNumber用法介绍

    简介 SequenceNumber是SQL Server2012推出的一个新特性.这个特性允许数据库级别的序列号在多表或多列之间共享.对于某些场景会非常有用,比如,你需要在多个表之间公用一个流水号.以往的做法是额外建立一个表,然后存储流水号.而新插入的流水号需要两个步骤: 1.查询表中流水号的最大值 2.插入新值(最大值+1) 现在,利用SQL Server2012中的Sequence.这类操作将会变得非常容易. SequenceNumber的基本概念 SequenceNumber的概念并不是一

  • 关于iOS 11的一些新特性适配实践总结

    前言 iOS 11 已经发布了一段时间了,随手记团队也早早的完成了适配.在这里,我们做了点总结,与大家一起分享一下关于 iOS 11 一些新特性的适配. UIView & UIViewController Layout Margins iOS 11 中,官方提供了一种新的布局方法--通过 layout margins 进行布局.官方文档 Positioning Content Within Layout Margins 称,使用这种布局可以保证各个 content 之间不会相互覆盖. 总的来说,

  • C++11新特性“=default”,“=delete”的使用

    1. =default 和=delete 概述 任何事物的出现都必然有着其出现的理由,伴随着每一个新的概念产生都会带来一系列的便利和价值.C++在不断的演变与发展,与此同时,伴随着许多新的特性和功能产生.=default.=delete 是C++11的新特性,分别为:显式缺省(告知编译器生成函数默认的缺省版本)和显式删除(告知编译器不生成函数默认的缺省版本).C++11中引进这两种新特性的目的是为了增强对"类默认函数的控制",从而让程序员更加精准地去控制默认版本的函数.其具体的功能和使

  • ES11新增的这9个新特性,你都掌握了吗

    ECMAScript 2020 是 ECMAScript 语言规范的第11版.自1997年出版第一版以来,ECMAScript 已发展成为世界上使用最广泛的通用编程语言之一. ES2020(ES11) 引入了以下新特性: String 的 matchAll 方法 动态导入语句 import() import.meta export * as ns from 'module' Promise.allSettled 一种新的数据类型:BigInt GlobalThis Nullish coalesc

随机推荐