C++11的新特性

        C++11是由C++标准委员会指定的语言规范。相比于C++98/03,C++11则带来了数量可观的变化,其中包含了约140 个新特性,以及对C++03标准中约600个缺陷的修正,C++11能更好地用于系统开发和库开发、语法更加泛华和简单化、更加稳定和安全,不仅 功能更强大,而且能提升程序员的开发效率。

列表初始化{ }

C++11扩大了{ }的适用范围,使得{ }可以用于所有内置类型和用户自定义类型。

具体例子:

#include<iostream>
#include <vector>
#define NUM 2
class Base
{
public:
	Base(int left, int right)
		:_left(left),
		_right(right)
	{}

private:
	int _left;
	int _right;
};
int main()
{
	int array[2] = { 1, 2 };
	std::vector<int> v = { 1, 2, 3 };
	Base b = { 1, 2 };
	return 0;
}

变量类型推导auto

应用场景:变量类型太长或者创建变量不清楚类型时。

具体例子:

#include <iostream>
#include <unordered_map>
int main()
{
	std::unordered_map<std::string, std::string> hash = { {"apple", "苹果"}};
	std::unordered_map<std::string, std::string>::iterator it = hash.begin();
	auto cur = it;
	return 0;
}

注意事项:

1、auto类型不可做函数形参,auto声明的变量必须由编译器在编译时期推导而得。auto推导的类型都是已经被初始化的。

2、当在同一行声明多个变量时,这些变量必须是相同的类型,否则编译器将会报错,因为编译器实际只对第一个类型进行推导,然后用推导出来的类型定义其他变量。

3、用auto声明指针类型时,用auto和auto*没有任何区别,但用auto声明引用类型时则必须加&。

decltype类型推导

应用场景:解决需要根据表达式运行完成之后所得结果的类型推导问题。

具体例子:

#include <iostream>
int Func()
{
	return -1;
}
int main()
{
	int a = 10;
	int b = 20;
	decltype(a + b) c;
	decltype(Func()) funcType;
	std::cout << typeid(funcType).name() << std::endl;//int
	return 0;
}

范围for

C++11中提供的一种遍历容器的方法

具体例子:

#include <iostream>
#include <vector>
#include <list>
int main()
{
	std::list<int> _list = { 1,2,3 };
	std::vector<int> _v = { 1,2,3 };
	for (int e : _list)
		std::cout << e << std::endl;
	for (int e : _v)
		std::cout << e << std::endl;

	return 0;
}

        可以结合auto来进行类型推导,也可以在类型后加&,表示以引用的方式来依此提取右边容器内的元素。

final与override

final和override在C++的多态中使用。

1、final:修饰虚函数,表示该虚函数不能被重写

2、override: 检查派生类虚函数是否重写了基类某个虚函数,如果没有重写则编译报错。

具体使用:

class Car
{
public:
 virtual void Drive() final {}
};
class Benz : public Car
{
public:
 virtual void Drive() {cout << "Benz-舒适" << endl;}
};

class Car
{
public:
 virtual void Drive(){}
};
class Benz : public Car {
public:
 virtual void Drive() override {cout << "Benz-舒适" << endl;}
};

默认成员函数控制

默认成员函数控制又包括显示缺省函数删除默认函数。

        在C++中对于空类编译器会生成一些默认的成员函数,比如:构造函数、拷贝构造函数、运算符重载、析构 函数和&和const&的重载、移动构造、移动拷贝构造等函数。如果在类中显式定义了,编译器将不会重新生成默认版本。有时候这样的规则可能被忘记,最常见的是声明了带参数的构造函数,必要时则需要定义不带 参数的版本以实例化无参的对象。而且有时编译器会生成,有时又不生成,容易造成混乱,于是C++11让程序员可以控制是否需要编译器生成。

显示缺省函数

C++11中,可以在函数声明或定义时加上 =default 显式的指示编译器生成该函数的默认版本,用=default 修饰的函数称为显式缺省函数。

具体用法:

class Base
{
public:
	Base(int left, int right)
		:_left(left), _right(right)
	{}
	Base() = default;
private:
	int _left;
	int _right;
};

删除默认函数

如果能想要限制某些默认函数的生成,在C++98中,是该函数设置成private,并且不给定义,这样只要其他 人想要调用就会报错。在C++11中更简单,只需在该函数声明加上=delete即可,该语法指示编译器不生成对 应函数的默认版本,称 =delete 修饰的函数为删除函数。

具体使用:

class Base
{
public:
	Base(int left, int right)
		:_left(left), _right(right)
	{}
	Base() = default;
	Base(const Base&) = delete;
	Base& operator=(const Base&) = delete;
private:
	int _left;
	int _right;
};

右值引用

为了提高程序运行效率,C++11引入右值引用,顾名思义,右值引用只能引用右值。形似int&&

左值与右值

1、 普通类型的变量,因为有名字,可以取地址,都认为是左值。

2.、const修饰的常量,不可修改,只读类型的,理论应该按照右值对待,但因为其可以取地址(如果只是 const类型常量的定义,编译器不给其开辟空间,如果对该常量取地址时,编译器才为其开辟空间), C++11认为其是左值。

3、如果表达式的运行结果是一个临时变量或者临时对象,认为是右值。

4、 如果表达式运行结果或单个变量是一个引用则认为是左值。

左值引用和右值引用

左值引用只能引用左值,但const左值引用既可以引用左值,也可以引用右值。

右值引用一般只能引用右值,但可以引用move后的左值。

具体例子:

