c++11 特性(二)

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将这种语法用于构造函数中,使得派生类将继承基类的构造函数(默认构造函数、复制构造函数、移动构造函数除外)。

猜你喜欢

转载自blog.csdn.net/programer_vc/article/details/78651046