您的位置:首页 > 编程语言 > C语言/C++

C++11新特性总结

2015-06-24 13:27 585 查看
【C++11 新特性】

1、long long 类型:是由long衍生而来的,最小尺寸是64位,8字节。

2、列表初始化:先讲一点,在C++语言中,初始化和赋值是两个完全不同的操作。怎么讲?初始化的含义是创建变量时赋予其一个初始化,而赋值的含义是把对象的当前值擦除,以一个新值来替代。

那么,什么是列表初始化?比如:int nTemp{0}; 用花括号来初始值,无论是初始化对象还是某些时候为对象赋新值。

不过,要注意的一点是,在初始化内置类型的变量时,如果使用列表初始化且初始值存在丢失信息的风险,则编译器将报错。比如:long a = 3.39839; int b{a};

对于内置类型的变量如果未被显式初始化,那么它有两种情况:一是,定义于任何函数体之外的变量都被初始化为0;二是,定义于函数体之内的内置类型变量将不被初始化,所以一旦被调用而出错。

3、nullptr 常量:将空指针初始化。比如:int *ptr = nullptr; 在以前的程序还会用到一个名为NULL的预处理变量来给指针赋值,这个变量在头文件cstdlib中定义,值为0。

4、***(好处在哪里)constexpr变量:常量表达式是指值不会改变并且在编译过程就能得到计算结果的表达式。C++11 新标准规定,允许将变量声明为constexpr类型以便由编译器来验证变量的值是否是一个常量表达式。用constexpr声明的变量一定要是一个常量。

比如:constexpr int a = 2;

constexpr int b = Fun();这个表达式是有限制的,那么就是函数Fun也应当是个constexpr函数。

5、***constexpr函数:是指能用于常量表达式的函数。而且有这样的约定:一是,函数的返回类型及所有形参的类型都得是字面值类型。(tip:什么是字面值类型?到目前为止接触过的数据类型中,算术类型、引用和指针都属于字面值类型;而自定义的类、IO库、string类型则不属于字面值类型。)二是,函数体中必须有且只有一条return语句。(tip:在这里插一知识点,指针本身是一个对象,但同时它又可以指向另外一个对象。为了区分这两个互相独立的问题,用“顶层const”表示指针本身是个常量,而用“底层const”表示指针所指的对象是一个常量。)用法是:constexpr int fun() { return 43; }

先说一下,constexpr函数的好处在哪?那就是为了能在编译过程中随时展开,constexpr函数被隐式地指定为内联函数。

6、***constexpr构造函数:

7、类型别名声明:新标准规定了一种新的方法,使用“别名声明”来定义类型的别名:

比如:using SI = CStudent; SI s;这种方法用关键字using作为别名声明的开始,其后紧跟别名和等号。(理解误区:遇到一条使用了类型别名的声明语句时,人们往往会错误地尝试把类型别名替换成它本来的样子,以理解该语句的含义,这是错误的。为什么呢?比如:using *pstring = char; 那么pstring实际上是指向char的指针。 本来const pstring str = 0; 如果改写为const char *str = 0;那么就是错误的。因为前者是声明了一个指向char的常量指针,后者是一个指向const char的指针。)

8、auto类型指示符:用它就能让编译器替我们去分析表达式所属的类型。不过,auto定义的变量必须有初始值。

9、decltype类型指示符:它的作用是选择并返回操作数的数据类型,并且在这过程中,编译器分析表达式并得到它的类型,却不实际计算表达式的值。

10、类内初始化:创建对象时,类内初始值将用于初始化数据成员。

11、范围for语句:如果想对string对象中的每个字符做点什么操作,那么“范围for”是首选。先说它的语法形式:

for (declaration : expression)

statement

举个实例:for (auto c : str) //string str( “some string.” )

cout << c << endl; //输出当前字符,然后换行。

如果想要改变string对象中字符的值,那么必须把循环变量定义成引用类型。

举个实例:for (auto &c : str)

c= toupper(c); //将str中的每个字符都改变为大写的字母。

12、容器的cbegin和cend函数:为了便于专门得到const_iterator类型的返回值,新标准引入了这两个新函数。比如:

const vector<int> vect;

