alignas:指定对齐大小ios
alignof:获取对齐大小程序员
decltypeexpress
auto(从新定义):可做为返回值类型后置时的占位符数组
static_assert:静态断言数据结构
using(从新定义):类型别名或者模板别名ide
noexcept:声明函数不能够抛出任何异常函数
export(弃用,不过将来可能留做他用)this
nullptrspa
constexpr:可在在编译期确认的常量表达式3d
thread_local:等价于TLS
C++11中支持使用等号 = 或者花括号 {} 进行就地的(也就是在声明的时候)非静态成员变量初始化。例如:
struct init{ int a = 1; double b {1.2}; }
在C++11标准支持了就地初始化非静态成员的同时,初始化列表这个手段也被保留下来了。只不过初始化列表老是看起来“后做用于”非静态成员。
C++11提供关键字final,做用是使派生类不可覆盖它所修饰的虚函数。final关键字也可 用于基类中,可是这样定义的虚函数就没有意义了。final一般就是在继承关系的“中途”终止派生类的重载。
在C++中对于基类声明为virtual的函数,以后的重载版本都不须要再声明该重载函数为virtual。即便在派生类中声明了virtual,该关键字也是编译器能够忽略的。另外,有的虚函数会“跨层”,没有在父类中声明的接口有多是祖先的虚函数接口。因此C\++11引入了虚函数描述符override,来帮助程序员写继承结构复杂的类型。若是派生类在虚函数声明时使用了override描述符,那么该函数必须重载其基类中的同名函数,不然代码将没法经过编译。
C++11提供了继承构造函数,在派生类中使用using声明,就能够把基类中的构造函数继承到派生类中。 但其实质是编译器自动生成代码,经过调用父类构造函数来实现,不是真正意义上的“继承”,仅仅是为了减小代码书写量。
class A { A(int i) {} A(double d, int i) {} A(char *c , double d, int i) {} //... 更多构造函数 }; class B : A { using A::A; //继承构造函数 //... }
C++11提供委派构造函数,能够简化多构造函数的类的编写。若是咱们能将一个构造函数设定为“基准版本”,则其余构造函数能够经过委派“基准版本”来进行初始化。咱们将这个“基准版本”称为目标构造函数。
class Info { public: Info() { InitRest(); }//目标构造函数 Info(int i) : Info() { type = i; }//委派构造函数 Info(char c) : Info() { name = c; }//委派构造函数 private: void InitRest(); { /* 其余初始化 */} int type {1}; char name {'a'}; //... }
注意:委派构造函数不能有初始化列表。若是委派构造函数要给变量赋初值,初始化代码必须放在函数体中。
在使用委派构造函数时,建议程序员抽象出最为“通用”的行为做为目标构造函数。
在C++11中,这样的“偷走”临时变量中资源的构造函数,被称为移动构造函数。而这样的“偷”的行为,则称之为“移动语义”。
class HasPtrMem(){ public: HasPtrMem() : d(new int(3)) {} HasPtrMem(const HasPtrMem & h) : d(new int(*h.d)) {}// 拷贝构造函数 HasPtrMem(HasPtrMem && h) : d(h.d) {h.d = nullptr;}// 移动构造函数,需将临时值的指针成员置空 ~HasPtrMem() {delete d;} private: int *d; }
关于左值,右值很难去作一个很是明确的定义,可是在C++中有一个被普遍认同的说法,那就是能够取地址的、有名字的就是左值,反之,不能取地址的、没有名字的就是右值。
不管是声明一个左值引用仍是右值引用,都必须当即进行初始化。左值引用是具名变量值的别名,而右值引用则是不具名(匿名)变量的别名。
标准库在<type_traits>头文件中提供了3个模板类:is_rvalue_reference、is_lvalue_reference、is_reference,可供咱们进行判断引用类型是左值引用仍是右值引用。
C++11中提供了std::move这个函数使咱们能将一个左值强制转化为右值引用,继而咱们能够经过右值引用使用该值,以用于移动语义。
// 使用上面例子中的HasPtrMem HasPtrMem a; HasPtrMem b(std::move(a));// 调用移动构造函数
事实上,为了保证移动语义的传递,程序员在编写移动构造函数的时候,应该老是记得使用std::move转换拥有形如堆内存、文件句柄等资源的成员为右值,这样依赖,若是成语支持移动构造的话,就能够实现其移动语义。而即便成员没有移动构造函数,那么接受常量左值的构造函数版本也会轻松地实现拷贝构造,所以也不会引发大的问题。
在C++中,有一个很是好也很是坏的特性,就是隐式类型转换。隐式类型转换的“自动性”可让程序员免于层层构造类型。但也是因为它的自动性,会在一些程序员意想不到的地方出现严重的但不易被发现的错误。
关键字explicit主要用于修饰的构造函数,其做用主要就是阻止构造函数的隐式转换。
C\++11中将explicit的使用范围扩展到了自定义的类型转换操做符上,以支持所谓的“显示类型转换”。
在C\++11中可使用花括号“{}”来进行初始化,这种初始化方式被称为列表初始化,已经称为C++语言的一个基本功能。咱们甚至可使用列表初始化的方式对vector、map等非内置的复杂的数据类型进行初始化。
并且,C++11中,标准老是倾向于使用更为通用的方式来支持新的特性。标准模板库中容器对初始化列表的支持源自<initilizer_list>这个头文件中的initilizer_list类模板的支持。咱们能够经过声明已initilizer_list<T>模板类为参数的构造函数,来使得自定义的类使用列表初始化。
enum Gender {boy, girl}; class People{ public: People(initilizer_list<pair<string, Gender>> l) {//initilizer_list的构造函数 auto i = l.begin(); for (; i != l.end(); ++i) data.pushback(*i); } private: vector<pair<string, Gender>> data; }; People ship2012 = {{"Garfield", boy}, {"HelloKitty", girl}};
另外,使用列表初始化能够有效的防止类型收窄。类型收窄通常是指一些可使得数据变化或者精度丢失的隐式类型转换。
auto声明的变量必须被初始化,以使编译器可以从其初始化表达式中推导出其类型。
auto使用时需注意:
(1)、可使用const、volatile、pointer(*)、reference(&)、rvalue reference(&&)等说明符和声明符来修饰auto关键字;
(2)、用auto声明的变量必须初始化;
(3)、auto不能与其它任何类型说明符一块儿使用;
(4)、方法、参数或模板参数不能被声明为auto;
(5)、定义在堆上的变量,使用了auto的表达式必须被初始化;
(6)、auto是一个占位符,不是类型,不能用于类型转换或其它一些操做,如sizeof、typeid;
(7)、auto关键字内声明的声明符列表的全部符号必须解析为同一类型;
(8)、auto不能自动推导成CV-qualifiers(constant& volatile qualifiers),除非被声明为引用类型;
(9)、auto会退化成指向数组的指针,除非被声明为引用;
(10)、auto不能做为函数的返回型,在C++14中是能够的。
decltype的类型推导并非像auto同样是从变量声明的初始化表达式得到变量的类型,decltype老是以一个普通的表达式为参数,返回该表达式的类型。而与auto相同的是,做为一个类型指示符,decltype能够将得到的类型来定义另一个变量。与auto相同,decltype类型推导也是在编译时进行的。
decltype只能接受表达式作参数,像函数名作参数的表达式decltype(hash)这种是没法经过编译的。
利用auto和decltype以及返回类型后置的语法就能实现追踪返回类型。好比,咱们想写一个泛型的加法函数时可能直观的写下以下代码。
template <typename T1, typename T2> decltype(t1 + t2) sum(const T1& t1, const T2& t2) { return t1 + t2; }
可是,这样写是编译不过的,由于编译器只会从左往右地读入符号,这里的t1和t2在编译器看来都是未声明的。正确的写法是这样的。
template <typename T1, typename T2> auto sum(const T1& t1, const T2 & t2) -> decltype(t1 + t2) { return t1 + t2; }
语法很简单,不赘述。主要使用时须要注意两点。一是使用基于范围的for循环须要for循环迭代的范围是可肯定的。二是,基于范围的for循环要求迭代对象实现++和==等操做符,这点标准库中的容器不会有问题,但用户自定义的类须要本身实现。
原来的枚举类型有非强类型做用域,容许隐式转换为整型,占用存储控件及符号性不肯定的缺点。C++11引入了强类型枚举来解决问题。
声明强类型枚举只须要在enum后加上关键字class。好比:
enum class Type { General, Light, Medium, Heavy};
强类型枚举具备一下几点优点:
enum class Type: char { General, Light, Medium, Heavy};
因为enum class是强类型做用域的,故匿名的enum class极可能什么都作不了。
常量表达式函数须要知足几个条件,不然不能用constexpr关键字进行修饰:
constexpr int GetConst() { return 1; }
const int i = 0;
constexpr int j = 0;
constexpr表示的就是编译期常量,const表示的是运行期常量。
大部分状况下这两个定义是没有区别的。不过i只要在全局范围内声明,编译器必定会为它产生数据;而对于j,若是没有地方调用它,编译器能够选择不为它生成数据。
而且,默认只有内置类型才能修饰为常量表达式值,自定义类型若是要成为常量表达式值,必须定义一个constexpr修饰的构造函数。
当咱们但愿将一些数据组合成单一对象,但又不想麻烦地定义一个新的数据结构来表示这些数据时,tuple是很是有用的。通常,tuple能够用于函数返回多个返回值。
tuple容器, 可使用直接初始化, 和"make_tuple()"初始化, 访问元素使用"get<>()"方法, 注意get里面的位置信息, 必须是常量表达式(const expression)。
能够经过"std::tuple_size<decltype(t)>::value"获取元素数量; "std::tuple_element<0, decltype(t)>::type"获取元素类型。
若是tuple类型进行比较, 则须要保持元素数量相同, 类型能够比较。
#include <iostream> #include <vector> #include <string> #include <tuple> using namespace std; std::tuple<std::string, int> giveName(void) { std::string cw("Caroline"); int a(2013); std::tuple<std::string, int> t = std::make_tuple(cw, a); return t; } int main() { std::tuple<int, double, std::string> t(64, 128.0, "Caroline"); std::tuple<std::string, std::string, int> t2 = std::make_tuple("Caroline", "Wendy", 1992); //返回元素个数 size_t num = std::tuple_size<decltype(t)>::value; std::cout << "num = " << num << std::endl; //获取第1个值的元素类型 std::tuple_element<1, decltype(t)>::type cnt = std::get<1>(t); std::cout << "cnt = " << cnt << std::endl; //比较 std::tuple<int, int> ti(24, 48); std::tuple<double, double> td(28.0, 56.0); bool b = (ti < td); std::cout << "b = " << b << std::endl; //tuple做为返回值 auto a = giveName(); std::cout << "name: " << get<0>(a) << " years: " << get<1>(a) << std::endl; return 0; }
在C++11中,nullptr是一个所谓“指针空值类型”的编译期常量。指针空值类型被命名为nullptr_t。
须要注意的是,nullptr是C++11中的关键字,它是有类型的,且仅能够被隐式转化为指针类型,其类型定义是:
typedef decltype(nullptr) nullptr_t;
lambda表达式的语法定义以下:
\[capture] (parameters) mutable ->return-type {statement};
\[capture]:捕捉列表。捕捉列表老是出如今lambda函数的开始处。实质上,[]是lambda引出符(即独特的标志符),编译器根据该引出符判断接下来的代码是不是lambda函数。捕捉列表可以捕捉上下文中的变量以供lambda函数使用。捕捉列表由一个或多个捕捉项组成,并以逗号分隔,捕捉列表通常有如下几种形式:
备注:父做用域是指包含lambda函数的语句块。
// 简单示例 int a = 20, b = 10; auto totalAB = [] (int x, int y)->int{ return x + y; }; int aAddb = totalAB(a, b); cout << "aAddb :" << aAddb << endl; // lambda与STL vector<int> v{ 1, 2, 3, 4, 5 }; for_each( v.begin(), v.end(), [] (int val) { cout << val; } );
在现阶段,一般编译器都会把lambda函数转化为一个仿函数对象。这是编译器实现lambda函数的一种方式。所以,C++11中,lambda函数能够视为仿函数的一种等价形式。
原生字符串字面量的意思就是所见即所得,在代码中的字符串常量是怎么样的,咱们获得的就是怎么样的,不须要转义字符来控制特定的字符。
在C++11中程序员只须要在字符串前面加入前缀字母R,并在引号中使用括号左右标识便可将该字符串声明为原生字符串了。
// 输出带引号的字符串"你好" int main() { std::string str = "\"你好\""; std::cout << str << std::endl; const char* str1 = R"("你好")"; std::cout << str1 << std::endl; return 0; }