在C++11中,初始化列表的使用范围被大大增强了,但是一些模糊的概念也随之而来,在前面的例子中可以得知,初始化列表可以用于自定义类型的初始化,但是对于一个自定义类型来说,初始化列表可能有两种执行结果。聚合体类型:-普通数组本身可以看作是一个聚合体类型-满足以下条件的class,struct,union可以被看作是一个聚合体类型-无用户自定义的构造函数-无私有或保护的非静态数据成员-无基类-无虚函数-类中不能有使用{}和=直接初始化的非静态数据成员(从c++14开始就支持了)。structT1{intx;longy;protected:intz;}t{1,,2};//error,类中有私有成员,无法使用初始化列表初始化structT2{intx;longy;protected:staticintz;}t{1,,2};//error静态成员不能通过初始化列表初始化31.使用初始化列表初始化非聚合类型的对象
#includeiostream#includestringusingnamespacestd;classPerson{public://有自定义的构造函数Person(intnum):n_num(num){};Person(intnum,stringname):n_num(num),m_name(name){};private://有私有或者受保护的非静态数据成员这两个条件之一都能体现出这个类是一个非聚合类型intn_num;stringm_name;};PersongetPerson(){return{,"starzjx"};//注意必须提供对应的构造函数}intmain(void){return0;}32.initialzer_list模板类的使用
在C++的STL容器中,可以进行任意长度的数据的初始化,使用初始化列表页只能进行固定参数的初始化,如果想要做到和STL一样有任意长度的能力,可以使用std::initializer_list这个轻量级的类模板来使用std::initializer_list的特点:-它是一个轻量级的容器类型,内部定义了迭代器iterator等容器必须的概念,遍历的时候得到的迭代器是只读的-对于std::initializer_listT而言,它可以接收任意长度的初始化列表,但是要求元素必须是同种类型T-在std::initializer_list内部有三个成员接口:size()begin()end()-std::initializer_list对象只能被整体初始化或者赋值注意:相同类型#includeiostream#includestringusingnamespacestd;voidfunc(initializer_listintls){for(autoit=ls.begin();it!=ls.end();it++)cout*it"";coutendl;}intmain(void){func({1,2,3,4,5,6,7,8,9});return0;}33.可调用对象
在C++中存在可调用对象这么一个概念,准确来说,可调用对象有如下几种定义:-是一个函数指针-是一个具有operator()成员函数的类对象(仿函数)-是一个可被转换为函数指针的类对象-是一个类成员函数指针或者类成员指针简单理解:可以按照函数的方式进行调用就是一个可调用对象是一个函数指针#includeiostream#includestringusingnamespacestd;intprint(inta,doubleb){coutabendl;return0;}typedefint(*func)(int,double);intmain(void){funcf=print;f(1,2.3);return0;}是一个具有operator()成员函数的类对象(仿函数)#includeiostream#includestringusingnamespacestd;structTest{voidoperator()(stringmsg){coutmsgendl;}};intmain(void){Testt;t("starzjx");return0;}是一个可被转换为函数指针的类对象#includeiostream#includestringusingnamespacestd;usingfunc_ptr=void(*)(int,string);structTest{staticvoidprint(inta,stringb){coutabendl;}//将函数对象转为函数指针operatorfunc_ptr(){//operator需要转化的类型returnprint;}};intmain(void){Testt;t(1,"starzjx");return0;}是一个类成员函数指针或者类成员指针#includeiostream#includestringusingnamespacestd;structTest{voidprint(inta,stringb){coutabendl;}intn_num;};typedefvoid(Test::*func_ptr)(int,string);//usingfptr=void(Test::*)(int,string);func_ptrf=Test::print;intmain(void){Testt;(t.*f)(1,"starjzx");return0;}34.可调用对象包装器1
std::function是可调用对象的包装器。它是一个类模板,可以容纳除了类成员(函数)指针之外的所有可调用对象。通过指定它的模板参数,它可以用统一的方式处理函数、函数对象、函数指针,并允许保存和延迟执行它们。std::function必须要包含一个叫做functional的头文件,可调用对象包装器使用语法如下:#includefunctionalstd::function返回值类型(参数类型列表)diy_name=可调用对象;我们前面说了四种可调用对象:-普通函数-仿函数-可被转换为函数指针的类对象-成员函数其中成员函数不能直接使用function来进行统一处理,还需要通过后面要说的bind结合起来,才可以。#includeiostream#includestring#includefunctionalusingnamespacestd;//普通函数intadd(inta,intb){couta"+"b"="a+bendl;returna+b;}usingfunc_ptr=int(*)(int,int);classT1{public:staticintsub(inta,intb){couta"-"b"="a-bendl;returna-b;}//可被转换为函数指针的类对象operatorfunc_ptr(){returnsub;}};classT2{public://仿函数intoperator()(inta,intb){couta"*"b"="a*bendl;returna*b;}};intmain(void){//1.包装普通函数functionint(int,int)f1=add;f1(1,2);//2.包装类的静态函数转换为函数指针的对象functionint(int,int)f2=T1::sub;T1t1;functionint(int,int)f3=t1;f2(1,2);f3(1,2);//3.包装仿函数T2t2;functionint(int,int)f4=t2;f4(1,2);return0;}35.可调用对象包装器2
#includeiostream#includestring#includefunctionalusingnamespacestd;classA{public:A(constfunctionvoid()f):callback(f){}voidnotify(){callback();//函数回调调用可调用对象}private:functionvoid()callback;//其实就相当于一个可调用对象,可以堪称是一个函数指针};classB{public:voidoperator()(){cout"我是要成为海贼王的男人!!!"endl;}};intmain(void){Bb;Aa(b);//仿函数通过包装器对象进行包装a.notify();return0;}36.可调用对象绑定器
std::bind用来将可调用对象与其参数一起进行绑定。绑定后的结果可以使用std::function进行保存,并延迟调用到任何我们需要的时候。通俗来讲,它主要有两大作用:将可调用对象与其参数一起绑定成一个仿函数。将多元(参数个数为n,n1)可调用对象转换为一元或者(n-1)元可调用对象,即只绑定部分参数。//绑定非类成员函数/变量autof=std::bind(可调用对象地址,绑定的参数/占位符);//绑定类成员函/变量autof=std::bind(类函数/成员地址,类实例对象地址,绑定的参数/占位符);placeholders::_1是一个占位符,代表这个位置将在函数调用时被传入的第一个参数所替代。同样还有其他的占位符placeholders::_2、placeholders::_3、placeholders::_4、placeholders::_5等……#includeiostream#includestring#includefunctionalusingnamespacestd;voidoutput(intx,inty){coutxyendl;}intmain(void){bind(output,1,2)();bind(output,placeholders::_1,2)(10);bind(output,2,placeholders::_1)(10);//10传递过来是不起作用的,因为咱们绑定的时候指定了固定的值那么就用绑定的值传递的无效bind(output,2,placeholders::_2)(10,20);//第二个参数传递给x第一个参数传递给ybind(output,placeholders::_2,placeholders::_1)(10,20);//第一个参数传递给x第二个参数传递给ybind(output,placeholders::_1,placeholders::_2)(10,20);return0;}37.可调用对象绑定器的使用1
#includeiostream#includestring#includefunctionalusingnamespacestd;voidtestFunc(intx,inty,constfunctionvoid(int,int)f){if(x%2==0){f(x,y);//所以这里就会调用f绑定好的参数了}}voidoutput_add(intx,inty){cout"x:"x",y:"y",x+y"(x+y)endl;}intmain(void){for(inti=0;i10;i++){//得到一个可调用对象autof=bind(output_add,i+,i+);//然后打印的其实是i+i+//我们前面说了在bind的时候设置了固定参数之后,再传递参数是无效的testFunc(i,i,f);//f可以看作是一个仿函数}return0;}38.可调用对象绑定器的使用2
#includeiostream#includestring#includefunctionalusingnamespacestd;classTest{public:voidoutput(intx,inty){cout"x:"x",y:"yendl;}intm_number=;};intmain(void){Testt;//指定函数地址属于那个对象参数autof=bind(Test::output,t,placeholders::_1,placeholders::_2);f(1,2);functionvoid(int,int)f22=bind(Test::output,t,placeholders::_1,placeholders::_2);//绑定成员变量不用指定参数autofnumber=bind(Test::m_number,t);coutfnumber()endl;//因为fnumber调用之后是会打印int所以返回值是intfunctionint(void)f=bind(Test::m_number,t);return0;}39.右值引用
C++11增加了一个新的类型,叫做右值引用,标记为,在介绍右值引用类型之前要先了解什么是左值,什么是右值:-lvalue是loactorvalue的缩写,rvalue是readvalue的缩写-左值是指存储在内存中、有明确存储地址(可取地址)的数据;-右值是指可以提供数据值的数据(不可取地址);#includeiostream#includestring#includefunctionalusingnamespacestd;intmain(void){//左值intnum=9;//左值引用即num的别名inta=num;//右值右值就是没有地址的常量//右值引用右值引用只能使用右值去初始化而不能使用左值intb=8;//常量左值引用constintc=num;//常量右值引用constintd=6;//常量的右值引用也必须使用右值进行初始化//右值只能使用右值去初始化constinte=b;//errorintf=b;//errorreturn0;}40.右值引用的作用以及使用
右值引用就是对一个右值进行引用的类型。因为右值是匿名的,所以我们只能通过引用的方式找到它。无论声明左值引用还是右值引用都必须立即进行初始化,因为引用类型本身并不拥有所绑定对象的内存,只是该对象的一个别名。通过右值引用的声明,该右值又“重获新生”,其生命周期与右值引用类型变量的生命周期一样,只要该变量还活着,该右值临时量将会一直存活下去。在C++中在进行对象赋值操作的时候,很多情况下会发生对象之间的深拷贝,如果堆内存很大,这个拷贝的代价也就非常大,在某些情况下,如果想要避免对象的深拷贝,就可以使用右值引用进行性能的优化。#includeiostream#includestring#includefunctionalusingnamespacestd;classTest{public:Test():m_num(newint()){cout"construct:mynameisstarzjx"endl;printf("m_num