auto iter = vect.cbegin(); //那么iter的类型就是vector<int>::const_iterator

13、标准库begin和end函数:为了让指针、数组的使用更简单、更安全,新标准引入了两个名为begin和end的函数。也与容器中的两个同名成员功能一样,不过,毕竟数组不是类类型,因此这两个函数不是成员函数。

举个实例: int ia[] = {0,1,2,3,4,5,6};

int* nBegin = begin(ia); //指向ia首元素的指针

int* nEnd = end(ia); //指向ia尾元素的下一位置的指针

14、***(好处在哪里,应用场景?)将sizeof用于类成员:新标准允许我们使用作用域运算符来获取类成员的大小。通常情况下只有通过类的对象才能访问到类的成员,但是sizeof运算符无须我们提供一个具体的对象,因为要想知道类成员的大小无须真的获取该成员。

举个实例:

void fun( )

{

cout<<sizeof(C::m_i)<<endl;

cout<<sizeof(C::m_str)<<endl;

}

15、标准库initializer_list类:如果函数的实参数量未知但是全部实参的类型都相同,可以使用initializer_list类型的形参。initializer_list和vector一样也是一种模板类型。应用如下:

void fun( initializer_list<string> strlist)

{

.....

}

16、列表初始化返回值:新标准规定,函数可以返回花括号包围的值得列表,以此对表示函数返回的临时量进行初始化。比如:

vector<string> fun( ) { return {“string1”, “string2”}; }

17、定义尾置返回类型:在讲这个特性的之前,首先要了解另外一点。(tip:如何声明一个返回数组指针的函数?形式如下: int (*func( int i )) [10]; )。新标准中简化了func声明的方法,那就是使用“尾置返回类型”。形式如下:

Auto func(int i) -> int(*) [10];

//func接受一个int类型的实参,返回一个指针,该指针指向含有10个整数的数组。

18、使用decltype简化返回类型定义:有一种情况,如果我们知道函数返回的指针将指向哪个数组,就可以使用decltype关键字声明返回类型。举个实例:

int odd[ ] = {1,3,5,7,9};

int even[ ] = {0,2,4,6,8};

decltype(odd) *arrPtr(int i)

{

return (i %2 ) ? &odd : &even;

}

19、使用=default生成默认构造函数:在讲这个之前,要先了解一个知识点。

(tip:什么时候编译器才会去合成调用默认构造函数?看下面的解析:)

有四种情况,会导致“编译器必须为未声明constructor之classes合成一个default constructor”,其他情况都不会,这点要注意。分别是:

①带有Default Constructor的Member Class Object的class

比如:class Foo{ public : Foo(), Foo(int)...}

Class Bar{ public : Foo foo; char * str; ...}

Void foo_bar()

{

Bar bar; //Bar此时便会被合成default constructor

....

}

②带有Default Constructor的Base Class 的class

比如:class Base{ public : Base(); ....}

Class A : public Base{....}

Void fun()

{

A a; //此时,会按顺序先调用Base的default constructor,

//编译器再合成A的default constructor。

}

③这个class声明(或继承)有一个virtual function;

④这个class派生自一个或更多的virtual base class。

在新标准中,如果我们需要默认的行为,那么可以通过在参数列表后面写上“= default”来要求编译器生成构造函数。

20、类对象成员的类内初始化:直接举个实例:

class base

{

private:

std::vector<A> vect{ A(24,39) }; //将A的值被传递给vect<A>的构造函数。

}

21、委托构造函数:文字表达起来很拗口,直接看用委托构造函数的实例:

Class base

{

Public:

Base(std::string s, int cnt, double sum) : strTemp(s), nCnt(cnt), dSum(sum) {}

//以下其余构造函数全部委托给另一个构造函数

Base() : Base(“ ”, 0, 0){}

Base(std::string s) : Base(s, 0, 0) {}

Base(std::istream &is) : Base() { read (is, *this); }

}

假如函数体都有代码的话,将先执行受委托的构造函数体,然后再将控制权交还给委托者的函数体。

22、用string对象处理文件名:比如:

ifstream in(ifile);

ofstream out;

新标准中,文件名ifile既可以是string对象,也可以是C风格字符数组。

23、array和forward_list容器:这两个是新标准增加的顺序容器类型。下面,看一下顺序容器类型有哪些:

