【C++11】C++11常用特性详解

这篇具有很好参考价值的文章主要介绍了【C++11】C++11常用特性详解。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

【C++11】C++11常用特性详解


需要云服务器等云产品来学习Linux的同学可以移步/-->腾讯云<--/-->阿里云<--/-->华为云<--/官网,轻量型云服务器低至112元/年,新用户首次下单享超低折扣。


 目录

一、列表初始化

1、一切皆可大括号

2、 std::initializer_list

2.1initializer_list的原理

2.2使用initializer_list仿写vector的构造函数 

二、auto、decltype、nullptr、范围for

1、auto

2、decltype

3、nullptr

4、范围for

三、STL新增容器和已有容器的新接口

1、STL新增容器

1.1array

1.2forward_list

2、已有容器的新接口

2.1cbegin()、cend()

2.2vector、string和deque的shrink_to_fit

2.3移动构造和移动赋值

2.4emplace_xxx插入接口

四、final和override

五、lambda表达式

1、lambda表达式介绍

2、lambda表达式的捕捉列表、mutable

3、lambda的底层就是仿函数

六、容器中新增的emplace系列

1、可变参数模板

参数包的解析

2、emplace系列的高效性 

七、包装器

1、function包装器

1.1包装器的意义

1.2包装器的使用

1.3包装器求解力扣150题《逆波兰表达式求值》

2、bind


自C++11开始。C++的语法就开始不太像C++98和C了。

一、列表初始化

1、一切皆可大括号

struct Point
{
	int _x;
	int _y;
};
class Date
{
public:
	Date(int year, int month, int day)
		:_year(year)
		, _month(month)
		, _day(day)
	{
		cout << "Date(int year, int month, int day)" << endl;
	}
private:
	int _year;
	int _month;
	int _day;
};
int main()
{
	Point p1 = { 1,2 };
	Point P2{ 1,2 };

	int arr1[] = { 1,2 };
	int arr2[]{ 1,2 };

	int a1 = 1;
	int a2 = { 2 };
	int a3{ 3 };
    
	// C++11中列表初始化也可以适用于new表达式中
	int* p3 = new int[10];
	int* p4 = new int[10] {1, 2, 3};

	Point* p3 = new Point[2]{ { 1,1 },{ 2,2 } };

	//创建对象时也可以使用列表初始化方式调用构造函数初始化
	Date d1(1, 2, 3);
	//C++11支持的列表初始化,这里会调用构造函数初始化
	Date d2 = { 1,2,3 };//因为日期类支持三个参数的构造函数
	Date d3{ 1,2,3 };
	return 0;
};

        C++11扩大了用大括号括起的列表(初始化列表)的使用范围,使其可用于所有的内置类型和用户自定义的类型,使用初始化列表时,可添加等号(=),也可不添加。

2、std::initializer_list

2.1initializer_list的原理

        initializer_list相当于一个容器,和vector很像,区别在于它不存储数据,有指针指向存储于常量区的数组。

【C++11】C++11常用特性详解

        这里的{}被识别成了一个类,这个类叫std::initializer_list。

        上面的Date d2 = { 1,2,3 };是因为日期类支持了三个参数的构造函数,如果给它4个参数进行构造,将会报错。那为什么vector、list等支持不定参数的构造呢?并不是因为它们实现了多种不同参数的构造函数,而是因为这些容器中增加了std::initializer_list作为参数的构造函数。

int main()
{
    //支持不定参数的构造
	vector<int> v1 = { 1,2,3,4 };
	vector<int> v2{ 1,2,3,4,5,6 };
	return 0;
}

        vector中有一个initializer_list的构造函数,外部传参构造vector时,使用传入的参数先构造了一个initializer_list对象,initializer_list再将接收到的参数“push_back”进vector,这样vector就能够拥有不定参数的构造能力了。

【C++11】C++11常用特性详解

//里面的花括号是调用Date的构造函数,外面的花括号类型是initializer_list<Date>
vector<Date> v={{1,1,1,},{2,2,2},{3,3,3}};
//里面的花括号将生成pair的匿名对象,外面的花括号将生成initializer_list<pair<string,string>>的对象
map<string,string> dict = {{"字符串","string"},{"排序","sort"}};

2.2使用initializer_list仿写vector的构造函数 

class vector
{
public:
    vector(const initializer_list<T>& il)//initializer_list<T>& il是不行的,il具有常属性
        :_start(nullptr)
        ,_finish(nullptr)
        ,_endofstorage(nullptr)
    {
        typename initializer_list<T>::iterator it = il.begin();
        while (it != il.end())
        {
            push_back(*it);
            ++it;
        }
    }
	//......
};

