深入理解C++11(一)

导语 从最初的代号C++0x到最终的名称C++11,C++的第二个真正意义上的标准姗姗来迟。

C++11是一种新语言的开端。虽然设计C++11的目的是为了要取代C++98/03,相比于C++03标准,C++11则带来了数量可观的变化,包括了约140个新特性,以及对C++03标准中约600个缺陷的修正。因此,从这个角度看来C++11更像是从C++98/03中孕育出的一种新语言。

从使用上,Scott Mayers为C++11创建了另外一种有效的分类方式,Mayers根据C++11的使用者是类的使用者,还是库的使用者,或者特性是广泛使用的,还是库的增强的来区分各个特性。具体的,可以把特性分为类作者需要的(class writer,简称为”类作者”)、库作者需要的(library writer,简称为”库作者”)、所有人需要的(everyone,简称为”所有人”)、部分人需要的(everyone else,简称为”部分人”)。

1 C++11相对于C++98/03有以下几点的增强:

<1>.通过内存模型、线程、原子操作等来支持本地并行编程(Native Concurrency)。

<2>.通过统一初始化表达式、auto、declytype、移动语义等来统一对泛型编程的支持。

<3>.通过constexpr、POD等更好的支持系统编程。

<4>.通过内联命名空间、继承构造函数和右值引用等,以更好地支持库的构建。

2 保持与C99兼容(类别:部分人)

C语言发展中的大多数改进都被引入了C++语言标准中,但还是存在着一些属于C99标准的”漏网之鱼”。所以C++11将对以下C99特性的支持也都纳入了新标准中:(1)C99中的预定义宏 (2)func__预定义标识符 (3)_Pragma操作符 (4)不定参数宏定义__VA_ARGS (5)宽窄字符串连接

2.1  预定义宏

相较于C89标准,C99语言标准增加一些预定义宏。C++11同样增加了对这些宏的支持,如下表:

宏名称

功能描述

—|—

STDC_HOSTED

如果编译器的目标系统环境中包含完整的标准C库,那么这个宏就定义为1,否则宏的值为0

STDC

C编译器用这个宏的值表示编译器的实现是否和C标准一致。C++11中这个宏是否定义以及定成什么值由编译器决定

STDC_VERSION

C编译器通常用这个宏来表示所支持的C标准的版本。C++11中这个宏是否定义以及定成什么值由编译器决定

STDC_ISO_10646

这个宏定义为一个yyyymml格式的整数常量,例如199712L,用来表示C++编译环境符合某个版本的ISO/IEC 10646标准

2.2  __func__预定义标识符

__func__预定于标识符的基本功能是返回所在函数的名字。

2.3  _Pragma操作符

在C/C++标准中,#pragma是一条预处理的指令。简单的说,#pragma是用来向编译器传达语言标准以外的一些信息。如果在代码的头文件中定义了#pragma once语句,那么该指令会指示编译器,该头文件应该只被编译一次。在C++11中,定义了与预处理指令#pragma功能相同的操作符_Pragma格式如下:

 _Pragma(字符串字面量)

相对于预处理指令#pragma,_Pragma是一个操作符,因此可以用在一些宏中。

2.4 变长参数的宏定义以及__VA_ARGS__

在C99标准中,程序员可以使用变长参数的宏定义,指在宏定义中参数列表的最后一个参数为省略号,__VA_ARGS__可以在宏定义的实现部分替换省略号所代表的字符串。

#define PR(…) printf(VA_ARGS)

2.5  宽窄字符串的连接

在之前的C++标准中,窄字符串(char)转换为宽字符串(wchar_t)是未定义的行为。C++11的标准规定,窄字符串和宽字符串进行连接时,支持C++11标准的编译器将窄字符串转换成宽字符串,然后与宽字符串进行连接。

3.1 扩展的整形(类别:部分人)

C++11中一共只定义了5种标准的有符号整型:(1)signed char (2)short int (3)int (4)long int (5)long long int。同时规定,每一种有符号整型都有一种对应的无符号整数版本,且有符号整型与其对应的无符号整型具有相同的存储空间大小。如与signed int对应的无符号版本的整型是unsigned int。

3.2 静态断言(类别:库作者)

3.2.1 断言:运行时与预处理时

断言(assertion)是编程中常用的手段。一般情况下,断言就是将一个返回值总是需要为真的判别式放在语句中,用于排除在设计的逻辑上不应该产生的情况。在某种意义上,断言并不是正常程序所必需的。不过对于调试程序来说,通常断言能够帮助开发GG快速定位那些违反了某些前提条件的程序错误。在C++中,头文件中提供了assert宏,用于在运行时进行断言。

main函数中对ArrayAlloc的使用没有满足n>0的条件,在运行时,出现Assertion n > 0 failed。

3.2.2 静态断言与static_assert

断言assert宏只有在程序运行时才能起作用。而#error只在编译器预处理是才能起作用。在某些场合,希望能在编译时做一些断言。如下述例子:

本例中,使用了assert断言,assert断言的作用是为了保证a和b两种类型的长度一致,这样bit_copy才能够保证复制操作不会遇到越界等问题。因为assert断言是一个运行时断言,如果出现bit_copy不被调用等情况,我们将无法触发该断言。实际上,为了解决上述问题,正确产生断言的时机应该在模板实例化时,即编译时期的断言,也可以称作为“静态断言”。在实际应用中,我们可以利用“除0”会导致编译器报错这个特性来实现静态断言。

无论是哪种方式的静态断言,缺陷都是非常明显的:诊断信息不够充分,不熟悉该静态断言实现的开发GG可能一时无法将错误对应到断言错误上,从而难以准备定位错误的根源。

在C++11的新标准中,引入了static_assert断言来解决上述问题。static_assert接收两个参数,一个是断言表达式,这个表达式通常需要返回一个bool值,一个是警告信息,通常是一段字符串。使用static_assert替换上述bit_copy的声明。

再次编译上述代码,会出现如下信息:

因为static_assert是编译时期的断言,其使用范围不像assert一样受到限制。在通常情况下,static_assert可以用于任何名字空间。

3.3 noexcept修饰符与noexcept操作符(类别:库作者)

相比较断言排除逻辑上不可能存在的状态,异常用于逻辑上可能发生的错误。在异常处理的代码中,开发GG有可能看到过如下的异常声明表达形式:

void excpt_func() throw(int ,double){……}

在excpt_func()函数声明之后,定义了一个动态异常声明throw(int,double),该声明指出了excpt_func()可能抛出的异常的类型。实际情况下,该特性很少被使用。C++11的新标准中,使用noexcept替换了上述特性。在C++11中,如果noexcept修饰的函数抛出了异常,编译器可以选择直接调用std::terminate()函数终止程序的运行。一般情况下,noexcept修饰符有两种形式:(1)void excpt_func() noexcept; (2)void excpt_func() noexcept(常量表达式);第二种形式中的常量表达式的结果会被转换成一个bool类型的值。若该值为true,表示函数不会抛出异常,反之,则有可能抛出异常。

noexcept作为一个操作符是,通常可以用于模板。

template

void fun() noexcept(noexcept(T())){}

这里,fun函数是否是一个noexcept的函数,将由T()表达式是否抛出异常所决定。这里的第二个noexcept就是一个noexcept操作符。

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏性能与架构

ES6 新特性 let 有什么优势?

最近看国外的前端代码时,发现ES6的新特性已经相当普及,尤其是 let,应用非常普遍 虽然 let 的用法与 var 相同,但不管是语法语义上,还是性能上,都提...

3495
来自专栏函数式编程语言及工具

泛函编程(2)-初次体验泛函编程

    泛函编程和数学方程式解题相似;用某种方式找出问题的答案。泛函编程通用的方式包括了模式匹配(pattern matching)以及递归思维(Recursi...

1929
来自专栏nummy

ECMAScript 6 特性ECMAScript 6 特性

ECMAScript 6,也被称做ECMAScript 2015,是ECMAScript标准的下一个版本。这个标准预计将于2015年6月被正式批准。ES6是这门...

621
来自专栏屈定‘s Blog

Java--StringBuilder,StringBuffer,StringJoiner

开始自己的一个半年计划,也就是java相关常用类的源码阅读,通过阅读查漏补缺,加深基础知识的运用与理解.

752
来自专栏张善友的专栏

C# Enum设计和使用的相关技巧

C#的Enum设计的相关指导在网上有相关文章论述: 英文:Design Guidelines Update: Enum Design 中文:C# Idioms:...

1927
来自专栏Java爬坑系列

【Java入门提高篇】Day22 Java容器类详解(五)HashMap源码分析(上)

准备了很长时间,终于理清了思路,鼓起勇气,开始介绍本篇的主角——HashMap。说实话,这家伙能说的内容太多了,要是像前面ArrayList那样翻译一下源码,稍...

2085
来自专栏Play & Scala 技术分享

挑逗 Java 程序员的那些 Scala 绝技

有个问题一直困扰着 Scala 社区,为什么一些 Java 开发者将 Scala 捧到了天上,认为它是来自上帝之吻的完美语言;而另外一些 Java 开发者却对它...

1186
来自专栏对角另一面

lodash源码分析之获取数据类型

所有的悲伤,总会留下一丝欢乐的线索,所有的遗憾,总会留下一处完美的角落,我在冰峰的深海,寻找希望的缺口,却在惊醒时,瞥见绝美的阳光! ——几米 本文为读...

2593
来自专栏偏前端工程师的驿站

(cljs/run-at (JSVM. :all) "细说函数")

前言  作为一门函数式编程语言,深入了解函数的定义和使用自然是十分重要的事情,下面我们一起来学习吧! 3种基础定义方法 defn 定义语法 (defn name...

1829
来自专栏偏前端工程师的驿站

(cljs/run-at (JSVM. :all) "细说函数")

971

扫码关注云+社区