①vector (可变大小数组。支持快速随机访问。在尾部之外的位置插入或删除元素较慢。)

②deque(双端队列。支持快速随机访问。在头尾位置插入/删除速度较快。)

③list (双向链表。只支持双向顺序访问。在任何位置插入/删除速度较快。)

④forward_list(单向链表。只支持单向顺序访问。在任何位置插入/删除速度较快。)

⑤array (固定大小数组。支持快速随机访问。不能添加或删除元素。)

⑥string(与vector相似的容器,但专门保存字符。随机访问、插入/删除速度快。)

24、容器的非成员函数swap:在新标准中,容器既提供成员函数版本的swap,也提供非成员版本的swap。除array外,交换两个容器内容的操作保证会很快,因为元素本身并未交换,swap只是交换了两个容器的内部数据结构。而,swap两个array则会真正交换他们的元素,因此交换两个array所需的时间与array中元素的数目成正比。

25、容器insert成员的返回类型:在新标准下,insert可以返回指向第一个新加入元素的迭代器。举个实例:

list<string> lst;

auto iter = lst.begin();

while( cin >> word)

iter = lst.insert(iter, word); //这等价于调用push_front.

26、容器的emplace成员:新标准引入了三个新成员——emplace_front、emplace和emplace_back,这些操作分别对应push_front、insert和push_back,前者允许我们将元素放置在容器头部、一个指定的位置之前、容器的尾部。

27、***(作用是什么?)shrink_to_fit:在新标准中,我们可以调用shrink_to_fit来要求deque、vector或string退回不需要的内存空间。但是,具体的实现可以选择忽略此请求,因为shrink_to_fit也并不保证一定退回内存空间。

28、string的数值转换函数:新标准引入了多个函数,可以实现数值数据与标准库string之间的转换:比如:

①string s = to_string(i); //将整数i转换为字符表示形式,如43->”43”

②stoi(s,p,b)、stol(s,p,b)、stoul(s,p,b)、stoll(s,p,b)、stoull(s,p,b)

③stof(s,p)、stod(s,p)、stold(s,p)、

29、lambda表达式:(tip:到目前了解了可调用对象是函数、函数指针、重载了函数调用运算符的类、lambda表达式。)一个lambda表达式具有如下形式:

[ captrue list ] ( parameter list) -> return type { function body }

captrue list是指一个lambda所在函数中定义的局部变量的列表(通常为空)。其余三个和任何普通函数一样,这里不介绍。与普通函数不同的是,lambda必须使用尾置返回来指定返回类型。

我们可以忽略参数列表和返回类型,但必须永远包含捕获列表和函数体,例如:

auto f = [] { return 3 ;};

stable_sort( w.begin(), w.end(),

[] (const string& a, const string& b)

{ return a.size() < b.size(); });

还有一种情况就是捕获列表中有参数的,比如:

[sz] (const string& a)

{ return a.size() >= sz; }

解释一下,这个sz是定义在函数体中的局部变量。在捕获列表中有列出的函数局部变量才能被lambda本身的函数体使用。

30、***标准库bind函数:为了让有多个实参的函数,可以代替lambda作为另一个函数的参数被使用,bind函数诞生了。可以将bind函数看做一个通用的函数适配器,它接受一个可调用对象,生成一个新的可调用对象来“适应”原对象的参数列表。bind的形式为:

auto newCallable = bind( callable, arg_list );

newCallable 本身是一个可调用对象,当我们在调用它的时候,newCallable会调用callable,并传递给它arg_list中的参数。arg_list中的参数可能包含形如_n的名字,数值n表示生成的可调用对象中参数的位置:_1为newCallable的第一个参数,_2为newCallable的第二个参数,以此类推。举个实例:

auto wc = find_if( w.begin(), w.end(), bind(fun1, _1, sz) );

31、pair的列表初始化:在新标准中,支持对pair返回值进行列表初始化。比如:

pair< string, int > process( vector< string > &vect)

{

if( !vect.empty() )

return { vect.back(), vect.back().size() }; //列表初始化

else

return pair< string, int>();

}

