1.1开始支持宏_STDC_ 和 _func_
不得不吐槽,g++早就支持这俩宏了,第一个宏_STDC_还有几个衍生的宏,用来控制版本;_func_在函数定义体内使用返回函数名称,不要在形参列表中使用,原因你懂的,函数都还没声明完毕呢。
顺带提一下标准C支持的其他的宏:
__LINE__ 在源代码中插入当前源代码行号
__FILE__ 在源代码中插入当前源代码文件名
__DATE__ 在源代码中插入当前编译日期〔注意和当前系统日期区别开来〕
__TIME__ 在源代码中插入当前编译时间〔注意和当前系统时间区别开来〕
__STDC__ 当要求程序严格遵循ANSIC标准时该标识符被赋值为1。
__cplusplus 当用C++编译程序编译时,标识符__cplusplus就会被定义
1.2_Pragma操作符替代#Pragma宏定义
语法: _Pragma (字符常量)
好处:该操作符可以参与其他宏定义和操作符运算
实例:
#Pragma once 变为: _Pragma("once");
1.3 支持long long int
也是马后炮,一直在用了
C输出格式:long long int :%lld,unsigned long long int: %llu
1.4编译期断言:static_assert()
运行期断言函数:assert(逻辑表达式),表达式为假时调用abort()中断执行
static_assert(逻辑表达式,警告信息字符常量);
注意:逻辑表达式必须在编译期就可以断言真假!!!
请记住,static_asset 是在编译时执行的,不能用于检测运行时的值,向下面函数的参数。
void Divide(int a, int b) { static_assert(b==0, “Bad arguments.....leading to division by zero”); // sorry mate! the above check is not possible via static_assert...use some other means }
static_assert 这个声明对于模板的调试非常有用,编译器快速执行这个常量表示式参数(不能依赖模板参数)。否则编译器当模板实例化时执行这个常量表达式的参数。
1.5 引入noexcept修饰符和noexcept操作符
从语法上讲,noexcept修饰符有两种形式,一种就是简单地在函数声明后加上noexcept关键字。比如:
void excpt_func() noexcept;
另外一种则可以接受一个常量表达式作为参数,如下所示:
void excpt_func() noexcept (常量表达式);
常量表达式的结果会被转换成一个bool类型的值。该值为true,表示函数不会抛出异常,反之,则有可能抛出异常。这里,不带常量表达式的noexcept相当于声明了noexcept(true),即不会抛出异常。
1.6支持快速初始化成员变量
#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
(1)支持在类声明时初始化,其优先级低于构造函数初始化列表,因为后者要晚些发生!!
(2)初始化有两种方式:=和{}
(3)静态常量 成员 和 非常量静态成员初始化方式不变:前者里外都可以初始化,后者只能在类声明外初始化
1.7非静态成员支持sizeof()
这个变化不大,增加了对非对象的非静态成员的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
1.8扩展友元friend语法
看到这个,只想说,C++从此又多了一个奇巧淫技!!
(1)语法声明友元类时,可以不加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
(2)可以为类模板声明友元类!!
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
对于People这个模板类,在使用类P为模板参数时,P是People<P>的一个friend类。而在使用内置类型int作为模板参数的时候,People<int>会被实例化为一个普通的没有友元定义的类型。这样一来,我们就可以在模板实例化时才确定一个模板类是否有友元,以及谁是这个模板类的友元。
1.9多态重载控制:final/override控制
(1)final拒绝重载
(2)override标识重载
struct Object{ virtual void fun() = 0; }; struct Base : public Object { void fun() final; // 声明为final }; struct Derived : public Base { void fun(); // 无法通过编译 }; // 编译选项:g++ -c -std=c++11 2-10-2.cpp
struct Base { virtual void Turing() = 0; virtual void Dijkstra() = 0; virtual void VNeumann(int g) = 0; 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
1.10模板函数支持默认参数
模板类也是支持的。
对于多参数的模板类和模板函数来说,要求不一样!!!
模板类的默认形参书写顺序必须是从右往左,为了方便模板类具现化时类型推导
模板函数随意
template<typename T1, typename T2 = int> class DefClass1; template<typename T1 = int, typename T2> class DefClass2; // 无法通过编译 template<typename T, int i = 0> class DefClass3; template<int i = 0, typename T> class DefClass4; // 无法通过编译 template<typename T1 = int, typename T2> void DefFunc1(T1 a, T2 b); template<int i = 0, typename T> void DefFunc2(T a); // 编译选项:g++ -c -std=c++11 2-11-2.cpp
函数模板的参数推导规则也并不复杂。简单地讲,如果能够从函数实参中推导出类型的话,那么默认模板参数就不会被使用,反之,默认模板参数则可能会被使用。
template <class T, class U = double> void f(T t = 0, U u = 0); void g() { f(1, 'c'); // f<int,char>(1,'c') f(1); // f<int,double>(1,0), 使用了默认模板参数double f(); // 错误: T无法被推导出来 f<int>(); // f<int,double>(0,0), 使用了默认模板参数double f<int,char>(); // f<int,char>(0,0) } // 编译选项:g++ -std=c++11 2-11-3.cpp