c++11 特性(二)

来源:互联网 发布:学软件开发学校 编辑:程序博客网 时间:2024/05/17 21:39

8 模板和STL方面

为了改善模板和标准化方面的易用性,C++ 11做了多个改进:

8.1 改进的for循环

double prices[5] = {1, 2, 3, 4, 5};for(double x : prices)for(auto x : prices)

如果要在循环中修改数组或容器中的每个元素,可以使用引用:

for(auto& x : prices)

8.2 新增的STL容器

C++ 11新增了forward_list、unordered_map、unordered_multimap、unordered_set、unordered_multiset。

新增了模板array,实例化时指定元素类型和个数:

std::array<int, 10> ar; // 10个int

8.3 新增STL方法

cbegin()、cend()、crbegin()、crend()。这几个方法将容器元素视为const。

8.4 valarray升级版

对于valarray模板,C++ 11新增了两个方法begin()和end()。

8.5 摒弃了export

C++ 98增加的关键字export可以让程序员将模板定义放在接口文件中,实现文件中放置方法体,但是实践证明这一点也不现实,因此C++ 11把它摒弃了。

8.6 尖括号

旧时的C++要求在定义嵌套的模板时,两个尖括号之间必须有空格,比如:
vector<list<int> >,但是C++ 11中不再需要这样了,比如vector<list<int>>也可以通过的。

9 右值引用

C++新增了右值引用,使用&&表示。【相对于左值,右值表示字面常量、表达式、函数的非引用返回值等】

如:

int&& r1 = 12;

int x = 5;

int y = 8;

int&& r2 = x + y;

我们可以通过r1来修改12,很方便。

10 移动语义和右值引用

10.1 为何需要移动语义

移动语义就是为了避免多余的复制工作,就是说与其复制,还不如将源地址传给使用者,因为有时复制工作确是没什么用。

要实现移动语义,需要采用某种措施让编译器知道到底什么情况下需要复制,什么情况下不必复制。这时,右值引用就可以配上用场了。

传统的复制构造函数执行深复制,并且不改变实参,因此,参数为const类型;

移动构造函数只是更改了引用记录,并且可能会改变实参,因此,参数必须是非const类型;
如下实例:

class Use{public:    Use();    Use(const Use& f);   // copy constructor    Use(Use&& f);        // move constructor    ...private:int n;char* pc;};...Use::Use(const Use& f) : n(f.n) // 深复制{    pc = new char[n];    for(int i = 0; i < n; i++)       pc[i] = f.pc[i];}Use::Use(Use&& f) : n(f.n){    pc = f.pc;    // 移动构造,转移控制权    f.pc = nullptr;    f.n = 0;}

10.2 移动构造解析

虽然移动构造定义好了,但是如何调用呢,也就是说什么情况下才会发生移动构造呢?

必须使用右值引用调用:

Use two = one;           // match Use::Use(const Use&);Use four(one + three);  // match Use::Use(Use&&);

因为one是左值,而one + three是右值。

移动赋值情况类似,不记录了。

10.3 强制移动

移动构造和移动赋值使用右值引用,如果需要让他们操作左值呢?

答案是使用static_cast<>将对象强制转换为Use&&即可。不过在C++ 11种提供了更便捷的操作,在头文件utility中声明的函数std::move(…)。

如果使用了std::move()函数调用,但是类中却没有定义移动相关函数,那么编译器会调用传统版本的移动构造函数和移动赋值操作符。

11 新的类功能

假设你为类定义了构造函数,那么类就不会自动提供默认的构造函数了,然而,如果你仍然想使用类提供的默认版本,那么可以使用default关键字:

class Some{public:    Some(Some&&);    Some() = default; // use default constructor    ...};

相反地,如果要禁用编译器提供的默认函数,可以使用delete:

class Some{public:    Some(Some&&);    Some() = default; // use default constructor    Some(const Some&) = delete; //disable copy constructor    ...};

当然要想禁用某个编译器提供的函数也可以显式声明为private,但是使用delete更方便且不易出错。

注意:default关键字只能用于6个特殊函数,而delete却能够用于任何成员函数。

委托构造

如果一个类包含多个构造函数,C++ 11允许在一个构造函数中的定义中使用另一个构造函数,但这必须通过初始化列表进行操作,如下:

class Notes{    int k; double x; string s;public:    Notes(int kk, double xx, string ss) : k(kk), x(xx), s(ss){ }    Notes(int kk) : Notes(0, 0.5, “benxin”){ k = kk; }    ...};

继承构造

C++ 11允许派生类继承基类的构造函数。C++ 98提供了一种让某个名称空间中的同名重载函数都可用的语法,如下:

namespace Box{    int fn(){}    int fn(int){}    int fn(double){}}

using Box::fn;该语句使得fn的所有重载版本都可用。我们一般使用这种方法在派生类中调用基类的同名函数。之所以提供这种语法,就是因为覆盖是以函数名为基础的,不论参数是否对应都将被覆盖。

C++ 11将这种语法用于构造函数中,使得派生类将继承基类的构造函数(默认构造函数、复制构造函数、移动构造函数除外)。