32、***无序容器:新标准定义了4个无序关联容器——unordered_set、unordered_multiset、unordered_map和unordered_multimap。这些容器不是使用比较运算符来组织元素,而是使用一个哈希函数和关键字类型的==运算符。

33、智能指针:为了更安全、更方便地使用动态内存,新的标准库提供了两种智能指针类型来管理动态对象,分别是shared_ptr和unique_ptr。这两者的区别在于管理底层指针的方式:shared_ptr允许多个指针指向同一个对象,而unique_ptr则独占所指向的对象。

那么智能指针是怎么应用的呢?是这样的:shared_ptr< string > ptr;

介绍几个shared_ptr独有的函数:

①make_shared< T > ( args ) :返回一个shared_ptr,使用args初始化此对象。

②shared_ptr< T >p ( q ) : p是shared_ptr q的拷贝。

③p.use_count( ):返回与p共享对象的智能指针数量。

34、unique_ptr类:与shared_ptr不同,没有类似make_shared的函数,unique_ptr在初始化的时候,必须采用直接初始化形式:比如:

unique_ptr< int > p2 ( new int(42) );

35、动态分配对象的列表初始化:在自由空间分配的内存是无名的,因此new无法为其分配的对象命名,比如:int* pi = new int; //pi指向一个动态分配的、未初始化的无名对象。

在新标准下,可以使用列表初始化,比如:

vector< int > *pv = new vector< int > {0,1,2,3,4,5};

除此之外,还可以使用auto来生成一个与obj类型相同的对象,比如:

auto p1 = new auto( obj ); 若obj是int,那么p1就是int*;若是string,那么p1就是string*;

36、***allocator::construct可使用任意构造函数:allocator类的出现,是为了解决new的一些局限性。它是希望将内存分配和对象构造分离。allocaor分配的内存是未构造的。在新标准库中,construct成员函数接受一个指针和零个或多个额外参数,在给定位置构造一个元素。

37、***将=default用于拷贝控制成员:我们可以通过将拷贝控制成员定义为=default来显示地要求编译器生成合成的版本。

38、使用=delete阻止拷贝类对象:为了防止对一切没实际做法的copy constructot和copy operator不必要的拷贝,在新标准下,我们可以通过将其定义为“删除的函数”来阻止拷贝。比如:

class Base

{

Base( ) = default; //使用合成的默认构造函数

Base( const Base& ) = delete; //阻止拷贝

Base & operator= ( const Base& ) = delete; //阻止拷贝

}

39、***用移动类对象代替拷贝类对象:通过使用新标准引入的两种机制,我们可以避免类的拷贝。一是,“移动构造函数”,通常是将资源从给定对象“移动”而不是拷贝到正在创建的对象。二是,“move函数”,首先,当reallocate在新内存中构造string时,它必须调用move来表示希望使用string的移动构造函数。其次,当我们使用move时,直接调用std::move而不是move。

40、右值引用:为了支持移动操作,新标准引入了一种新的引用类型。所谓“右值引用”就是必须绑定到右值的引用,通过&&而不是&来获得右值引用。右值引用还有一个重要的性质,那就是只能绑定到一个将要销毁的对象。

int i = 43;

int &r = i; //正确。r引用i。

int &&r = i; //错误。不能将一个右值引用绑定到一个左值上。

int &r = i * 43; //错误。i*43 是一个右值。

const int &r = i * 43; //正确。将一个const的引用绑定到一个右值上。

int &&r = i * 43; //正确。将r绑定到右值上。

对于“右值”与“左值”的相互区别之处:左值有持久的状态,而右值要么是字面常量,要么是在表达式求值过程中创建的临时对象。

例如:string s1 = “a value”; string s2 = “another”;在这个例子中,s1和s2都是左值。但是,如果这样的话:s1 + s2 = “wow”; 这是一种对右值进行赋值。这是允许的。

41、标准库move函数:先看下面两个实例,或许对于右值与左值有着更好的理解。比如:

int &&r1 = 4; //正确。因为字面常量是右值。

int &&r2 = r1; //错误。因为r1是左值。不是一个临时的对象。

但是,如果你一定要这样写的话,也是有办法的。我们可以显示地将一个左值转换为对于的右值引用类型,就是调用move函数。使用实例如下:

int &&r3 = std::move(r1); //正确。

