《深入理解C++11:C++ 11新特性解析与应用》——第2章 保证稳定性和兼容性 2.1 保持与C99兼容

简介: 本节书摘来自华章计算机《深入理解C++11:C++ 11新特性解析与应用》一书中的第2章,第2.1节,作者 IBM XL编译器中国开发团队,更多章节内容可以访问云栖社区“华章计算机”公众号查看。

第 2 章

保证稳定性和兼容性

作为C语言的嫡亲,C++有一个众所周知的特性—对C语言的高度兼容。这样的兼容性不仅体现在程序员可以较为容易地将C代码“升级”为C++代码上,也体现在C代码可以被C++的编译器所编译上。新的C++11标准也并不例外。在C++11中,设计者总是保证在不破坏原有设计的情况下,增加新的特性,以充分保证语言的稳定性与兼容性。本章中的新特性基本上都遵循了该设计思想。

2.1 保持与C99兼容

类别:部分人

在C11之前最新的C标准是1999年制定的C99标准。而第一个C++语言标准却出现在1998年(通常被称为C++98),随后的C++03标准也只对C++98进行了小的修正。这样一来,虽然C语言发展中的大多数改进都被引入了C++语言标准中,但还是存在着一些属于C99标准的“漏网之鱼”。所以C++11将对以下C99特性的支持也都纳入了新标准中:

image

这些特性并不像语法规则一样常用,并且有的C++编译器实现也都先于标准地将这些特性实现,因此可能大多数程序员没有发现这些不兼容。但将这些C99的特性在C++11中标准化无疑可以更广泛地保证两者的兼容性。我们来分别看一下。

2.1.1 预定义宏

除去语法规范等,包括标准库的接口函数定义、相关的类型、宏、常量等也都会被发布在语言标准中。相较于C89标准,C99语言标准增加一些预定义宏。C++11同样增加了对这些宏的支持。我们可以看一下表2-1。

image

使用这些宏,我们可以查验机器环境对C标准和C库的支持状况,如代码清单2-1所示。

image

在我们的实验机上,__STDC_VERSION__这个宏没有定义(也是符合标准规定的,如表2-1所示),其余的宏都可以打印出一些常量值。

预定义宏对于多目标平台代码的编写通常具有重大意义。通过以上的宏,程序员通过使用#ifdef/#endif等预处理指令,就可使得平台相关代码只在适合于当前平台的代码上编译,从而在同一套代码中完成对多平台的支持。从这个意义上讲,平台信息相关的宏越丰富,代码的多平台支持越准确。

