c++11新特性

C++11特性划分为七大类,分别是:python

1.保持语言的稳定性和兼容性ios

2.更倾向于通用的而不是特殊化的手段来实现特性c++

3.专家新手都支持程序员

4.加强类型的安全性算法

5.加强性能和操做硬件的能力编程

6.开发可以改变人们思惟方式的特性安全

7.融入编程现实ide

 

一、保持语言的稳定性和兼容性函数

(1)新增关键字布局

alignas alignofdecltype auto从新定义 static_assert using从新定义 noexcept export nullptr constexpr thread_local

(2)返回函数名字

const char* hello(){return __func__;};

(3)#pragma once 等价于

#ifndef THIS_HEADER #define THIS_HEADER //... #endif

(4)变长函数参数的宏定义,例子:

#define LOG(...) {\ fprintf(stderr, "%s: line %d: \t", __FILE__, __LINE__); \ fprintf(stderr, __VA_ARGS__); \ fprintf(stderr, "\n") ; \ }

使用方式:

LOG("x=%d", x)

注意加上编译选项 g++ -std=c++11 xx.cpp

(5)__cplusplus 判断编译器类型, 其等于199711L(c++03) 201103L(c++11)

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

(6)运行时检查用assert, 编译时检测用static_assert(1==1, "information")

c++11以前能够经过除0致使编译器出错的方式来模拟一个静态断言

#define assert_static(e) \ do{\ enum{assert_static__ == 1/(e)}; \ }while(0)

(7)noexcept

若是c++11中noexcept修饰的函数抛出了异常,编译器能够选择直接调用std::terminate()来终止程序运行

c++11默认将delete函数设置成noexcept

类析构函数默认也是noexcept(false)的

这个用法能够推广到函数模板

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

这样能够根据条件决定“函数是否抛出异常”

(8)快速初始化成员变量

能够经过=、{}对非静态成员尽快就地初始化,以减小多个构造函数重复初始化变量的工做,注意初始化列表会覆盖就地初始化操做

class Group{ public: Group(){} Group(int a) : data(a) {} Group(Mem m) : mem(m) {} Group(int a, Mem m, string n) : data(a), mem(m), name(n) {} private: int data = 1; Memmem{0}; string name("Group"); };

(9) sizeof能够对People::hand这样的类的非静态成员进行计算,而不用先新建一个对象People p了

(10)类模板能够声明友元了

template<typename T> class People[ friend T; }

(11)经过final和override让编译器辅助作一些函数是否禁止重载或函数是不是重载函数的判断,

这样作的目的是避免——类继承者的编写者有时想重载某个基类的函数,结果因为参数写错或者函数名写错,致使意想不到的误会

(12)c++98中函数有默认参数,模板类有默认模板参数,可是函数模板没有,c++11增长了这一特性,但与类模板有区别

template<T1, T2=int> class xxx; //OK template<T1 = int, T2> class xxx2; //error template<T, int i=0> class xxx3; //OK template<inti=0, T> class xxx4; //error template<T1=int, T2> void fun(T1 a, T2 b); //OK template<inti=0, T> void fun2(T a); //OK

(13)外部模板加快编译器编译速度,以下(固然不加也无妨)

extern template void fun<int>(int);

二、更倾向于通用的而不是特殊化的手段来实现特性

(1)继承构造函数

目的:因为类构造函数是不可继承的,为了不派生类逐个实现基类的构造函数(透传)

例子:

struct A{ A(int i){} A(double d, int i){} A(float f, int i, const char* c){} } struct B:A{ using A::A; }

等价于

struct B{ B(int i){} B(double d, int i ){} B(float f, int i, const char* c){} }

(2)委派构造函数

目的:减小多构造函数的类编写时间,让复杂的构造函数调用简单的构造函数(通用函数),实现这个目的在之前一般采用一个通用的init的类成员函数来实现

例子:有点像变量初始化列表