调用move函数就意味着承诺:除了对r1赋值或销毁它外,我们将不再使用它。

42、移动构造函数和移动赋值运算符:为了让我们自己的类型支持移动操作,需要为其定义移动构造函数和移动赋值运算符。省去拷贝的繁琐,移动操作相对简单多了。举个实例:

Base::Base( Base &&b ) noexcept

: element1(b.element1), element2( b.element2 ), element3( b.element3 )

{

B.element1 = b.element2 = b.element3 = nullptr;

}

(注意:)标准库容器、string和shared_ptr类既可以支持移动也支持拷贝。IO类和unique_pr类可以移动但不能拷贝。

可以具体看到移动构造函数的运行原理, 我们偷走了临时变量的内存空间,据为己用。节省了开辟空间的时间。

43、noexcept异常说明:这个在新标准中,新增加的,用来通知标准库我们的构造函数不抛出任何异常。在一个构造函数中,noexcept出现在参数列表和初始化列表开始的冒号之间。

44、***移动迭代器:一般来说,一个迭代器的解引用运算符返回一个指向元素的左值。但是移动迭代器则不同,它的解引用运算符生成一个右值引用。

我们可以通过调用标准库的make_move_iterator函数将一个普通迭代器转换为一个移动迭代器。

45、***引用限定成员函数:因为新标准库类仍然允许向右值赋值,但是我们是希望在自己的类中阻止这种做法的。于是,我们指出this的左值/右值属性的方式,即在参数列表后放置一个引用限定符。

对于&限定的函数,我们只能将他用于左值;对于&&限定的函数,只能用于右值。并且引用限定符只能用于(非static)成员函数,且必须同时出现在函数的声明和定义中。

举个实例:

class Foo

{

public:

Foo sorted() &&; //可用于可改变的右值。

Foo &operator=( const Foo& ) &; //可用于可改变的左值。

}

如果我们定义两个或两个以上具有相同名字和相同参数列表的成员函数,就必须对所有函数都加上引用限定符,或者所有都不加。

46、function类模板:先看运用:

function<T> f; //f是一个用来储存解调用对象的空function,这些可调用对象的调用形式应该与函数类型T相同。

function的一个优势就是可以用来表达各种不同的类型对象。看了下面这个实例,就更清楚这句话的意思。

int add( int i, int j) { return i + j; }; //普通函数

auto mod = []( int i, int j ) { return i % j; } //lambda, 其产生一个未命名的函数对象类

struct divide{ //函数对象类,重定义操作符“()”。

int operator( ) (int i, int j ){ return i / j; } };

function<int ( int, int )> f1 = add;

function<int ( int, int )> f2 = divide();

function<int ( int, int )> f3 = [](int i, int j ){ return i * j; };

cout << f1(4, 2) << endl; //6

cout << f2(4, 2) << endl; //2

cout << f3(4, 2) << endl; //8

47、***explicit类型转换运算符:首先明白一点,什么是类型转换?“类型转换运算符”是类的一种特殊成员函数,它负责将一个类类型的值转换成其他类型。一般格式为:

operator type( ) const;

但是这种类型转换有时可能产生意外的结果,于是,为了防止这样的异常情况发生,C++11新标准引入了“explicit类型转换运算符”。比如:

bool val;

explicit operator int() const { return val; }

将bool类型转换为int类型。但是要显式的被调用才可以。

bool b = true;

int a = static<int>( b );

但是该规则存在例外,即如果表达式被用作条件,则编辑器会显式的类型转换自动应用于它。换句话说,当表达式出现在下列位置时,显式的类型转换将被隐式的执行。

①if、while及do语句的条件部分

②for语句头的表示式

③逻辑非运算符(!)、逻辑或运算符(||)、逻辑与运算符(&&)的运算对象。

④条件运算符(?:)的条件表达式。

48、虚函数的override指示符:派生类经常(但不总是)覆盖它继承的虚函数。如果派生类没有覆盖其基类中的某个虚函数,则该虚函数的行为类似于其他的普通成员,派生类会直接继承其在基类中的版本。

派生类可以在它覆盖的函数前使用virtual关键字,但是不是非得这么做的,这是为什么呢? C++11新标准推出了override关键字来说明派生类中的改写的虚函数。这种做法使得人们可以很清晰的知道,是否是重写了基类中的虚函数。

