1.1 __func__预定义标识符
在c99中,__func__基本功能是返回所在函数的名字,c++11中允许使用在类或结构体中。
1 #include <iostream> 2 using namespace std; 3 4 struct TestStruct { 5 TestStruct() : name(__func__) {} 6 const char *name; 7 }; 8 int main() 9 { 10 TestStruct ts; 11 cout << ts.name << endl; 12 return 0; 13 }
执行结果为:
1.2 _Pragma操作符
在c/c++标准中,#pragma是一条预处理的指令。如在代码头文件中定义如下语句
#pragma once
该命令会指示编译器,该头文件只被编译一次。等价于
#ifndef THIS_HEADER #define THIS_HEADER // ... #endif
在C++11标准定义了预处理指令#pragma功能相同的操作符_Pragma,格式如下:
_Pragma( 字符串字面量 )
使用方法同sizeof等操作符一样,_Pragma ("once") 等价于 #pragma once
#define CONCAT(x) PRAGMA(concat on #x) #define PRAGMA(x) _Pragma(#x) int main() { CONCAT(../concat.dir); // 最终会产生 _Pragma(concat on "../concat.dir") return 0; }
#pragma不支持在宏中展开,而_Pragma支持,C++11的_Pragma具有更大的灵活性。
1.3 变量参数的宏定义__VA_ARGS__
在c99标准中,可以使用参数列表的最后一个参数为省略号来表示变长参数的宏定义。c++11中,预定义宏__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", 3); //执行结果 D:\clion_local_project\test3\main.cpp: Line 12: x = 3 return 0; }
1.4 __cplusplus
在c和c++混合编程时,经常看到如下声明
1 #ifdef __cplusplus 2 extern "C" { 3 #endif 4 //一些代码 5 #ifdef __cplusplus 6 }; 7 #endif
这种类型的头文件可以在c程序中进行编译,也可以在c++程序进行编译。在c++03标准中,__cplusplus的值被预定为199711L,而c++11中为201103L,这个变化可以为代码所用,例如想使用C++11进行编译时,可以使用如下方法:
#if __cplusplus < 201103L #error "should use C++11 implementation" #endif
这样不支持C++11的代码编译会立即报错,并终止编译。
1.5 static_assert静态断言
断言assert宏只有在程序运行时才起作用(动态断言),而#error只有在编译器预处理时才能起作用。
1 #include <cassert> 2 #include <cstring> 3 using namespace std; 4 5 template <typename T, typename U> int bit_copy(T &a, U &b) { 6 assert(sizeof(a) == sizeof(b)); 7 memcpy(&a, &b, sizeof(b)); 8 }; 9 10 int main() { 11 int a = 0x2568; 12 double b; 13 bit_copy(a, b); 14 }
如上,代码编译期间无法触发断言。使用static_assert即可在编译期间触发断言异常。
1 template <typename T, typename U> int bit_copy(T &a, U &b) { 2 static_assert(sizeof(a) == sizeof(b), "the parameters of bit_copy must have same width"); 3 memcpy(&a, &b, sizeof(b)); 4 }; 5 6 int main() { 7 int a = 0x2568; 8 double b; 9 bit_copy(a, b); 10 }
编译报错:
1.6 noexcept操作符
在c++11中,如果noexcept修饰的函数抛出了异常,编译器可以选择直接调用std::terminate()函数来终止程序的运行,阻止了异常的传播,这比基于throw()的效率更高。
#include <iostream> using namespace std; void Throw() {throw 1;} void NoBlockThrow() {Throw();} void BlockThrow() noexcept {Throw();} int main() { try { Throw(); } catch (...) { cout << "Found throw." << endl; } try { NoBlockThrow(); } catch (...) { cout << "Throw is not blocked." << endl; } try { BlockThrow(); } catch (...) { cout << "Found throw 1." << endl; } }
noexcept更大的作用是保证程序的安全,C++11让类中的析构函数默认也是noexcept(true)的,如果显示地为析构函数指定了noexcept,或者类的基类或成员有noexcept(false)的析构函数,析构函数就不会保持默认值。
#include <iostream> using namespace std; struct A { ~A() {throw 1;} }; struct B { ~B() noexcept(false) {throw 2;} }; 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; } try { funC(); } catch (...) { cout << "caught funC." << endl; } try { funA(); } catch (...) { cout << "caught funA." << endl; } }
1.7 非静态成员的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中通过 }
注意最后一个sizeof操作,在C++11中,对非静态成员变量使用sizeof操作是合法的。而在C++98中,只有静态成员,或者对象的实例才能对其成员进行sizeof操作。在C++98中,可以强制转换0为1个People类的指针,继而通过指针的解引用获取其成员变量,再通过sizeof获取大小
sizeof(((People*)0)->hand);
1.8 friend的扩展
在C++11中,声明一个类为另外一个类的友元时,不再需要关键字class。这个特性可以为类的模板声明友友元了。
class P; template <typename T> class People { friend T; }; People<P> PP; //类P在这里是PP的友元 People<int> Pi; //对于int类型的模板参数,友元声明被忽略,这样我们可以在模板实例化时才确定一个模板类是否有友元。
#include <iostream> using namespace std; template <typename T> class DefenderT { public: friend T; void Defence(int x, int y) {} void Tackle(int x, int y) {} private: int pos_x = 15; int pos_y = 0; int speed = 2; int stamina = 120; }; template <typename T> class AttackerT { public: friend T; void Move(int x, int y) {} void SpeedUp(float ratio) {} private: int pos_x = 0; int pos_y = -30; int speed = 3; int stamina = 100; }; using Defender = DefenderT<int>; //普通类定义,使用int做参数 using Attacker = AttackerT<int>; class Validator; using DefenderTest = DefenderT<Validator>; //测试专用的定义,Validator类成为友元 using AttackerTest = AttackerT<Validator>; class Validator { public: void Validate(int x, int y, DefenderTest &d) {d.pos_x = x;} //通过友元来访问 void Validate(int x, int y, AttackerTest &a) {a.speed = y;} //通过友元来访问 }; int main() { DefenderTest d; AttackerTest a; a.Move(15, 30); d.Defence(15, 30); Validator v; v.Validate(7, 0, d); v.Validate(1, -10, a); return 0; }
1.9 final/override控制
final关键字的作用是使派生类不可覆盖它所修饰的虚函数。
如果派生类在虚函数声明时使用了override描述符,那么该函数必须重载其基类中的同名函数。
1.10 模板函数的默认模板参数
C++98模板类声明时允许其有默认的模板参数,却不支持函数模板的默认模板参数,C++11中已经支持。
有多个模板参数时,类模板指定默认值必须“从右往左”的规则进行指定,而函数模板并不是必须的。
1.11 外部模板
使用extern显示的实例化模板。
1.12 局部和匿名类型作为模板参数
C++11 支持局部和匿名类型作为模板的参数。
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; X<A> x1; // C++98通过, C++11通过 X<B> x2; // C++98错误, C++11通过 X<C> x3; // C++98错误, C++11通过 TempFun(a); // C++98通过, C++11通过 TempFun(b); // C++98错误, C++11通过 TempFun(c); // C++98错误, C++11通过 }