二、auto、decltype、nullptr、范围for

1、auto

        自动推导类型。非常好用。

2、decltype

【C++11】C++11常用特性详解

        typeid(a).name()返回的是变量类型的字符串;

        而decltype只能用于推导一个变量的类型,给另一个变量的类型进行定义。decltype的使用场景:

template<class T1, class T2>
void F(T1 t1, T2 t2)
{
    //暂时不确定ret的类型,因为你不知道外部传入的T1,T2哪个会被提升
	decltype(t1 * t2) ret;
	cout << typeid(ret).name() << endl;
}

        当然这个例子你用auto也行,所以decltype是一个没啥用的特性。

3、nullptr

        由于C++中NULL被定义成字面量0,这样就可能会带来一些问题,因为0既能指针常量,又能表示整形常量。所以出于清晰和安全的角度考虑,C++11中新增了nullptr,用于表示空指针。

#ifndef NULL
#ifdef __cplusplus
#define NULL   0
#else
#define NULL   ((void *)0)
#endif
#endif

4、范围for

        语法糖,本质是替换成迭代器。非常好用。

三、STL新增容器和已有容器的新接口

1、STL新增容器

        C++11中STL新增了<array>、<forward_list>、<unordered_set>、<unordered_map>,后面两个在往期博客中已经专门介绍过了。

1.1array

【C++11】C++11常用特性详解

        array是固定大小的顺序容器。和vector相比,array中的数据并不要求挨着存储,所以并没有push_back。

        array并不会对数组进行初始化,而vector的构造函数会对vector进行初始化。

【C++11】C++11常用特性详解

        array和普通数组在越界方面的比较:

        普通数组对数组的边界是必检的,但对于其他位置则是抽查,如上图数组a1成功访问a1[20]的数据。

        而array不一样,只要数组越界编译器必崩溃。

        无论使用哪种方式玩定长数组都是可以的,不过个人认为写代码写出数组越界纯属个人菜,得认。

        对于大多数的C系程序员来说,更愿意接受普通数组的写法,因为用习惯了,通俗易懂。其次,array并不会对数组进行初始化,它对数组越界进行了严格的检查,势必增加了开销,相对于普通数组唯一的优势是有错必报,但是vector不是比array更香吗?

1.2forward_list

        这是一个单链表,只支持头插头删,不支持尾插尾删。

        对比list的优点就是每一个节点可以节省一个指针的空间。

2、已有容器的新接口

2.1cbegin()、cend()

        让人一看就知道这里调用的是const迭代器。【C++11】C++11常用特性详解

2.2vector、string和deque的shrink_to_fit

        给vector提供了缩容方法。这可能会导致重新分配空间,但是不会影响vector的size。谁没事会去缩容啊。

2.3移动构造和移动赋值

        很有用的特性,见【C++11】左值引用和右值引用。

2.4emplace_xxx插入接口

        性能强于C++98的push_back和insert等接口。具体见本文第六节。

四、final和override

        final修饰类,表示该类不能被继承,final修饰虚函数,表示该虚函数不能被重写。

        override修饰子类的虚函数,用于检查子类是否完成对该函数的重写。

五、lambda表达式

1、lambda表达式介绍

        lambda表达式的作用是创建一个匿名函数,可以在需要函数对象的地方使用。它通常用于函数式编程中,可以避免定义一个命名函数,从而简化代码。使用lambda表达式可以在代码中直接定义一个函数对象,并将其作为参数传递给其他函数或算法,或者直接调用它。lambda表达式可以捕获其所在作用域的变量,并将其作为函数对象的成员变量,从而实现闭包功能。使用lambda表达式可以使代码更加简洁、清晰,提高程序的可读性和可维护性。

        lambda本质是一个匿名函数,其表达式书写格式:[capture-list] (parameters) mutable -> return-type { statement }

        [capture-list] : 捕捉列表,该列表总是出现在lambda函数的开始位置,编译器根据[]来判断接下来的代码是否为lambda函数,捕捉列表能够捕捉上下文中的变量供lambda函数使用,必须写

        (parameters)参数列表。与普通函数的参数列表一致,如果不需要参数传递,则可以连同()一起省略;

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

        ->returntype返回值类型。用追踪返回类型形式声明函数的返回值类型,没有返回值时此部分可省略。返回值类型明确情况下,也可省略,由编译器对返回类型进行推导

        {statement}函数体。在该函数体内,除了可以使用其参数外,还可以使用所有捕获到的变量,必须写

【C++11】C++11常用特性详解

2、lambda表达式的捕捉列表、mutable

        这是一段lambda表达式对外部参数b的捕获:

