C++11新特性

Posted by 谢玄xx on May 11, 2022

C++11的新特性也多次被问到,在这里做一个简要总结。

一、统一的初始化方法——赋值符号”=”可省略

Talk is cheap. Show you the code : )

class A {
    int i,j;
    A(int m, int n):i(m),j(n) {}
};
int main() {
    A* pa = new A {3,7};

    int arr[3] {1, 2, 3};   //C++11可以直接这么写,而不需要括号。
    vector<int> a {1, 2, 3};    //vector 同理
    string str {"Today"};       //stirng 同理
    unordered_map<string, int> pairs { {"a", 1}, {"b", 2}};
    int *p = new int[20] {1,2,3};
}

二、成员变量默认初始值

class B {
public:
    int m = 1234;
    int n;
};

int main() {
    B b;
    cout << b.m << endl;    //输出值为1234,即对象的成员变量默认初始值为类中定义的m值
    return 0;
}

三、auto 关键字

auto关键字用于定义变量,编译器可以自动判断变量类型。

auto a = 100; //a的数据类型为int
auto p = new A();//p的数据类型为A*;
auto b = 1234LL;//b的数据类型为long long

unordered_map<string, int> pairs;
for(auto i = pairs.begin(); i != pairs.end(); i++) {
    cout << i->first << i->second << endl;
}
//这个i的类型是 unordered_map<string, int> :: iterator, 可见方便多了!

除了判断变量类型外,auto还有一个关键用途:

class A {
    
};
//运算符重载
A operator + (int n, const A & a) {
    reutrn a;
}

template <class T1, class T2>
//->表明函数的返回值
auto add(T1 x, T2 y)->decltype(x + y) {
    return x + y;
}

auto i = add(10, 1.5);  //i的数据类型为float, 值为11.5
auto j = add(10, A());  //j的数据类型为A,因为10 + A()中的加号运算符已被重载,返回的是后面的加数A()。
//这个decltype是什么含义?请看下文。

四、Lambda表达式(匿名函数)

匿名函数是C++11中重要的新特性之一。

在实际的项目开发or刷题中,会出现一些“只需要被调用一次的”函数。如果为此专门写一个函数,基于内存考量则不太划算;这个时候就可以定义一个匿名函数(不需要起名字的函数)

调用匿名函数

可以这么写:

    int c = [](int a, int b) -> int {
        return a + b;
    }(1,2);

根据上述匿名函数进行改良:

    auto c = [](int a, int b) -> int {
        return a + b;
    };
    
    d = c(1, 2);

其中,[]为捕获列表,()内为参数及数据类型,箭头指向返回值类型。

调用嵌套匿名函数

auto c = [](int m){
    return [m](int n) {
        return m + n;
    }(1);       //这里是给n赋值 n = 1
}(2);           //这里是给m赋值 m = 2

cout << c << endl;  //这里显而易见输出 2 + 1 = 3

如果我不想立刻调用这个匿名函数,我可以这么写:

auto c = [](int m){
    return [m](int n) {
        return m + n;
    };       //这里是给n赋值 n = 1
};           //这里是给m赋值 m = 2

int f = c(2)(1);    //请注意传参的顺序——先外层后内层
cout << f << endl;  //这里显而易见输出 2 + 1 = 3
  • 嵌套匿名函数(函数式编程)在多线程和并发场景中具有天生优势,非常常用。
  • 简而言之,匿名函数的核心在于函数体内的表达式以及函数体结束后末尾对于函数变量的赋值

更多内容详见我之后的博文——一些有趣的C++代码书写方法

五、decltype关键字

如上文所述,decltype用于做类型判别。上述函数意为:add函数的返回值类型为decltype(x + y)。decltype会根据x + y的类型决定自己是什么类型。这样编译器以后从add模板实例化add函数时,就会根据x + y的表达式类型来推断出add函数返回值的类型。

int i;
double j;
struct A {
    double k;
};
const A* a = new A();

decltype(a) x1; //定义了x1,变量类型为decltype(a)。由于a的数据类型为A*, 所以x1的类型就是A*.
decltype(i) x2; //定义了x2,变量类型为decltype(i)。由于i的数据类型为int,所以x2的类型为int.
decltype(a->k) x3;  //x3的类型是double
decltype((a->k)) x4 = t;    //用括号括起来的时候(双重括号..)要注意。这时x4的类型就是double的引用。

六、智能指针

详见博文:智能指针专题

七、右值引用

右值引用

  1. int &&c = 20; 专门用来引用右值类型,指令上,可以自动产生临时量然后直接引用临时量 c = 40;
  2. 右值引用变量本身是一个左值,只能用左值引用来引用它;
  3. 不能用一个右值引用变量,来引用一个左值

这里解释一下:右值引用变量本身是一个左值,只能用左值引用来引用它。一个&是左值引用,&&是右值引用。

int &&c = 20; /// c这个变量是左值

右值引用可以用如下代码来加深理解:

#include <iostream>
#include <typeinfo>
using namespace std;
int main()
{
	int a = 10; // 左值,它有内存,有名字,值可以修改的
	int &b = a;

	//int &c = 20; // 20是右值:没内存,没名字。 这句是错的左值不能引用右值
	//C++11提供了右值引用,汇编中是先mov再lea和左值引用相反,然后多了一步mov
	int &&c = 20; // 右值引用引用右值
	c = 30;

	int &e = c; // 一个右值引用变量,本身是一个左值

	/*
	int temp = 20;
	temp -> d
	*/
	const int &d = 20; //可编译通过,汇编中和右值引用一样
	// 与右值引用不同的是d无法修改
	return 0;
}