49、final指示符:我们还可以将某个函数指定为final,这样一来任何尝试覆盖该函数的操作都将引发错误。另外,如果final是写在跟在类名后面的话,这是一种防止继承的方法。

50、***删除的拷贝控制和继承:

51、继承的构造函数:在C++11新标准中,派生类能够重用其直接基类定义的构造函数。使用的方法是注明基类名的using声明语。比如:在类CBird中,using CAnimal::CAnimal;

52、声明模块类型形参为友元:举个实例:

template < typename type > class Bar

{

friend type; //将访问权限授予用来实例化Bar的类型。

//......

}

53、定义模板类型别名:新标准中允许我们为类模板定义一个类型别名:例如:

template <typename T> using twin = pair <T, T>;

twin<string> authors; //authors是一个pair<string, string>

54、模板函数的默认模板实参:首先要明白什么是默认实参?那就是因为函数会被多次调用,并且使用相同的参数值,我们把这样反复出现的值称为函数的默认实参。举个实例:

string screen( int ht = 24, int wid = 50, char bg = “” );

其中我们为每一个形参都提供了默认实参,默认实参作为形参的初始值出现在形参列表中。不过要注意的一点是,一旦某个形参被赋予了默认值,它后面的所有形参都必须有默认值。

理解了上面这一点,那么模板函数的默认模板实参也就很好理解了。

template < class T = int >

class Numbers {

public:

Number( T v = 0 ): val( v ) {} };

55、实例化的显式控制:当模板被使用时才会进行实例化,那么在一个大系统中,如果多个文件中多次实例化模块,那么额外开销可能就非常严重。在新标准中,我们可以通过“显式实例化”来避免这种开销,使用extern来声明。例如:

extern template class Base< string >; //声明

56、模板函数与尾置返回类型:为了使用函数参数中的类型,我们必须使用尾置返回类型。举个实例:

template < typename It >

auto fun( It beg, It end ) -> decltype(*beg)

{

return *beg;

}

57、***引用折叠规则:

58、用static_cast将左值转换为右值:虽然我们不能隐式地将一个左值转换为右值引用,但我们可以用static_cast显式地将一个左值转换为一个右值引用。

59、***标准库std::forward函数:

60、可变参数模板:就是一个接受可变数目参数的模板函数或模板类。可变数目的参数被称为参数包,分为两种:模板参数包和函数参数包。

61、sizeof...运算符:这个的作用就是用于计算参数包中有多少个元素。举个实例:

template < typename ... Args > void g( Args ... args )

{

cout << sizeof...( Args ) << endl; //类型参数包的元素数目

cout << sizeof...( args ) << endl; //函数参数包的元素数目

}

62、***可变参数模板与转发:在新标准下,我们可以组合使用可变参数模板与std::forward机制来实现,将其实参不变地传递给其他参数。

63、标准库tuple类模板:一个tuple类可以有任意数量的成员。其实,可以将tuple看做一个“快速而随意”的数据结构。

定义一个tuple函数如下:tuple< int, vector<string>, bool > dept;

在初始化的时候要注意一个问题,那就是tuple的这个构造函数时explicit的,因此必须使用直接初始化语法:例如:

tuple< int, int , int > t1 = {1, 2, 3 }; //错误。

tuple< int, int , int > t1 {1, 2, 3 }; //正确。

标准库还定义了make_tuple函数,我们可以用它来生成tuple对象。例如:

auto item = make_tuple( “abc”, 3, true );

此外还提供了,tuple_size和tuple_element模板使用,还定义了< 和 == 运算符(这样我们可以将tuple序列传递给算法,并且可以在无序容器中将tuple作为关键字类型)。

64、新的bitset运算:原先标准库中定义了bitset类,使得位运算的使用更为容易,并且能够处理超过最长整型类型大小的位集合。当定义个bitset时,需要声明它包含多少个二进制位,例如: bitset< 32 > bitvec( “1234343453 ”);

bitset还定义了许多操作。

65、正则表达式库(书上例子很好):

参考网站:/article/4812126.html