int main()
{
	int a = 0, b = 1;
	auto add = [b](int a) {return a + b; };//把上文的b捕捉到lambda表达式中
	return 0;
}

        捕捉的真的是b吗?其实捕捉的仅仅是带const属性的b的拷贝罢了。可使用mutable取消拷贝对象的const属性:

auto add = [b](int a) mutable{return a + b; };//取消b的const属性

        现在我想用lambda表达式交换一下外部两个变量的值,那么就需要在捕捉列表中引用这两个变量:

int main()
{
	int a = 0, b = 1;
	auto swap = [&a, &b]()//这里是引用的方式捕捉,不是取地址
	{
		int tmp = a;
		a = b;
		b = tmp;
	};
	swap();
	std::cout << a << " " << b << std::endl;
	return 0;
}

        上面的lambda表达式的捕捉列表中的&a和&b其实是a和b的引用,不要理解成取地址。因为捕捉只有两种方式:传值捕捉和传引用捕捉静态变量和全局变量无法捕捉,但可以在lambda表达式中直接使用并自带引用属性

[var]

传值捕捉

[&var]

传引用捕捉

[=]

表示值传递方式捕获上文已出现的变量(包括this)

[&]

表示引用传递捕捉上文已出现的变量(包括this)

[this]

表示值传递方式捕捉当前的this指针

[var1,&var2]、[=,&var]等

混合捕捉

无法重复捕捉,如:[=,var]

捕捉列表不允许变量重复传递,否则就会导致编译错误。

lambda表达式之间不能互相赋值

/

3、lambda的底层就是仿函数

【C++11】C++11常用特性详解

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

六、容器中新增的emplace系列

1、可变参数模板

        C++11的新增特性可变参数模板能够让使用者创建可以接受可变参数的函数模板和类模板。

【C++11】C++11常用特性详解

参数包的解析

方式一:

【C++11】C++11常用特性详解

方式二:这种方式不能解析0个参数的参数包

template <class T>
int PrintArgs(T t)
{
	std::cout << t << " ";
	return 0;
}
template <class ...Args>
void ShowList(Args... args)
{
	//int arr[] = { (PrintArgs(args),0)... };//逗号表达式
    //参数包有几个值,数组就开多大,函数就被调用几次,函数调用时依次展开参数包中内容
	int arr[] = { PrintArgs(args)... };
	std::cout << std::endl;
}
int main()
{
	//ShowList();
	ShowList(1);
	ShowList(1,std::string("xxxxx"));
	return 0;
}

2、emplace系列的高效性 

int main()
{
	std::list<int> list1;
	list1.push_back(1);
	list1.emplace_back(2);
	list1.emplace_back();//会插入0
    
	std::list< std::pair<int, char> > mylist;
	mylist.push_back(make_pair(1, 'a'));  // 构造+拷贝构造(如果是右值就是构造+移动构造)
	//mylist.push_back(1, 'a');//push_back不允许传两个参数
    mylist.push_back({ 40, "sort" });//列表初始化
	mylist.emplace_back(1, 'a'); // empalce支持多参数,直接构造
	return 0;
}
int main()
{
	pair<int, std::string> kv(20, "sort");
	std::list< std::pair<int, std::string> > mylist;

	mylist.push_back(kv); // 左值
	mylist.push_back(make_pair(30, "sort")); // 右值
	mylist.push_back({ 40, "sort" }); // 右值

    mylist.emplace_back(kv); // 左值
	mylist.emplace_back(make_pair(20, "sort")); // 右值(简化为一次构造)
	mylist.emplace_back(10, "sort"); // 构造pair参数包(简化为一次构造)

	return 0;
}

        无论是push_back还是emplace_back,尽量不要使用左值进行插入操作,插入左值必发生拷贝构造。

  1. push_back方法:

对于非深拷贝类型,push_back会调用构造+拷贝构造,如果元素是深拷贝的对象,就是构造+移动构造。

  1. emplace_back方法:

        emplace_back方法会在容器尾部直接构造一个新元素,它的参数是元素的构造函数参数列表。emplace系列能将参数包展开,将过程简化为一次构造。

        所以从效率上来说,emplace系列会高效一点.如果一个深拷贝的类没有实现移动构造,这个时候push_back的效率将远不如emplace_back。

七、包装器

1、function包装器

1.1包装器的意义

        function包装器,也叫作适配器。C++中的function本质是一个类模板,也是一个包装器。

function的作用是对各种可调用对象进行类型统一

【C++11】C++11常用特性详解

函数模板useF被实例化了三份,使用包装器可以解决这一问题。