class Info{ public: Info() {InitRest(); } Info(int i) : Info() {type = i;} Info(char e) : Info() {name = e;} private: void InitRest() { //其余初始化操做} int type{1}; char name{'a'}; };

须要注意的是, : Info()不能与初始化列表一块儿使用

以下面这段是错误的:

Info(int i) : Info(), type(i){}

(3)移动语义

目的:解决深拷贝问题,同时减小函数返回类对象时拷贝构造的次数,注意这里移动构造函数只对临时变量起做用,在以前的解决中都是经过手工定制拷贝构造函数来解决的

概念:

左值——能够取地址的、有名字的就是左值,如 a = b+c中的a

右值——不能取地址的、没有名字的就是右值,如a = b+c中的b+c

右值引用——就是对一个右值进行引用的类型,由于右值没有名字,下面代码是从右值表达式获取其引用:

T && a = ReturnRvalue();

能够理解左值引用是具备名字的变量名的别名,右值引用则是没有名字的变量名的别名,因此必须当即初始化(如上)

c++98中,常量左值引用(const T&) 是一个“万能”的引用类型,它能够接受很是量左值(T a),常量左值(const T a),右值(T fun() )进行初始化

使用常量左值引用能够减小临时对象的开销,以下

struct Copyable { Copyable() {} Copyable(const Copyable& o) { cout<< "Copied" <<endl; } }; Copyable ReturnRvalue() { return Copyable(); } void AcceptVal(Copyable) {} void AcceptRef(const Copyable& cp) {}//Copyable c = std::move(cp);} void AcceptRRef(int&& i) {i+=3; cout<< (char)i<<endl;} int main() { cout<< "Pass by value: " <<endl; AcceptVal(ReturnRvalue()); // 临时值被拷贝传入 cout<< "Pass by reference: " <<endl; AcceptRef(ReturnRvalue()); // 临时值被做为引用传递 AcceptRRef('c'); // 临时值被做为引用传递 }

例子:

①移动构造函数

#include <iostream> using namespace std; class HasPtrMem { public: HasPtrMem(): d(new int(3)) { cout<< "Construct: " << ++n_cstr<<endl; } #if 0 //拷贝一分内存,并用h.d的值赋值 HasPtrMem(const HasPtrMem& h): d(new int(*h.d)) { cout<< "Copy construct: " << ++n_cptr<<endl; } #endif HasPtrMem(HasPtrMem&& h): d(h.d) { // 移动构造函数 h.d = nullptr; // 将临时值的指针成员置空 cout<< "Move construct: " << ++n_mvtr<<endl; } ~HasPtrMem() { delete d; cout<< "Destruct: " << ++n_dstr<<endl; } int * d; static int n_cstr; static int n_dstr; static int n_cptr; static int n_mvtr; }; int HasPtrMem::n_cstr = 0; int HasPtrMem::n_dstr = 0; int HasPtrMem::n_cptr = 0; int HasPtrMem::n_mvtr = 0; const HasPtrMem GetTemp() { const HasPtrMem h; cout<< "Resource from " << __func__ << ": " << hex <<h.d<<endl; return h; } int main() { const HasPtrMem&& a = GetTemp(); //延长临临时变量的生命周期 cout<< "Resource from " << __func__ << ": " << hex <<a.d<<endl; return 0; }

<utility>的函数std::move(T) 能够调用类T的移动构造函数,来完成对象的深度拷贝,在类没有定义移动构造函数的时候,会默认调用该类的拷贝构造函数

一个高性能的置换函数以下,调用时使用移动构造函数,移动赋值函数

class T { T(T&& t); //移动构造函数 T& operator=(T&& t); //移动赋值函数 T(const T& t); //拷贝构造函数 T& operator=(const T& t); //赋值函数 } template<class T> void swap(T& a, T& b) { T tmp(move(a)); a = move(b); b = move(tmp); }

(4)完美转发

目的:完美转发的意思就是在函数模板中,彻底依照模板的参数的类型,将参数传递给函数模板中调用的另一个函数,如:

template<typename T> void Iamforwording(T t){ IrunCodeActually(t); }

为了使这个函数可以支持左值引用和右值引用, c++11里面定义了新的引用折叠规则(属于模板推导规则的一部分),详细的推导规则能够参见《深刻理解c++11》一书P86的表3-2(属于模板的推导规则之一)

简单例子以下:

void IamForwording(X&&& t){ IrunCodeActually(static_cast<X&&&>(t)); }

应用引用折叠规则,则是

void IamForwording(X& t){ IrunCodeActually(static_cast<X&>(t)); }

 

例子:

void RunCode(int&& m) { cout<< "rvalue ref" <<endl; } void RunCode(int& m) { cout<< "lvalue ref" <<endl; } void RunCode(const int&& m) { cout<< "constrvalue ref" <<endl; } void RunCode(const int& m) { cout<< "constlvalue ref" <<endl; } template<typename T> void PerfectForward(T&& t) { RunCode(forward<T>(t)); } int main() { int a; int b; const int c = 1; const int d = 0; PerfectForward(a); // lvalue ref PerfectForward(move(b)); // rvalue ref PerfectForward(c); // constlvalue ref PerfectForward(move(d)); // constrvalue ref }

(5)初始化列表

容许如下的初始化代码,而c++98只能经过第一行的编译

int a[] = {1,2,3}; int b[]{1,2,3}; vector<int> c{1,2,3}; map<int, float> d= {{1,1.0f}, {2, 2.0f}};

总结起来,如下初始化结果是等价的

int a= 3+4; int a= {3+4}; int a(3+4); int a{3+4}; int *pa = new int(3+4);

初始化列表也能够用于类的构造函数

enum Gender {boy, girl}; class People { public: People(initializer_list<pair<string, Gender>> l) // initializer_list的构造函数 { autoi = l.begin(); for (;i != l.end(); ++i) data.push_back(*i); } private: vector<pair<string, Gender>> data; }; People ship2012 = {{"Garfield", boy}, {"HelloKitty", girl}};

 

(6)POD(plain old data)类型

POD一般是用户自定义的普通类型,体现了与C的兼容性,能够用最老的memcpy进行复制,能够用memset进行初始化

c++11将POD划分为两个基本概念的合集,即:平凡的(trivial) 和 标准布局的(standard layout)

平凡的类或结构体应符合如下定义:

①拥有平凡的默认构造函数和析构函数,一旦定义了构造函数,即便是一个无参数的空函数,该构造函数也再也不是的平凡的。

②拥有平凡的拷贝构造函数和移动构造函数

③拥有平凡的拷贝赋值运算符和移动赋值运算符

④不能包含虚函数以及虚基类

能够经过

cout<<is_trivial<sturct XXX>::value <<endl

来检验,头文件<type_traits>

标准布局的类或结构体应符合如下定义:

①全部非静态成员有相同的访问权限

②在类或者结构体继承时,知足如下条件之一:

*派生类中有非静态成员,且只有一个仅包含静态成员的基类

*基类有非静态成员,而派生类没有非静态成员

③类中第一个非静态成员的类型与其基类不一样

④没有虚函数和虚基类

⑤全部非静态数据成员均符合标准布局类型,其基类也符合标准布局

能够经过

cout<<is_standard<sturct XXX>::value<<endl

来检验,

头文件<type_traits>

 

(7)用户自定义常量

struct Watt{ unsigned int v; }; Watt operator "" _w(unsigned long long v) { return {(unsigned int)v}; } int main() { Watt capacity = 1024_w; }

 

(8)模板别名

typedef unsignd int UINT 等价于 using UINT = unsigned int 一样适用于模板 
template<typename T> using MapString = std::map<T, char*> MapString<int> test;

三、专家新手都支持

(1)右尖括号改进, Y<X<1>>再也不编译失败了。

(2)auto类型推导

从新定义了auto的语意,经过编译器实现对类型的自动推导,使c++有点相似python等动态类型语言

简单例子:

int main() { double foo(); auto x = 1; // x的类型为int auto y = foo(); // y的类型为double struct m { int i; }str; auto str1 = str; // str1的类型是struct m auto z; // 没法推导,没法经过编译 z = x; }

做用以下:

①简化代码

std::vector<std::string> t; for(auto i=t.begin(); i<t.end(); i++){}

②免除类型声明麻烦

class PI { public: double operator* (float v) { return (double)val * v; // 这里精度被扩展了 } const float val = 3.1415927f; }; int main() { float radius = 1.7e10; PI pi; auto circumference = 2 * (pi * radius); }

③跨平台

④模板推导,须要配合decltype

template<typename T1, typename T2> auto Sum(T1& t1, T2& t2) ->decltype(t1+t2){ return t1+t2; }

⑤用在宏定义上,避免屡次变量展开

#define Max1(a, b) ((a) > (b)) ? (a) : (b) #define Max2(a, b) ({ \ auto _a = (a); \ auto _b = (b); \ (_a > _b) ? _a: _b; }) int main() { int m1 = Max1(1*2*3*4, 5+6+7+8); int m2 = Max2(1*2*3*4, 5+6+7+8); }

上述代码避免了,1*2*3*4被屡次计算

(3)decltype,一样也是类型推导,区别在于

decltype的类型推导并非像auto同样是从变量声明的初始化表达式得到变量的类型,其老是以一个普通表达式为参数,返回该表达式的类型。

做用以下:

①增长代码可读性

vector<int> vec; typedef decltype(vec.begin()) vectype; vectype i; // 这是auto没法作到的 for (i = vec.begin(); i<vec.end(); i++) { } for (decltype(vec)::iterator i = vec.begin(); i<vec.end(); i++) { }

②恢复匿名结构体

sturct{ int d; }ttt; decltype(ttt) ok;

③模板利器

// s的类型被声明为decltype(t1 + t2) template<typename T1, typename T2> void Sum(T1 & t1, T2 & t2, decltype(t1 + t2) & s) { s = t1 + t2; }

④基于decltype的result_of

#include <type_traits> using namespace std; typedef double (*func)(); int main() { result_of<func()>::type f; // 由func()推导其结果类型 }

⑤追踪函数的返回类型

下面两个代码是等价的,第二个代码可读性稍好

int (* (*pf()) () ) () { return 0;} auto pf1() -> auto (*) () ->int (*) () { return 0; }

可用于函数转发:

#include <iostream> using namespace std; double foo(int a) { return (double)a + 0.1; } int foo(double b) { return (int)b; } template<class T> auto Forward(T t) ->decltype(foo(t)){ return foo(t); } int main(){ cout<< Forward(2) <<endl; // 2.1 cout<< Forward(0.5) <<endl; // 0 }

 

推导法则:

首先定义标记符表达式:除去关键字、字面量等编译器须要使用的标记以外的程序员自定义的标记均可以是标记符。而耽搁标记符对应的表达式就是标记符表达式。

经过下面的例子和注释能够了解推导的四个法则。

int i = 4; int arr[5] = {0}; int *ptr = arr; struct S { double d; } s; void Overloaded(int); void Overloaded(char); // 重载的函数 int&& RvalRef(); const bool Func(int);

// 规则1: 单个标记符表达式以及访问类成员,推导为本类型

decltype(arr) var1; // int[5], 标记符表达式 decltype(ptr) var2; // int*, 标记符表达式 decltype(s.d) var4; // double, 成员访问表达式 decltype(Overloaded) var5; // 没法经过编译,是个重载的函数

// 规则2: 将亡值,推导为类型的右值引用

decltype(RvalRef()) var6 = 1; // int&&

// 规则3: 左值,推导为类型的引用

decltype(true ? i : i) var7 = i; // int&, 三元运算符,这里返回一个i的左值 decltype((i)) var8 = i; // int&, 带圆括号的左值 decltype(++i) var9 = i; // int&, ++i返回i的左值 decltype(arr[3]) var10 = i; // int& []操做返回左值 decltype(*ptr) var11 = i; // int& *操做返回左值 decltype("lval") var12 = "lval"; // const char(&)[9], 字符串字面常量为左值

// 规则4:以上都不是,推导为本类型

decltype(1) var13; // int, 除字符串外字面常量为右值 decltype(i++) var14; // int, i++返回右值 decltype((Func(1))) var15; // constbool, 圆括号能够忽略

(4)基于范围的for循环关键字改进

int arr[5] = {1,2,3,4,5}; for(auto i : arr) cout<<i;

要求迭代的对象实现++和==等操做符,且范围是肯定的。同时注意迭代器对象在for中是解引用的

vector<int> v = {1, 2, 3, 4, 5}; for (auto i = v.begin(); i != v.end(); ++i) cout<< *i<<endl; // i是迭代器对象 for (auto e: v) cout<< e <<endl; // e是解引用后的对象

 

四、加强类型的安全性

(1)强类型枚举

(2)堆内存管理

 

五、加强性能和操做硬件的能力

(1)常量表达式

(2)变长模板

(3)原子类型与原子操做 aotmic<float>af{1.2f}

(4)线程局部存储, 原来的__thread interrCode 能够写成intthread_localerrCode;

(5)快速退出:quick_exit与at_quick_exit 不执行析构函数而只是让程序终止,与exit同属于正常退出

用法相似下面:

#include <cstdlib> #include <iostream> using namespace std; void openDevice() { cout<< "device is opened." <<endl; } void resetDeviceStat() { cout<< "device stat is reset." <<endl; } void closeDevice() { cout<< "device is closed." <<endl; } int main() { atexit(closeDevice); atexit(resetDeviceStat); openDevice(); exit(0); }

输出:

device is opened device stat is reset device is closed

 

六、开发可以改变人们思惟方式的特性

(1)nullptr

(2)=default =deleted

(3)lambda

lambda语法

[捕捉列表] (参数) mutable修饰符 -> 返回类型 {}

若是不须要参数,其括号能够省略

捕捉列表用于捕捉上下文的变量,形式以下:

[var]表示值传递方式捕捉变量var

[=] 表示值传递方式捕捉全部父做用域的变量(包括this)

[&var] 表示引用传递捕捉变量var

[&] 表示引用传递捕捉全部父做用域的变量(包括this)

[this] 表示值传递捕捉当前的this指针

lambda 显然比仿函数(operator() (参数) )好用

class AirportPrice{ private: float _dutyfreerate; public: AirportPrice(float rate): _dutyfreerate(rate){} float operator()(float price) { return price * (1 - _dutyfreerate/100); } }; int main(){ float tax_rate = 5.5f; AirportPrice Changi(tax_rate); auto Changi2 = [tax_rate](float price)->float{ return price * (1 - tax_rate/100); }; float purchased = Changi(3699); float purchased2 = Changi2(2899); }

 

②代码可读性强

int Prioritize(int i); int AllWorks(int times){ int i; int x; try { for (i = 0; i < times; i++) x += Prioritize(i); } catch(...) { x = 0; } const int y = [=]{ int i, val; try { for (i = 0; i < times; i++) val += Prioritize(i); } catch(...) { val = 0; } return val; }(); }

③捕捉列表不一样会致使不一样的结果

#include <iostream> using namespace std; int main() { int j = 12; auto by_val_lambda = [=] { return j ;}; auto by_ref_lambda = [&] { return j;}; cout<< "by_val_lambda: " <<by_val_lambda() <<endl; //12 cout<< "by_ref_lambda: " <<by_ref_lambda() <<endl; //12 j++; cout<< "by_val_lambda: " <<by_val_lambda() <<endl; //12, 注意这里,j传入的时候是12 cout<< "by_ref_lambda: " <<by_ref_lambda() <<endl; //13 }

mutable的做用

int main(){ int val; // 编译失败, 在const的lambda中修改常量 auto const_val_lambda = [=]() { val = 3;}; // 非const的lambda,能够修改常量数据 auto mutable_val_lambda = [=]() mutable { val = 3;}; // 依然是const的lambda,对引用不影响 autoconst_ref_lambda = [&] { val = 3;}; // 依然是const的lambda,经过参数传递val auto const_param_lambda = [&](int v) { v = 3;}; const_param_lambda(val); return 0; }

⑤与STL结合

#include <vector> #include <algorithm> using namespace std; vector<int>nums; vector<int>largeNums; const int ubound = 10; inline void LargeNumsFunc(inti){ if (i>ubound) largeNums.push_back(i); } void Above() { // 传统的for循环 for (auto itr = nums.begin(); itr != nums.end(); ++itr) { if (*itr>= ubound) largeNums.push_back(*itr); } // 使用函数指针 for_each(nums.begin(), nums.end(), LargeNumsFunc); // 使用lambda函数和算法for_each for_each(nums.begin(), nums.end(), [=](inti){ if (i>ubound) largeNums.push_back(i); }); }

七、融入编程现实

(1)algnof和alognas

(2)[[ attibute-list ]]

(3)Unicode的库支持

(4)原生字符串字面量

相关文章
相关标签/搜索