C++11新特性

C++11新特性

初始化变量

  • 自定义类型可以用大括号(构造列表)
  • 构造列表还可以作为函数参数和返回值
  • 在定义变量时检测精度丢失问题

自动类型

  • auto回炉重造,在编译期间推到变量类型。主要用在复杂类型减少代码量,比如

    std::vector::iterator it

  • auto不能做函数参数,定义类的non-static成员变量,实例化模板

  • decltype可以根据表达式完成之后的结果推断类型

返回值类型追踪

  • 在模板体系里,一个函数想通过返回值带出结果,而这个返回值类型是由输入参数类型决定的,那么就需要追踪
template<class T1, class T2> 
auto Add(const T1& left, const T2& right)->decltyp e(left+right) 
{   
    return left + right;
} 
  • 支持可迭代范围内的for循环
  • λ表达式,匿名函数,底层是仿函数的实现方式,使用场景是在函数内部封装一个在本函数使用频次较高的内部函数。
int main()
{
    int a = 1;
    int b = 2;
    auto fun2 = [=, &b](int c) ->int {return b += a + c; };
    fun2(3);
}
  • final修饰派生类的虚函数,让这个虚函数不能再被下级重写
  • override保证了重写虚函数成功
  • 跨平台线程库支持

接下来是重头戏!

移动语义,右值引用,完美转发

右值引用

右值就是不可被修改的,不能被取地址操作。

最常见的右值是常量,函数返回值或者表达式产生的临时对象。还有一种是将亡值。

右值引用就是对右值进行引用的类型

String ReturnRValue() {     return String("1234"); } 
void TestString() {  
    //临时对象是右值也是将亡值
    String&& s = ReturnRValue();
    //接下来可以控制这个右值
}

右值引用还可以引用将亡值,延续了它的生命周期,配合移动构造转移其资源。

万能引用,是const 类型的左值引用。可以接收常量左值,非常量左值,右值。

移动语义

分析一下String类的赋值运算符重载,调用赋值后语句后首先进入+号函数,+号函数退出时调用拷贝构造一个临时对象,之后str3调用=号函数将临时对象的内存拷贝到自己类中。可以看出,在第一步构造临时对象时,调用的拷贝构造是多余的。

String& operator=(const String& s)
    {
      if (this != &s) {
          char* pStr = new char[strlen(s._pStr) + 1];
          strcpy(pStr, s._pStr);
          delete[] _pStr;
          _pStr = pStr;
      }
      return *this;
    }   
String operator+(const String& s) {
    int len = strlen(s._pStr) + strlen(_pStr) + 1;
    String tmp(len);//借助构造函数
    strcpy(tmp._pStr, _pStr);
    strcat(tmp._pStr, s._pStr);
    return tmp;
    }
String str1("lalala");
String str2("hahaha");
str3 = str1 + str2;

移动语义是值资源的转移,移动构造函数判断参数是右值引用,直接转移走资源。这样在创建临时对象时,tmp就是将亡值,所以直接转移了tmp的资源。

String(String&& s)
    :_pStr(s._pStr) 
    {
        s._pStr = NULL;
    }

总结一下就是移动构造使用了右值引用,达到对将亡值资源转移的目的,减少不必要的内存拷贝。

move的用处

还可以通过move把对象转为右值引用类型。

分析一个例子。调用get函数,get函数退出时要构造临时对象,调用的是移动构造,但是移动构造里的构造的两个对象不是右值引用,会调用string类的拷贝构造函数,性能不好。

class Person{
public:
    Person(Person&& p)  
    : _name(p._name)    
    , _sex(p._sex)  
    , _age(p._age)   
    {
    } 
private:  
    String _name;  
    String _sex; 
    int _age;
}; 
Person GetTempPerson() { 
    Person p("prety", "male", 18);  
    return p;
}

move上场,强行改变了name,sex对象成为右值引用,调用string类的移动构造。name是即将销毁的对象,但是不像p对象那么明显编译器看不出来,所以需要显式move转换。

Person(Person&& p)    
    : _name(move(p._name))  
    , _sex(move(p._sex))  
    , _age(p._age)  
{} 

完美转发

//创建临时变量,有开销
void fun(int a) {
    a += 1;
}
template<typename T>
void Transpond(T a) {
    fun(a);
}

//引用转发的话,不能引用右值
void fun(int a) {
    a += 1;
}
template<typename T>
void Transpond(T& a) {
    fun(a);
}

//万能引用,如果fun函数是引用类型就转不过去
void fun(int& a) {
    a += 1;
}
template<typename T>
void Transpond(const T& a) {
    fun(a);
}

万能引用的目标是在模板体系中,一个函数模板可以透明转发参数给另一个函数。上面三种转发都失败了。

正确的姿势

template<typename T>
void PerfectForward(T&& t){
    fun(std::forward(t));
}

应用模板匹配的折叠规则,可以根据转发函数参数类型和推倒类型进行转化。

forward (T&&)转化为 T&

你可能感兴趣的:(c/c++编程艺术)