深入理解C++11

1、很多 现实 的 编译器 都 支持 C99 标准 中的__ func__ 预定 义 标识符 功能, 其 基本 功能 就是 返回 所在 函数 的 名字。

  编译器 会 隐式 地 在 函数 的 定义 之后 定义__ func__ 标识符。

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

  __func__ 可以用于构造函数中。

#include < iostream> 
using namespace std; struct TestStruct {
  TestStruct () : name(__ func__) {}
  const char *name;
}; int main()
{
  TestStruct ts;
  cout << ts. name << endl; // TestStruct
} // 编译 选项: g++ -std= c++ 11 2- 1- 3. cpp

  不过 将__ fun__ 标识符 作为 函数参数的默认值不允许 的,

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

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

#define PR(...) printf(__ VA_ ARGS__)

  一个应用的例子。

#include < stdio. h> 

#define LOG(...) {\ 
  fprintf( stderr,"% s: Line %d:\ t", __FILE__, __LINE__);\
  fprintf( stderr, __VA_ ARGS__);\
  fprintf( stderr,"\ n");\
} int main() {
  int x = 3; // 一些 代码...
  LOG(" x = %d", x); // 2- 1- 5. cpp: Line 12: x = 3
}
// 编译 选项: g++ -std= c++ 11 2- 1- 5. cpp

3、long long 是C99标准。C++11中才将其定为正式标准。事实上,在C++11以前,很多编译器也支持了long long。

  long long 整型 有 两种: long long 和 unsigned long long。 在 C++ 11 中, 标准 要求 long long 整型 可以 在 不同 平台 上有 不同 的 长度, 但 至少 有 64 位。

  我们 在 写 常数 字面 量 时, 可以 使用 LL 后缀( 或是 ll) 标识 一个 long long 类型 的 字面 量, 而 ULL( 或 ull、 Ull、 uLL) 表示 一个 unsigned long long 类型 的 字面 量。

long long int lli = -9000000000000000000LL; 
unsigned long long int ulli = -9000000000000000000ULL;

  下面 的 类型 是 等价 的: long long、 signed long long、 long long int、 signed long long int; 而 unsigned long long 和 unsigned long long int 也是 等价 的。

  对于 printf 函数 来说, 输出 有 符号 的 long long 类型 变量 可以 用 符号% lld, 而无 符号 的 unsigned long long 则 可以 采用% llu。

4、__cplusplus

  比如 在 C++ 03 标准 中,__ cplusplus 的 值 被 预定 为 199711L, 而在 C++ 11 标准 中, 宏__ cplusplus 被 预 定义 为 201103L。

  这点 变化 可以 为 代码 所用。 比如 程序员 在想 确定 代码 是 使用 支持 C++ 11 编译器 进行 编译 时, 那么 可以 按下 面的 方法 进行 检测:

  

#if __cplusplus < 201103L #error "should use C++ 11 implementation" #endif

5、assert,运行时检查

  在 C++ 中, 标准 在< cassert> 或< assert. h> 头 文件 中为 程序员 提供 了 assert 宏, 用于 在 运行时 进行 断言。

  在 C++ 中, 程序员 也可以 定义 宏 NDEBUG 来 禁用 assert 宏。 这对 发布 程序 来说 还是 必要 的。

#ifdef NDEBUG
# define assert( expr) (static_ cast< void> ())
#else
...
#endif

  可以 看到, 一旦 定义 了 NDBUG 宏, assert 宏 将被 展开 为 一条 无意义 的 C 语句( 通常 会被 编译器 优化 掉)。

6、static_assert,静态检查。

  c++11中引入了static_assert。在过去,需要自己手动实现,或使用boost提供的功能 BOOST_STATIC_ASSERT。一个可能的手动实现如下:

#define assert_ static( e) \
do { \
enum { assert_ static__ = /( e) }; \
} while ()

  一个使用样例如下:

static_ assert( sizeof( int) == , "This 64- bit machine should follow this!");
int main()
{
return ;
}

  static_ assert 的 断言 表达式 的 结果 必须 是在 编译 时期 可以 计算 的 表达式, 即 必须 是 常量 表达式。 如果 读者 使用 了 变量, 则 会 导致 错误,

int positive( const int n)
{
static_ assert( n > , "value must > 0");
} // 编译 选项: g++ -std= c++ 11 -c 2- 5- 6. cpp