参考网站:https://msdn.microsoft.com/zh-cn/library/ae5bf541(VS.80).aspx

66、随机数库:以前使用随机数,都是依赖于一个简单的C库函数rand。但是,有时我们需要的是不同范围的随机数。定义在头文件random中的随机数库通过一组协作的类来解决这些问题:随机数引擎类、随机数分布类。一个引擎类可以生成unsigned随机数序列;一个分布类使用一个引擎类生成指定类型的、在给定范围内的、服从特定概率分布的随机数。

怎么使用呢?

uniform_int_distribution< unsigned> u(0, 9);

default_random_engine e;

cout << u(e) << endl; //这样生成的随机数范围就是在0~9之间。

67、***浮点数格式控制:

68、***内联命名控件:这个和普通的嵌套命名空间不同,内联命名空间中的名字可以被外层命名空间直接使用。定义内联命名空间的方式是在关键字namespace前添加关键字inline。

69、继承的构造函数与多重继承:在C++11新标准中,允许派生类从它的一个或几个基类中继承构造函数。但是这个有个问题要注意,那就是如果从多个基类中继承了相同的构造函数,则会出错。那么怎么解决呢?如果一个类从它的多个基类中继承了相同的构造函数,则这个类必须为该构造函数定义它自己的版本。

70、有作用域的enum:枚举属于字面值常量类型。C++包含两种枚举:限定作用域的和不限定作用域的。C++11引入了“限定作用域的枚举类型”,这和不限定的有什么区别呢?

首先两者的定义形式不一样:“限定作用域”的关键字enum class + name,这都是不能缺省的;而“不限定作用域的”则省略掉关键字class,名字也是可有或可没有。

除了形式不一样,两者的成员作用域也是不同的。在限定作用的枚举类型中,枚举成员的名字只在花括号里面可访问,而不限定的枚举成员的作用域与枚举类型本身的作用域相同。举个实例:

enum color { red, yellow, green }; //这是不限定作用域的枚举类型

enum other { red, yellow, green }; //错误:重复定义枚举成员

enum class peppers { red, yellow, green }; //正确。

color a = green; //正确。

other b = green; //错误。不在有效的作用域。

other c = other::green; //正确。

color a = color::green; //正确。

71、说明类型用于保存enum对象:一个“不限定作用域的”枚举类型的成员会被成员自动转换成整形,而“限定作用域的”则不会。举个例子:

int i = color::red; //正确。会隐式地转换成int

int i = other::red; //错误。

说这个例子的原因是,实际上enum是由某种整数类型表示的,但具体是什么我们不确定。在C++11新标准中,我们可以在enum的名字后面加上冒号以及我们想使用的类型:

enum intValues : unsinged long long {

charTyp = 255, shortTyp = 65535, intTyp = 65535,

longTyp = 4294967295UL,

longlongTyp = 18446744073709551615ULL };

72、enum的前置声明:在C++11新标准中,我们可以提前声明enum。但是enum的前置声明(无论隐式地还是显示地)必须指定其成员的大小:

enum intValue : unsigned long long; //不限定作用域,必须指定成员类型。

enum class other; //限定作用域的,因为有默认成员类型为int,因而可以不直接指出。

73、标准库mem_fn类模板:在讲mem_fn之前要先回顾下另外两个相似的类模板:一个是function,另一个是bind。举个例子来说明这种情况:

如果我们想要在一个string的vector中找到第一个空string,显然不能使用下面的语句:

auto fp = &string::empty();

find_if( svec.begin(), svec.end(), fp ); //这是不行的。

这时候,便会想到用function:

function< bool ( const string& ) > fcn = &string::empty;

find_if( svec.begin(), svec.end(), fcn ); // fcn是一个接受string参数并返回bool值得函数。

到了这里,我们就明白了,如果要使用function,我们必须提供成员的调用形式和返回类型。

终于mem_fn的作用来了,那就是我们可以通过使用mem_fn来让编辑器负责推断成员的类型。用法如下:

find_if( svec.begin(), svec.end(), mem_fcn( &string::empty ) );

mem_fn 是那么简单好用,再看看bind:

find_if( svec.begin(), svec.end(), bind( &string::empty , _1) );