int main()
{
	int a = 10;
	int& pa = a;
	int& pb = 10;//报错,左值引用无法引用右值
	const int& ppa = a;
	const int& ppb = 10;
	int&& pa2 = 10;
	int&& pb2 = a;//报错,右值引用无法引用左值
	int&& ppa2 = std::move(a);
	return 0;
}

引入右值引用的具体原因

本质是为了减少拷贝次数,提高效率。例如一个类中的拷贝构造和操作符重载,传值返回时,必须拷贝构造一个临时对象,再通过返回的临时对象来构造接受结果的对象。

看下面一段代码:

class Base
{
public:
	Base(int left, int right)
		:_left(left), _right(right)
	{}
	Base operator+(const Base& b)
	{
		Base tmp (_left + b._left, _right + b._right);
		return tmp;
	}
private:
	int _left;
	int _right;
};
int main()
{
	Base a(1, 2);
	Base b(3, 4);
	Base c(a + b);
	return 0;
}

        在operator+中:tmp在按照值返回时,必须创建一个临时对象,临时对象创建好之后,tmp就被销毁了,最后使用返回的临时对象构造c,c构造好之后,临时对象就被销毁了。仔细观察会发现:tmp和所形成临时对象,以及c,每个对象创建后,都有自己独立的空间,而空间中存放内容也都相同,相当于创建了三个内容完 全相同的对象,对于空间是一种浪费,程序的效率也会降低,而且临时对象确实作用不是很大。

移动语义

        C++11提出了移动语义,将一个对象中的资源转移到另一个对象中。依然是上面的代码,通过移动语义,会将tmp返回时构造的临时对象识别为”将亡值“,C++11认为临时对象为右值,将返回对象的资源直接转移给该临时对象,而再构造c时,又会调用移动语义,直接将返回的临时对象资源转移给对象c。

注意:

1、 移动构造函数的参数不能设置成const类型的右值引用,因为资源无法转移而导致移动语义失效。

2.、在C++11中,编译器会为类默认生成一个移动构造,该移动构造为浅拷贝,因此当类中涉及到资源管理时,用户必须显式定义自己的移动构造。

        当需要用右值引用引用一个左值时,可以通过move函数将左值转化为右值。C++11中std::move()函数位于 头文件中,唯一的功能就是将一个左值强制转化为右值引用,然后实现移动语义。被转化的左值,其生命周期并没有随着左值的转化而改变,即std::move转化的左值变量value不会被销毁,但会变的无效。

完美转发

        完美转发是指在函数模板中,完全依照模板的参数的类型,将参数传递给函数模板中调用的另外一个函数。所谓完美:函数模板在向其他函数传递自身形参时,如果相应实参是左值,它就应该被转发为左值;如果相 应实参是右值,它就应该被转发为右值。

       万能引用,既可以接收左值,也可以接收右值,但万能引用虽然都能接收,但是统一都退化为左值,所以要通过完美转发来保持实参原有的属性。

具体例子:

#include <iostream>
void Fun(int& x) 
{
	std::cout << "left_value ref" << std::endl;
}
void Fun(int&& x) 
{
	std::cout << "right_value ref" << std::endl;
}
template<typename T>
void PerfectForward(T&& t) 
{ 
	Fun(std::forward<T>(t));
}
int main()
{
	int a = 10;
	PerfectForward(a);
	PerfectForward(std::move(a));
	return 0;
}

移动语义具体例子:

#include <iostream>
class Base
{
public:
	Base(Base&& b)
		:_left(std::move(b._left)),
		_right(std::move(b._right))
	{}
	Base operator=(Base&& b)
	{
		_left = b._left;
		_right = b._right;
	}
private:
	int _left;
	int _right;
};

lambda表达式

本质是一种匿名函数。

应用场景:使用algorithm中的sort时,对自定义类型对象进行排序,需要给出对应的比较规则,往往都是通过仿函数来实现,但每出现一个类别,就要定义一个类,写一个用于比较的仿函数,比较繁琐,而lambda表达式可以很好的解决这个问题。

具体例子:

sort(goods, goods + sizeof(goods) / sizeof(goods[0]), [](const Goods& l, const Goods& r)
 ->bool
 {
     return l._price < r._price;
 });

lambda表达式语法

[capture-list] (parameters) mutable -> return-type { statement }

1、[ ]捕捉列表,编译器根据[ ]来判断接下来的代码是否为lambda函数,捕捉列表能够捕捉上下文中的变量供lambda函数使用。

包括

父作用域:指包含lambda函数的语句块

[ = ]:对父作用域进行传值捕捉

[ & ]:对父作用域进行传引用捕捉

[ val ]:对val变量进行传值捕捉

[ &val ]:对val变量进行传引用捕捉

[ this ]:对当前this指针进行传值捕捉

2、( )参数列表,和普通函数的参数列表相同,可以不传参数。

3、mutable:默认情况下,lambda函数总是一个const函数,mutable可以取消其常量性。使用该修饰符时,参数列表不可省略(即使参数为空)。

4、->return-type,表示该lambda表达式最终返回值的类型,可以省略。

5、{ }函数体,除了可以使用其参数外,还可以使用所有捕获到的变量。

        lambda表达式实际上可以理解为无名函数,该函数无法直接调用,如果想要直接调用,可借助auto将其赋值给一个变量。

注意:. lambda表达式之间不能相互赋值,即使看起来类型相同。

        实际在底层编译器对于lambda表达式的处理方式,完全就是按照函数对象的方式处理的,即:如果定义了一 个lambda表达式,编译器会自动生成一个类,在该类中重载了operator()。

  • 24
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值