7、noexcept

  在 excpt_ func 函数 声明 之后, 我们 定义 了 一个 动态 异常 声明 throw( int, double), 该 声明 指出 了 excpt_ func 可能 抛出 的 异常 的 类型。 事实上, 该 特性 很少 被 使用, 因此 在 C++ 11 中 被弃 用了( 参见 附录 B), 而 表示 函数 不会 抛出 异常 的 动态 异常 声明 throw() 也 被 新的 noexcept 异常 声明 所 取代。

  以下的语法已被抛弃:

void excpt_ func() throw( int, double) { ... }

  noexcept 的用法:

void excpt_ func() noexcept (常量 表达式);

  下面的第二个 noexcept 就是 一个 noexcept 操作 符。 当 其 参数 是 一个 有可能 抛出 异常 的 表达式 的 时候, 其 返回 值 为 false, 反之 为 true。

template < class T> void fun() noexcept( noexcept( T())) {}

  C++ 11 标准 中 让 类 的 析构函数 默认 也是 noexcept( true) 的。 当然, 如果 程序员 显 式 地 为 析 构 函数 指定 了 noexcept, 或者 类 的 基 类 或 成员 有 noexcept( false) 的 析 构 函数, 析 构 函数 就 不会 再 保持 默认值。

#include < iostream> using namespace std; 
struct A { ~ A() { throw ; } };
struct B { ~ B() noexcept( false) { throw ; } };
struct C { B b; };
int funA() { A a; }
int funB() { B b; }
int funC() { C c; }
int main() {
  try { funB(); } catch(...){ cout << "caught funB." << endl; // caught funB. }
  try { funC(); } catch(...){ cout << "caught funC." << endl; // caught funC. }
  try { funA(); // terminate called after throwing an instance of 'int' } catch(...){ cout << "caught funA." << endl; } } // 编译 选项: g++ -std= c++ 11 2- 6- 2. cpp

  无论是 析 构 函数 声明 为 noexcept( false) 的 类 B, 还是 包含 了 B 类型 成员 的 类 C, 其 析 构 函数 都是 可以 抛出 异常 的。 只有 什么 都没 有 声明 的 类 A, 其 析 构 函数 被 默认 为 noexcept( true), 从而 阻止 了 异常 的 扩散。

8、在 C++ 98 中, 支持 了 在 类 声明 中 使用 等号“=” 加 初始 值 的 方式, 来 初始化 类 中 静态成员常量。 这种 声明 方式 我们 也称 之为“ 就地” 声明。 就地 声明 在 代码 编写 时 非常 便利, 不过 C++ 98 对 类 中就 地 声明 的 要求 却 非常 高。 如果 静态 成员 不满足 常量 性, 则 不可以 就地 声明, 而且 即使常量 的 静态 成员 也 只能 是 整型 或者 枚举型 才能 就地 初始化。 而非 静态 成员 变量 的 初始化 则 必须 在 构造 函数 中 进行。

class Init{
public: Init(): a( ){}
Init( int d): a( d){}
private:
int a;
const static int b = ;
int c = ; // 成员, 无法 通过 编译
static int d = ; // 成员, 无法 通过 编译
static const double e = . ; // 非 整型 或者 枚举, 无法 通过 编译
static const char * const f = "e"; // 非 整型 或者 枚举, 无法 通过 编译
}; // 编译 选项: g++ -c 2- 7- 1. cpp

  使用 g++ 的 读者 可能 发现 就地 初始化 double 类型 静态 常量 e 是 可以 通过 编译 的, 不过 这 实际 是 GNU 对 C++ 的 一个 扩展, 并不 遵从 C++ 标准)。

  在 C++ 11 中, 标准 还 允许 使用 等号= 或者 花 括号{} 进行 就地 的 非 静态 成员 变量 初始化。

struct init{ int a = ; double b {. }; };

  就地初始化 和 初始化 列表 并不 冲突。 程序员 可以 为 同一 成员 变量 既 声明 就地 的 列表 初始化, 又在 初始化 列表 中 进行 初始化, 只不过 初始化 列表 总是 看起来“ 后 作用于” 非 静态 成员。 也就是说, 初始化 列表 的 效果 总是 优先于 就地 初始化 的。

  

#include < string> using namespace std; 
class Mem { public: Mem( int i): m( i){} private: int m; }; class Group {
 public:
  Group(){} // 这里 就不 需要 初始化 data、 mem、 name 成员 了
  Group( int a): data( a) {} // 这里 就不 需要 初始化 mem、 name 成员 了
  Group( Mem m) : mem( m) {} // 这里 就不 需要 初始化 data、 name 成员 了
  Group( int a, Mem m, string n): data( a), mem( m), name( n){}
 private:
  int data = 1;
  Mem mem{ 0};
  string name{" Group"};
}; // 编译 选项: g++ 2- 7- 4. cpp -std= c++ 11 -c

  对于 非 常量 的 静态 成员 变量, C++ 11 则 与 C++ 98 保持 了 一致。 程序员 还是 需要 到头 文件 以外 去 定义 它, 这 会 保证 编译 时, 类 静态 成员 的 定义 最后 只 存在 于 一个 目标 文件 中。

9、sizeof

  不过 在 C++ 98 标准 中, 对 非静态成员变量 使用 sizeof 是 不能 够 通过 编译 的。

#include < iostream>
using namespace std;
struct People {
public:
int hand;
static People * all;
};
int main() {
People p;
cout << sizeof( p. hand) << endl; // C++ 98 中 通过, C++ 11 中 通过
cout << sizeof( People:: all) << endl; // C++ 98 中 通过, C++ 11 中 通过
cout << sizeof( People:: hand) << endl; // C++ 98 中 错误, C++ 11 中 通过
} // 编译 选项: g++ 2- 8- 1. cpp

  而在 C++ 98 中, 只有 静态 成员, 或者 对象的实例 才能 对其 成员 进行 sizeof 操作。 因此 如果 读者 只有 一个 支持 C++ 98 标准 的 编译器, 在 没有 定义 类 实例 的 时候, 要 获得 类 成员 的 大小, 我们 通常 会 采用 以下 的 代码:

sizeof((( People*) )-> hand);

10、而在 C++ 11 中, 我们 无需 这样 的 技巧, 因为 sizeof 可以 作用 的 表达式 包括了 类 成员 表达式。

而在 C++  中, 我们 无需 这样 的 技巧, 因为 sizeof 可以 作用 的 表达式 包括了 类 成员 表达式。

11、friend

  C++98 中无法对 typedef 进行 friend. C++11中可以,并且C++11中省略了 class.

class Poly;
typedef Poly P;
class LiLei {
friend class Poly; // C++ 98 通过, C++ 11 通过
};
class Jim {
friend Poly; // C++ 98 失败, C++ 11 通过
};
class HanMeiMei {
friend P; // C++ 98 失败, C++ 11 通过
}; // 编译 选项: g++ -std= c++ 11 2- 9- 1. cpp

  C++11 中可以为类模板声明友元,这在C++98中是无法做到的。

class P; 
template < typename T>
class People { friend T; };
People< P> PP; // 类型 P 在这里 是 People 类型 的 友 元
People< int> Pi; // 对于 int 类型 模板 参数, 友 元 声明 被 忽略
// 编译 选项: g++ -std= c++ 11 2- 9- 2. cpp

12、final / override

  C++98 中没有方法阻止 vritual 函数被重载。

  

  C++11 中添加了 final 关键字来实现此功能。

  

  C++11 中,继承类重载时,最好加上 override 关键字,有了override 关键字,编译器可以帮助检测重载是否成功。

struct Base {
virtual void Turing() = ;
virtual void Dijkstra() = ;
virtual void VNeumann( int g) = ;
virtual void DKnuth() const;
void Print();
}; struct DerivedMid: public Base {
// void VNeumann( double g); // 接口 被 隔离 了, 曾 想 多 一个 版本 的 VNeumann 函数
}; struct DerivedTop : public DerivedMid {
void Turing() override;
void Dikjstra() override; // 无法 通过 编译, 拼写 错误, 并非 重载
void VNeumann( double g) override; // 无法 通过 编译, 参数 不一致, 并非 重载
void DKnuth() override; // 无法 通过 编译, 常量 性 不一致, 并非 重载
void Print() override; // 无法 通过 编译, 非 虚 函数 重载
; // 编译 选项: g++ -c -std= c++ 11 2- 10- 3. cpp

13、函数模板的默认参数

  C++98中,只有类模板参数可以有默认值,而函数模板不行。C++11中,这一限制已经解除了。

void DefParm( int m = ) {} // c++ 98 编译 通过, c++ 11 编译 通过 

template < typename T = int>
class DefClass {}; // 类模板参数,c++ 98 编译 通过, c++ 11 编译 通过 template < typename T = int>
void DefTempParm() {}; // 函数模板参数,c++ 98 编译 失败, c++ 11 编译 通过

  类模板的参数必须从右到左,而函数模板则无此规定。

template< typename T1, typename T2 = int>
class DefClass1; template< typename T1 = int, typename T2>
class DefClass2; // 无法 通过 编译 template< typename T, int i = >
class DefClass3; template< int i = , typename T>
class DefClass4; // 无法 通过 编译 template< typename T1 = int, typename T2>
void DefFunc1( T1 a, T2 b); template< int i = , typename T>
void DefFunc2( T a);

14、模板的显式实例化、外部模板声明。

  C++98中已经有了模板显式实例化的功能。

// 对于以下模板
template < typename T> void fun( T) {} // 像下面这样显式实例化
template void fun< int>( int);

  C++11中加入了外部模板声明的功能。外部 模板 的 声明 跟 显 式 的 实例 化 差不多, 只是 多了 一个 关键字 extern。

extern template void fun< int>( int);

  如果 外部模板声明 出现于某个编译单元 中, 那么与之对应的显示实例化必须出现于另一个编译单元中或者同一个 编译 单元 的 后续 代码 中; 外部 模板 声明 不能用于静态函数( 即 文件域函数), 但可以 用于 类静态成员函数( 这一点 是 显而易见 的, 因为 静态 函数 没有 外部 链接 属性, 不可能 在 本 编译 单元 之外 出现)。

  外部 模板 定义 更应该 算作 一种 针对 编译器 的 编译时间及空间 的优化手段。 很多 时候, 由于 程序员 低估 了 模板 实例 化 展开 的 开销, 因此 大量 的 模板 使用 会在 代码 中产 生 大量 的 冗余。 这种冗余, 有的时候 已经 使得 编译器和链接器 力不从心。

15、局部、匿名类型作模板实参

  局部的类型匿名的类型 在 C++ 98 中 都不 能做 模板类的实参

template < typename T>
class X {}; template < typename T>
void TempFun( T t){}; struct A{} a;
struct {int i;} b; // b是 匿名类型变量
typedef struct {int i;} B; // B是 匿名类型 void Fun() {
struct C {} c; // C是 局部类型 X< A> x1; // C++ 98 ok, C++ 11 通过
X< B> x2; // C++ 98 fail, C++ 11 通过
X< C> x3; // C++ 98 fail, C++ 11 通过 TempFun( a); // C++ 98 ok, C++ 11 通过
TempFun( b); // C++ 98 fail, C++ 11 通过
TempFun( c); // C++ 98 fail, C++ 11 通过
}

  除了 匿名的结构体(anonymous struct)之外, 匿名的联合体(anonymous union) 以及 枚举类型(union), 在 C++ 98 标准 下 也都是无法做 模板的实参的。

  如今看来这都是不必要的限制。 所以 在 C++ 11 中标准允许了以上类型做模板参数的做法。虽然如此,但以下写法是不被接受的。

template < typename T>
struct MyTemplate { };
int main() {
MyTemplate< struct { int a; }> t; // 无法 编译 通过, 匿名类型的声明不能在模板实参位置
return ;
} // 编译 选项: g++ -std= c++ 11 2- 13- 2. cpp

深入理解C++11的更多相关文章

  1. C++ 11学习和掌握 ——《深入理解C++ 11:C++11新特性解析和应用》读书笔记(一)

    因为偶然的机会,在图书馆看到<深入理解C++ 11:C++11新特性解析和应用>这本书,大致扫下,受益匪浅,就果断借出来,对于其中的部分内容进行详读并亲自编程测试相关代码,也就有了整理写出 ...

  2. 深入理解C++11【5】

    [深入理解C++11[5]] 1.原子操作与C++11原子类型 C++98 中的原子操作.mutex.pthread: #include<pthread.h> #include <i ...

  3. 深入理解C++11【4】

    [深入理解C++11[4]] 1.基于范围的 for 循环 C++98 中需要告诉编译器循环体界面范围.如for,或stl 中的for_each: int main() { ] = { , , , , ...

  4. 深入理解C++11【3】

    [深入理解C++11[3]] 1.POD类型 Plain Old Data. Plain 表示 了POD是个普通的类型.C++11将POD划分为两个基本概念的合集: 1)平凡的(trivial) 2) ...

  5. 深入理解C++11【2】

    [深入理解C++11[2]] 1.继承构造函数. 当基类拥有多个构造函数的时候,子类不得不一一实现. C++98 可以使用 using 来使用基类的成员函数. #include < iostre ...

  6. 发现《深入理解C++11》中扩展的friend代码的错误

    目前在总结现代C++的新特性,看了<深入理解C++11>这本书. 今天看到扩展的friend语法这一节,遇到了问题.本节电子版内容参见:https://book.2cto.com/2013 ...

  7. c++学习书籍推荐《深入理解C++11 C++11新特性解析与应用》下载

    百度云及其他网盘下载地址:点我 编辑推荐 <深入理解C++11:C++11新特性解析与应用>编辑推荐:C++标准委员会成员和IBM XL编译器中国开发团队共同撰写,权威性毋庸置疑.系统.深 ...

  8. 理解C++11正则表达式(2)

    今天有幸(2016/3/19)在上海参加了C++交流会,见到了梦寐已久想见的台湾C++大神老师侯捷,心情十分的激动.侯老师对C++理解的深刻,让人叹为观止.以为他教学的严谨,说话方式娓娓道来,听着非常 ...

  9. 理解c++11正则表达式 (1)

    概要 C++11提出了正则表达式这个概念,只需在头文件中包含#include<regex>即可.我们可以完成: Match 将整个输入拿来比对匹配某个正则表达式 Search 查找与正则表 ...

随机推荐

  1. isp

    Lens Shading可细分为Luma Shading(亮度均匀性)和Color Shading(色彩均匀性)两种.其中,Luma Shading就是我们常说的暗角.既图像呈现出中心区域较亮,四周偏 ...

  2. 爬虫-day02-抓取和分析

    ###页面抓取### 1.urllib3     是一个功能强大且好用的HTTP客户端,弥补了Python标准库中的不足     安装: pip install urllib3     使用: imp ...

  3. xirr函数

    内部收益计算函数 曾经看过一个帖子:有一个理财产品,每年年初存入10000元,每年年底得到利息1000元.持续5年,5年后返还本金50000元:问:利率是多少?下面有个回复:每年存10000,利息10 ...

  4. [蓝桥杯]PREV-26.历届试题_最大子阵

    问题描述 给定一个n*m的矩阵A,求A中的一个非空子矩阵,使这个子矩阵中的元素和最大. 其中,A的子矩阵指在A中行和列均连续的一块. 输入格式 输入的第一行包含两个整数n, m,分别表示矩阵A的行数和 ...

  5. 知识点:tuple 元素真的不可变吗

    tuple 元素真的不可变吗 有一种有序列表叫元组:tuple. tuple和list非常类似,但是tuple一旦初始化就不能修改,比如同样是列出单位或同学的名字: >>> clas ...

  6. 使用sqlplus创建Oracle表空间

    登录 dos窗口输入以下命令:sqlplus  回车 提示输入用户名——即创建数据库实例时的用户名 ,用户名输入:sys as sysdba 密码.........回车登录 查看数据文件位置 接下来, ...

  7. How To Upgrade ASMLib Kernel Driver as Part of Kernel Upgrade? (文档 ID 1391807.1)

    How To Upgrade ASMLib Kernel Driver as Part of Kernel Upgrade? (文档 ID 1391807.1)

  8. 好用的treeGrid

    jquery easyui 官网:http://www.jeasyui.net/plugins/186.html  下面以学校班级情况,先贴出效果图吧! 数据库设计:红色框中为必须要有的列,右边三个为 ...

  9. php中一个"异类"语法: $a && $b = $c; 【转载】

    $a = 1;$b = 2;$c = 3;$a && $b = $c;echo "a:$a";echo "b:$b";echo "c: ...

  10. SAP Solution Manager 能够连接到 SAP Service Marketplace

    使用 在该步骤中,您要确保 SAP Solution Manager 能够连接到 SAP Service Marketplace. 作业 SAP Support Portal(SAPOSS)的 RFC ...