【C++11】C++11常用特性详解

只要返回值和参数一样就可以用function进行统一封装。

1.2包装器的使用

【C++11】C++11常用特性详解

int f(int a, int b)
{
	return a + b;
}
struct Functor
{
public:
	int operator() (int a, int b)
	{
		return a + b;
	}
};
class Plus
{
public:
	static int plusi(int a, int b)
	{
		return a + b;
	}
	double plusd(double a, double b)
	{
		return a + b;
	}
};
int main()
{
	// 函数名(函数指针)的三种写法
	function<int(int, int)> func1 = f;
	cout << func1(1, 2) << endl;

	function<int(int, int)> func2;
	func2 = f;
	cout << func2(1, 2) << endl;

	function<int(int, int)> func3(f);
	cout << func3(1, 2) << endl;
	// 函数对象
	function<int(int, int)> func4 = Functor();
	cout << func4(1, 2) << endl;
	// lamber表达式
	function<int(int, int)> func5 = [](const int a, const int b)
	{return a + b; };
	cout << func5(1, 2) << endl;

	// 类的成员函数,包装器的静态成员函数可以不加&取地址,普通函数需要加&取地址,建议一律加上
	function<int(int, int)> func6 = &Plus::plusi;//类静态成员函数指针,&可加可不加
	cout << func6(1, 2) << endl;
	function<double(Plus, double, double)> func7 = &Plus::plusd;//类成员函数指针,&必须加,并且需要传类名
	cout << func7(Plus(), 1.1, 2.2) << endl;//调用的时候得传对象,不是传&Plus的地址
	//function包装带捕获的lambda表达式
	Plus plus;
	function<double(double, double)> func8 = [&plus](double x, double y)->double {return plus.plusd(x, y); };
	cout << func8(1.1, 2.2) << endl;
	return 0;
}

1.3包装器求解力扣150题《逆波兰表达式求值》

【C++11】C++11常用特性详解

class Solution {
public:
    int evalRPN(vector<string>& tokens) {
        stack<int> st;
        map<string,function<int(int,int)>> opFuncMap
        {
            {"+",[](int x,int y)->int{return x+y;}},
            {"-",[](int x,int y)->int{return x-y;}},
            {"*",[](int x,int y)->int{return x*y;}},
            {"/",[](int x,int y)->int{return x/y;}}
        };
        for(auto& str:tokens)
        {
            if(opFuncMap.count(str)==0)//说明是数字
            {
                st.push(stoi(str));
            }
            else//说明是运算符
            {
                int right=st.top();
                st.pop();
                int left=st.top();
                st.pop();
                //执行算术操作,算完后将数据重新push进栈
                st.push(opFuncMap[str](left,right));
            }
        }
        return st.top();
    }
};

2、bind

        std::bind函数定义在头文件中,是一个函数模板,它就像一个函数包装器(适配器),接受一个可调用对象,生成一个新的可调用对象来“适应”原对象的参数列表。可以用于固定绑定参数调整参数的顺序文章来源地址https://www.toymoban.com/news/detail-416482.html

// 原型如下:
template <class Fn, class... Args>
/* unspecified */ bind (Fn&& fn, Args&&... args);
// with return type (2) 
template <class Ret, class Fn, class... Args>
/* unspecified */ bind (Fn&& fn, Args&&... args);
int Plus(int a, int b)
{
	return a + b;
}
int SubFunc(int a, int b)
{
	return a - b;
}
class Sub
{
public:
	int sub(int a, int b)
	{
		return a - b;
	}
};
int main()
{
	//表示绑定函数Plus 参数分别由调用 func1 的第一,二个参数指定
	::function<int(int, int)> func1 = ::bind(Plus, placeholders::_1,placeholders::_2);
	cout << func1(1, 2) << endl;
	function<int(int, int)> func2 = bind(SubFunc, placeholders::_1, placeholders::_2);
	cout << func2(1, 2) << endl;
	//调整参数的顺序
	function<int(int, int)> func3 = bind(SubFunc, placeholders::_2, placeholders::_1);
	cout << func3(1, 2) << endl;
	function<bool(int, int)> gt = bind( less<int>(), placeholders::_2, placeholders::_1);//降序
	cout << gt(1, 2) << endl;
	//固定绑定参数
	function<int(Sub, int, int)> func4 = &Sub::sub;
	cout << func4(Sub(),1, 2) << endl;//这样写总是要传入一个类匿名对象进行打印
	function<int(int, int)> func5 = bind(&Sub::sub, Sub(), placeholders::_1, placeholders::_2);//固定sub()
	cout << func5(1, 2) << endl; 
	return 0;
}

到了这里,关于【C++11】C++11常用特性详解的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 阿里云产品试用系列-云服务器 ECS

    阿里云为个人开发者或者企业用户提供云产品的免费试用,本文主要描述ECS云服务器试用体验。 如上所示,在阿里云官方网站中,可使用云服务器ECS构建应用服务。 如上所示,在阿里云控制台中,可以显示成功定制免费使用阿里云ECS云服务器。 如上所示,使用阿里云提供登

    2024年02月08日
    浏览(55)
  • 建站为什么需要服务器?(Web服务器与计算机对比)

    ​ 在部署网站时,底层基础设施在确保最佳性能、可靠性和可扩展性方面发挥着至关重要的作用。虽然大多数人都熟悉个人计算机 (PC) 作为日常工作和个人任务的设备,但 PC 和 Web 服务器之间存在显著差异。在这篇文章中,我们将讨论这些差异是什么,并解释为什么服务器对

    2024年01月20日
    浏览(69)
  • 用服务器搭建网站需要做什么

    网站建设是一个广义的术语,涵盖了许多不同的技能和学科中所使用的生产和维护的网站。不同领域的网页设计,网页图形设计,界面设计,创作,其中包括标准化的代码和专有软件,用户体验设计和搜索引擎优化。许多人常常会分为若干个工作小组,负责网站不同方面的设

    2024年02月13日
    浏览(49)
  • 搭建web服务器需要哪些步骤?

       首先跟大家简单普及一下什么是web服务器? Web服务器也称为WWW(WORLD WIDE WEB)服务器 , 一般指网站服务器,是指驻留于因特网上某种类型计算机的程序 。 WEB服务器主要功能是提供网上信息浏览服务 , 可以处理浏览器等Web客户端的请求并返回相应响应,也可以放置网站文件

    2024年02月06日
    浏览(37)
  • 阿里云国际服务器ECS特性与优势

    阿里云国际服务器 的 ECS 作为一种安全、可靠、灵活、可扩展的云计算服务,不仅可以减少运行和维护,而且可以提高运行和维护效率,使用户关注核心业务的创新。 阿里云国际服务器ECS优势一:产品丰富 阿里云国际云服务器 ECS 可以提供业界丰富的产品序列,且支持主流计

    2024年02月01日
    浏览(47)
  • 阿里云服务器ECS有哪些功能特性?

    本文介绍云服务器ECS的功能特性,帮助您更好地了解和使用云服务器ECS。 实例是云上的虚拟计算服务器,内含vCPU、内存、操作系统、网络、磁盘等基础组件。您可以使用阿里云提供的控制台、API等管理工具创建和管理ECS实例,像使用本地服务器一样管理ECS实例的状态、应用

    2024年02月06日
    浏览(43)
  • 搭建gitlab服务器需要哪些端口号

    GitLab是一个开源的版本控制系统,类似于GitHub,但可以在私有服务器上搭建。搭建GitLab服务器的过程中需要配置一些端口号,以便GitLab的各种服务能够正常运行。本文将向您介绍搭建GitLab服务器所需要的端口号。 HTTP/S端口:80/443 GitLab通过HTTP/S协议进行通信,因此需要开放H

    2024年02月07日
    浏览(40)
  • 高防服务器与云防产品都适用哪些情况

    高防服务器与云防护产品(如高防IP,高防CDN)都可以对DDOS、CC等攻击进行防护,在现如今的互联网市场上,不法分子经常会通过DDOS、CC等攻击服务器,干扰业务正常运行,以此来获得利益。 高防服务器是以机房防火墙设备,以对应的流量进行硬抗。通过DDOS硬件防火墙对异常

    2024年02月10日
    浏览(41)
  • 美国大带宽服务器租用需要考虑的因素

    很多用户会选择租用服务器,那么美国大带宽服务器租用时应该考虑哪些因素呢?小编为您整理发布美国大带宽服务器租用考虑因素。 在美国租用大带宽服务器时,您可以考虑以下几个因素: 1. **性能配置**:选择服务器时,应确保其具有高性能的配置,如100% Intel内核和固态

    2024年01月24日
    浏览(44)
  • 阿里云服务器需要安装杀毒软件吗?

      阿里云服务器需要安装杀毒软件吗?   阿里云服务器本身自带5G的防御,能够有效防御病毒的。 目前的云服务器机房都有专业的人员定制对设备进行检测,因此在硬件的防护方面是有一定的保证的。但是在其他方面就不一定了,因为病毒的传播方式有很多,除了黑客入侵等

    2024年02月11日
    浏览(46)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包