不过值得注意的是,与所有预定义宏相同的,如果用户重定义(#define)或#undef了预定义的宏,那么后果是“未定义”的。因此在代码编写中,程序员应该注意避免自定义宏与预定义宏同名的情况。

2.1.2 __func__预定义标识符

很多现实的编译器都支持C99标准中的__func__ 预定义标识符功能,其基本功能就是返回所在函数的名字。我们可以看看下面这个例子,如代码清单2-2所示。

image

在代码清单2-2中,我们定义了两个函数hello和world。利用__func__预定义标识符,我们返回了函数的名字,并将其打印出来。事实上,按照标准定义,编译器会隐式地在函数的定义之后定义__func__标识符。比如上述例子中的hello函数,其实际的定义等同于如下代码:

const char* hello() { 
    static const char* __func__ = "hello"; 
    return __func__; 
}

__func__预定义标识符对于轻量级的调试代码具有十分重要的作用。而在C++11中,标准甚至允许其使用在类或者结构体中。我们可以看看下面这个例子,如代码清单2-3所示。

image

从代码清单2-3可以看到,在结构体的构造函数中,初始化成员列表使用__func__预定义标识符是可行的,其效果跟在函数中使用一样。不过将__fun__标识符作为函数参数的默认值是不允许的,如下例所示:

void FuncFail( string func_name = __func__) {};// 无法通过编译

这是由于在参数声明时,__func__还未被定义。

2.1.3 _Pragma操作符

在C/C++标准中,#pragma是一条预处理的指令(preprocessor directive)。简单地说,#pragma是用来向编译器传达语言标准以外的一些信息。举个简单的例子,如果我们在代码的头文件中定义了以下语句:

#pragma once

那么该指令会指示编译器(如果编译器支持),该头文件应该只被编译一次。这与使用如下代码来定义头文件所达到的效果是一样的。

#ifndef THIS_HEADER
#define THIS_HEADER
// 一些头文件的定义
#endif

在C++11中,标准定义了与预处理指令#pragma功能相同的操作符_Pragma。_Pragma操作符的格式如下所示:

_Pragma (字符串字面量)

其使用方法跟sizeof等操作符一样,将字符串字面量作为参数写在括号内即可。那么要达到与上例#pragma类似的效果,则只需要如下代码即可。

_Pragma("once");

而相比预处理指令#pragma,由于_Pragma是一个操作符,因此可以用在一些宏中。我们可以看看下面这个例子:

#define CONCAT(x) PRAGMA(concat on #x)
#define PRAGMA(x) _Pragma(#x)
CONCAT( ..\concat.dir )

这里,CONCAT( ..concat.dir )最终会产生_Pragma(concat on "..concat.dir")这样的效果(这里只是显示语法效果,应该没有编译器支持这样的_Pragma语法)。而#pragma则不能在宏中展开,因此从灵活性上来讲,C++11的_Pragma具有更大的灵活性。

2.1.4 变长参数的宏定义以及 VA_ARGS

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

#define PR(...) printf(__VA_ARGS__)

就可以定义一个printf的别名PR。事实上,变长参数宏与printf是一对好搭档。我们可以看如代码清单2-4所示的一个简单的变长参数宏的应用。

image

在代码清单2-4中,定义LOG宏用于记录代码位置中一些信息。程序员可以根据stderr产生的日志追溯到代码中产生这些记录的位置。引入这样的特性,对于轻量级调试,简单的错误输出都是具有积极意义的。

2.1.5 宽窄字符串的连接

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

事实上,在C++11中,我们还定义了更多种类的字符串类型(主要是为了更好地支持Unicode),更多详细的内容,读者可以参见8.3与8.4节。

相关文章
|
1天前
|
Linux 编译器 调度
xenomai内核解析--双核系统调用(二)--应用如何区分xenomai/linux系统调用或服务
本文介绍了如何将POSIX应用程序编译为在Xenomai实时内核上运行的程序。
16 1
xenomai内核解析--双核系统调用(二)--应用如何区分xenomai/linux系统调用或服务
|
2天前
|
自然语言处理 编译器 C语言
【C++】C++ 入门 — 命名空间,输入输出,函数新特性
本文章是我对C++学习的开始,很荣幸与大家一同进步。 首先我先介绍一下C++,C++是上个世纪为了解决软件危机所创立 的一项面向对象的编程语言(OOP思想)。
29 1
【C++】C++ 入门 — 命名空间,输入输出,函数新特性
|
4天前
|
供应链 搜索推荐 API
API在电子商务中的应用与优势:深入解析
API是电子商务成功的关键,它们不仅促进了技术创新,还提高了用户体验和运营效率。随着技术的不断进步,API将继续在电子商务领域发挥更加重要的作用。电子商务平台通过利用API,可以更加灵活地适应市场变化,提供更加丰富和个性化的购物体验,最终实现业务的增长和扩展。
|
5天前
|
缓存 自然语言处理 JavaScript
万字长文深度解析JDK序列化原理及Fury高度兼容的极致性能实现
Fury是一个基于JIT动态编译的高性能多语言原生序列化框架,支持Java/Python/Golang/C++/JavaScript等语言,提供全自动的对象多语言/跨语言序列化能力,以及相比于别的框架最高20~200倍的性能。
|
10天前
|
C++
【期末不挂科-C++考前速过系列P6】大二C++实验作业-模板(4道代码题)【解析,注释】
【期末不挂科-C++考前速过系列P6】大二C++实验作业-模板(4道代码题)【解析,注释】
【期末不挂科-C++考前速过系列P6】大二C++实验作业-模板(4道代码题)【解析,注释】
|
10天前
|
Serverless C++ 容器
【期末不挂科-C++考前速过系列P5】大二C++实验作业-多态性(3道代码题)【解析,注释】
【期末不挂科-C++考前速过系列P5】大二C++实验作业-多态性(3道代码题)【解析,注释】
|
10天前
|
C++ 芯片
【期末不挂科-C++考前速过系列P4】大二C++实验作业-继承和派生(3道代码题)【解析,注释】
【期末不挂科-C++考前速过系列P4】大二C++实验作业-继承和派生(3道代码题)【解析,注释】
|
10天前
|
编译器 C++
【期末不挂科-C++考前速过系列P3】大二C++第3次过程考核(20道选择题&12道判断题&2道代码题)【解析,注释】
【期末不挂科-C++考前速过系列P3】大二C++第3次过程考核(20道选择题&12道判断题&2道代码题)【解析,注释】
|
10天前
|
C++
【期末不挂科-C++考前速过系列P2】大二C++第2次过程考核(20道选择题&10道判断题&3道代码题)【解析,注释】
【期末不挂科-C++考前速过系列P2】大二C++第2次过程考核(20道选择题&10道判断题&3道代码题)【解析,注释】
|
10天前
|
供应链 Java API
Java 8新特性解析及应用区块链技术在供应链管理中的应用与挑战
【4月更文挑战第30天】本文将深入探讨Java 8的新特性,包括Lambda表达式、Stream API和Optional类等。通过对这些新特性的详细解析和应用实例,帮助读者更好地理解和掌握Java 8的新技术。

推荐镜像

更多