和function类似的地方是,当使用bind时,必须将函数中用于表示执行对象的隐式形参转换成显示的。和mem_fn类似的是,bind生成的可调用对象的第一个实参既可以是指针,也可以是引用。

74、类类型的union成员:以前C++是不能含有定义了构造函数或拷贝控制成员的类类型成员的,但是新标准取消了这一限制。

75、std::ostringstream buffer 的使用:可以将任何类型转为std::string。在最后输出的时候,是return buffer.str();

【******************C++11 新增的其他知识点*********************】

1、Unicode转utf8的问题:

头文件:#include <WinNls.h>

#include <codecvt>

CString str = L”中国人”;

std::wstring_convert<std::codecvt_utf8<wchar_t>> conv;

std::string str = conv.to_bytes(str);

Utf8的介绍:

在C++11中,char和string都已经被视为utf8编码。utf8是一种变长编码,对于unicode的字符被编码成1至4个字节。它可以向后兼容ASCII编码(这是一种现今最通用的单字节编码)。

此外utf8的优点就是没有字节序(大小端)的问题适合网络传输,还有是存储英文和拉丁文等字符非常节省存储空间。

对于编码的知识,参考网站:http://www.cppblog.com/Error/archive/2014/09/25/208413.html

Utf16的介绍:

在基本多语言平面内的码位UTF-16编码使用1个码元且其值与Unicode是相等的。

C++11将支持三种Unicode编码方式:UTF-8UTF-16,和UTF-32。除了上述char定义的变更,C++11将增加两种新的字元类别:char16_t和char32_t。它们各自被设计用来存储UTF-16以及UTF-32的字元。

以下展示如何产生使用这些编码的字符串字面值:

u8"I'm a UTF-8 string."

u"This is a UTF-16 string."

U"This is a UTF-32 string."

第一个字符串的类别是通常的const char[];第二个字符串的类别是const char16_t[];第三个字符串的类别是const char32_t[]。

3、对于std::enable_shared_from_this的使用:

http://blog.chinaunix.net/uid-442138-id-2122464.html

4、对于临界区加锁的问题,C++11提供了新的写法,如下:

参考网站:/article/4831262.html

std::mutex,最基本的 Mutex 类。

std::recursive_mutex,递归 Mutex 类。

std::time_mutex,定时 Mutex 类。

std::recursive_timed_mutex,定时递归 Mutex 类。

std::lock_guard,与 Mutex RAII 相关,方便线程对互斥量上锁。

std::unique_lock,与 Mutex RAII 相关,方便线程对互斥量上锁,但提供了更好的上锁和解锁控制。

5、C++11已提供完整的string - atoi/itoa、to_string()。

6、C++11多线程编程:

参考网站:/article/4812131.html

C++11提供了新头文件<thread>、<mutex>、<atomic>、<future>等用于支持多线程。

现在创建一个新线程的方法是,如下:

std::thread t(_mythread, args); //_mythread这是一个自定义的函数

t.detach();//加上这句,新线程在用完之后会自动被回收。而//不用t.join()方法阻塞主线程.

Int main()

{

std::vector<std::thread> threads;

for(int i = 0; i < 5; ++i){

threads.push_back(std::thread([](){

std::cout << "Hello from lamda thread " << std::this_thread::get_id() << std::endl;

}));

}

for(auto& thread : threads){

thread.join();

}

}

7、可以用于睡眠: std::this_thread::sleep_for(chrono::seconds(10));

8、采用新的static_assert。

因为断言assert宏只有在程序运行时才能起作用,static_assert是在编译期就能做出一些断言。

static_assert( 常量表达式,提示字符串 );

9、condition_variable的使用:通常都是和mutex一起配合使用的。

头文件:#include<mutex>

#include<condition_variable>

std::mutex mutex;

std::condition_variable cond_var; //全局变量

//thread1先运行。

Thread1(){

...

std::unique_lock<std::mutex> lock(mutex);

cond_var.wait(lock); //线程来到这里被挂起。只有等到//Thread2执行cond_var.notify_one();才//继续往下执行。

...

}

Thread2(){

std::unique_lock<std::mutex> lock(mutex);

...

cond_var.notify_one();//这句的作用是唤醒Thread1的wiat(lock)。

}

(继续学习中,完善中。)
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: