重装上阵 、 类型推导 、 模板改进、 初始列表 、 范围循环 、 函数绑定、匿名函数 、 泛型元组 、 右值引用
C++11 基础
一,重装上阵
1.过度
1).2011年11月,美国印第安纳州卢明顿市,八月印第安纳大学会议,C++0x草案正式获得通过,被命名C++11,ISO C++标准委员会(WG21)
2).2003年,WG21退出针对C++98的技术勘误表,即C++03,合称C++98/ 03
3).相比C++98 / 03,C++11增加了140个语法特性,修改了600个缺陷
2.标准编年史
1)1990年,The Anotated C++ Reference Manual
2)1998年,IOS/IEC 15882:1998,C++98
3)2003年, IOS/IEC 15882:2003,C++03
4)2005年, IOS/IEC TR19768:2005,扩充C++标准库.
5)2007年,SC22表决通过了下一个标准中的核心特性
6)2008年,增加了70个语言特性,83个库,300个缺陷修订
7)20 10年,发布新标准的试用版,更广泛的征求意见
8)2011年,正式发布C++2011标准---ISO/IEC 15882:2011
9)2012年,ANSI和ISO网店出售C++11标准
3.C++11的设计目标
1)比C语言更加适合系统编程
2)支持数据抽象;
3)支持面向对象
4)支持泛型编程
5)更加适合库的开发
6)比其他语言更加易学易用
7)保持足够的稳定性 ,完全向下兼容,完全兼容C语言
9)扩充标准库,满足不同领域开发的需求
4.今日之C++
1)如今C++依旧位列通用编程语言的前三;
2)C++无处不在.
二,类型推导
1.auto
int main(void){
autoint I =0;
}
auto -- 栈变量,自动变量,缺省
static -- 数据区 / BSS区 ,静态变量
register -- 寄存器变量
在C++11里,auto关键字被作为类型自动类型推导关键字
1. 基本用法
C++98:类型 变量名=初值;
int i =10;
C++11:auto 变量名=初值;
auto i=10;//i:int
autoi=3.14; //i:double
借助于auto关键字,可对变量进行隐式的类型定义,即有编译器在编译期间根据变量的初始化语句,自动推断出该变量的类型
参见:auto.cpp
#include<iostream>
#include<typeinfo>
using namespacestd;
int main(void){
//a就是int类型(const限定被丢弃)
auto a=1;
cout << typeid(a).name() <<endl;//i
cout << ++a << endl;//2
//b是int*类型
auto b=new auto(2);
cout << typeid(b).name() <<endl;//pi
//c是int const*leixing
//d是int const类型
auto const* c=&a,d=3;
cout << typeid(c).name() <<endl;//PKi
cout << typeid(d).name() <<endl;//i
/*-d带有常属性,不能修改
cout << ++d << endl;
*/
/* 不给初直,不能推导
auto const* e=&a,f;
*/
/*初直不一样,也不能能推导
auto e=a,f=1.0;
*/
//在旧语法中,auto型的变量存储区栈区,static型存储区静态区,不能同时用
//C++11的auto关键字不在作为存储类型指示符
static auto i=4.;
cout << typeid(i).name() <<endl;//d
/*C++auto关键字不再作为存储类型指示符
auto int j;
*/
/auto k;
return 0;
}
2)auto同指针或引用结合作用
A 即使不把auto声明为指针,其也可以被推导为指针类型
int a=0;
auto* b=&a;// auto=int b:int*
autoc=&a; // auto =int* c:int*
B,当表达式带有引用属性时,auto会抛弃其引用属性
auto&d=a; //auto=int, d:int&
auto e=d; //auto=int, e:int 不接受d的引用属性
C,当表达式带有CV限定,auto会抛弃其CV限定
auto const f =a; //auto = int , f: int const
auto g = f; ..auto = int, g: int 不接受const属性
但 如果auto和引用或指针结合使用,表达式的CV限定就会被保留下来
auto const&h = a; //auto = int ,h: intconst&
auto& i =h // auto = int const, i: intconst& 常引用可以接受
auto* j =&h; //auto = int const , j: intconst* 常指针可以接受
参见:auto2.cpp
3)auto使用的限制
A auto不能用于函数的参数
B auto 不能用于类的非静态成员函数
C auto 不能用于模板的类型实参
A<int> d1;
A<auto> d2=d1; // 不行
D auto不能用数组元素
int a[10]’;
auto b[10]=a; //不行
参见:auto3.cpp
#include<iostream>
#include<typeinfo>
using namespace std;
/*不能用于函数的参数
void foo(auto a=1){
cout <<typeid(a).name() << endl;
}
*/
class A{
public:
int m_x=0;
//auto m_y=1;
//static autom_z=2;
static autoconst m_t=3;
};
template<typename T>
class B{
public:
B(T const&arg):m_var(arg){}
T m_var;
};
int main(void){
//foo(1);
B<int>b1(10);
//B<auto>b2=b1;不能用于模板的实参
int arr1[10];
//autoarr2[10]=arr1;不能用于数组元素
autoarr3=arr1;//代表手地址
cout <<typeid(arr3).name() << endl;//pi
//auto=int[10],arr4:int(&)[10],在这arr代表的是数组的整体
auto&arr4=arr1;
cout <<typeid(arr4).name() << endl;//A10_i
return 0;
}
4)何时使用auto
A通过atuo可以减少模板的类型参数
B通过auto可以简化某些复杂类型的书写
参见:auto4.cpp
#include<iostream>
#include<map>
using namespacestd;
class A{
public:
A(int arg=0):m_var(arg){}
int get(void)const{
return m_var;
}
void set(int arg){
m_var=arg;
}
private:
int m_var;
};
class B{
public:
B(char const*arg=""):m_var(arg){}
char const* get(void)const{
return m_var;
}
private:
char const* m_var;
};
template<typenameV,typename X>
void foo(Xconst& x){
V var=x.get();
cout << var << endl;
//使用var..
}
template<typenameX>
void bar(Xconst& x){
auto var=x.get();
cout << var << endl;
//使用var..
}
int main(void){
A a(1234);
B b("abcd");
foo<int> (a);
foo<char const*> (b);
bar(a);
bar(b);
multimap<string,int> mm;
mm.insert(make_pair("张飞",70));
mm.insert(make_pair("赵云",75));
mm.insert(make_pair("关羽",80));
mm.insert(make_pair("张飞",85));
mm.insert(make_pair("赵云",90));
mm.insert(make_pair("关羽",95));
pair<multimap<string,int>::iterator,multimap<string,int>::iterator>its
=mm.equal_range("张飞");
int sum1=0;
for(multimap<string,int>::iteratorit=its.first;it!=its.second;++it)
sum1+=it->second;
cout << sum1 << endl;//155
//通过auto简化复杂类型的书写
auto its2=mm.equal_range("张飞");
int sum2=0;
for(autoit=its2.first;it!=its2.second;++it)
sum2+=it->second;
cout << sum2 << endl;
return 0;
}
2. decltype
1) 计算一个表达式的类型
decltype(表达式)
指示编译器在编译阶段自动推导出一个表达式的类型
int a = 1;
int b = a;
decltype(a) b =a;//先计算出a在定义 b
参见代码.decltype1.cpp
#include<iostream>
#include<typeinfo>
using namespace std;
int main(void){
int a=0;
//b:int,decltype的值就是表达式的类型本身
decltype(a) b=1;
cout <<typeid(b).name() << endl;//i
//c:int,decltype只是在编译期间计算表达式的类型,
//布杂u运行期间计算表达式的值
decltype(a++) c=2;
cout <<typeid(c).name() << endl;//i
cout << a <<endl;//0
int const& d=3;
//e:int const&,decltype会保留表达式的引用属性和CV限定
decltype(d) e=d;
cout << &e<< ' ' << &d << endl;
//f:int*,g:int**,decltype可以和指针连用
decltype(a)*f=&a,**g=&f;
cout <<typeid(f).name() << endl;
cout <<typeid(g).name() << endl;
cout << a << '' <<*f<< ' ' << **g << endl;
//h:intconst&,decltype可以和引用以及CV限定联用
decltype(a) const&h=a;
cout << &h<< ' ' << &a << endl;
return 0;
}
2) 类型推导规则
规则1:对标识符及成员标识符表达式,直接取表达式的类型
规则2:对函数调用表达式,取该函数返回值的类型
规则3:对其他表达式,若表达式的类型为左值则取该类型的左值引用,否则取表达式的类型
参见代码,decltype2.cpp
#include<iostream>
using namespacestd;
class Obj{
public:
Obj(int arg):m_var(arg){}
int m_var;
static int const s_var=0;
};
//规则1:对标识符以及成员标识符,直接取其类型
voidrule1(void){
int a=0;
int const volatile& b=a;
//c:int
decltype(a) c=a;
//d:int constvolatile&
decltype(b) d=b;
Obj e=1;
//f:int
decltype(e.m_var) f=2;
//g:int const
decltype(Obj::s_var) g=Obj::s_var;
}
//规则2:对函数调用表达式,取该函数返回值的类型
voidrule2(void){
int& lvr(void);//返回值引用
int&& rvr(void);//返回右值引用
int prv(void);//返回纯右值
Obj obj(void);//返回类对象
int const& clvr(void);//返回常左值引用
int const&& crvr(void);//返回常右值引用
int const cprv(void);//返回常纯右值
Obj const cobj(void);//返回常类对象
int i=0;
decltype(lvr()) a=i;//a:int&
decltype(rvr()) b=1;//b:int&&
decltype(prv()) c=2;//c:int
decltype(obj()) d=3;//d:Obj
int j=4;
decltype(clvr()) e=j;//e:int const&
decltype(crvr()) f=5;//f:int const&&
decltype(cprv()) g=6;//g:int
decltype(cobj()) h=7;//h:Obj
}
//规则3:对其它表达式,若表达式的类型为左值,则取该类型的左值引用,否则
//取表达式的类型
voidrule3(void){
int a=0;
//b:int,根据规则1
decltype(a) b=1;
cout << &b << ' ' <<&a << endl;
//c:int&,a是左值,因此括号表达式(a)也是左值,根据规则3,c的类型为
//左值引用
decltype((a)) c=a;
cout << &c << ' ' <<&a << endl;
cout << (c=1) << endl;
Obj const d=2;
//e:int,根据规则1
decltype(d.m_var) e=3;
//f:int const&,常对象的成员都是常左值,由规则3,f的类型为常左值引用
decltype((d.m_var)) f=d.m_var;
cout << &f << ' ' << &d.m_var << endl;
int g=4,h=5;
++g=10;
cout << g << endl;
decltype(++g) i=g;//i:int&
cout << &i <<' '<<&g << endl;
decltype(g++) j=g;//j:int
cout << &j <<' ' <<&g << endl;
decltype(g=h) k=g;//k:int&
decltype(g+h) l=g;//l:int
}
int main(void){
rule1();
rule2();
rule3();
return 0;
}
3) 何时使用decltype
A解决泛型编程中的类型无关性问题
B在冗长代码中精确确定某个前面定义的变量的类型,便于修改和维护
参见:decltype3.cpp
#include<iostream>
#include<vector>
#include<map>
using namespacestd;
//打印可写容器的元素
template<typenameC>
voidprintw(C& c){
for(typename C::iterator it=c.begin();
it!=c.end();it++)
cout << *it << ' ';
cout << endl;
}
//打印只读容器中的元素
template<typenameC>
void printr(Cconst& c){
for(typename C::const_iteratorit=c.begin();
it!=c.end();it++)
cout << *it << ' ';
cout << endl;
}
//打印任意容器中的元素
template<typenameC>
voidprinta(C& c){
for(decltype(c.begin()) it=c.begin();it!=c.end();it++)
cout << *it << ' ';
cout << endl;
}
int main(void){
int ai[]={10,20,30,40,50};
vector<int> v1(ai,ai+5);//可写容器
vector<int>const v2=v1;//只读容器
printw(v1);
printr(v2);
printa(v1);
printa(v2);
return 0;
}
4) 返回类型后置
在泛型编程中,往往根据函数参数的运算结果或重载匹配的版本来确定其返回值的类型,这时可以采用返回类型后置语法予以解决:
auto函数名(形参表)->decltype(表达式){函数体}
其中,”表达式”即体现了该函数返回值的类型
参见:decltype4.cpp
#include<iostream>
#include<typeinfo>
using namespace std;
template<typename X,typename Y>
auto add(X const& x,Y const&y)->decltype(x+y){
return x+y;
}
double foo(int arg){
return arg/2.0;
}
int foo(double arg){
return int(arg*2);
}
template<typename T>
auto bar(T const&arg)->decltype(foo(arg)){
return foo(arg);
}
int main(void){
auto a=add(1,0.5);
cout << typeid(a).name() << ' ' << a << endl;
auto b=add(1,'A');
cout << typeid(b).name() << ' ' << b << endl;
auto c=bar(1);
cout << typeid(c).name() << ' ' << c << endl;
auto d=bar(0.5);
cout << typeid(d).name() << ' ' << d << endl;
return 0;
day01$ a.out
d 1.5
i 66
d 0.5
i 1
}三,模板改进
1.using 起别名
旧语法:typedefunsigned int uint_t;
原始名 别名
新语法:usinguint_t = unsigned int;
别名 原始名
相对于typedef,using关键字除了为具体类型定义别名以外,还可以为模板定义别名
template<typename A,typename B> classX{};
typedef X x_t; //error
typedef X<int,double> xid_t; //ok
typedef basic_string<…,char,…>string;
template<typename A,typename B>
using x_t=X<A,B>; //OK
template<typename B>using xs_t =X<string,B>;
x_t<int,double>…//X<int,double>
xs_t<short> …//X<string,short>
参见:using1.cpp
1#include<iostream>
2#include<typeinfo>
3using namespace std;
4template<typename A,typename B> class X{};
5using uint_t=unsigned int;
6 //typedef unsigned int uint_t;
7using xsi_t=X<string,int>;
8 //typedef X<string,int> xsi_t;
9template<typename A,typename B>
10using x_t=X<A,B>;//模板只能使用using起别名
11template<typename B>
12using xs_t=X<string,B>;
13int main(void){
14 cout << typeid(unsigned int).name() <<endl;
15 cout << typeid(X<string,int>).name() <<endl;
16 u_int a;//unsigned int
17 xsi_t b;//X<string,int>
18 x_t<string,int> c;//x<string,int>
19 xs_t<int> d;//X<string,int>
20 cout << typeid(a).name() <<endl;
21 cout << typeid(b).name() <<endl;
22 cout << typeid(c).name() <<endl;
23 cout << typeid(d).name() <<endl;
24 return 0;
25 }
3. 在C++98 / 03标准中,只有类模板可以带有缺省模板参数
在C++11标准中,函数模板也可以带有缺省参数
参见:defparam1.cpp
#include<iostream>
#include<typeinfo>
using namespace std;
//在C++98/03中,只有类模板可以带缺省参数
template<typename A=int,typenameB=double,typename C=string>
class X{
public:
static void foo(void){
cout << typeid(A).name() <<' '<< typeid(B).name()<<' '
<<typeid(C).name() <<endl;
}
};
//在C++11中,函数模板也可以带有缺省参数
template<typename A=int,typenameB=double,typename C=string>
void foo(void){
cout << typeid(A).name() <<' '<< typeid(B).name()<<' '
<<typeid(C).name() <<endl;
}
//只要满足隐士推断的条件,函数模板参数的缺省值不一定非要写在最后
template<typename A=int,typename B/**/,typename C=string>
void bar(B b){//
cout << typeid(A).name() <<' '<< typeid(B).name()<<' '
<<typeid(C).name() <<endl;
}
//无法隐士推断的模板参数取缺省值,否则取隐士推断的类型,隐士生效,缺省忽略
template<typename A=int,typenameB=double/**/,typename C=string>
void hum(B b){//
cout << typeid(A).name() <<' '<< typeid(B).name()<<' '
<<typeid(C).name() <<endl;
}
int main(void){
X<char,short,long>::foo();
X<char,short>::foo();
X<char>::foo;
X<>::foo();
foo<char,short,long>();
foo<char,short>();
foo<char>();
foo<>();
foo();
bar("A");//
hum(1234);//以隐士推断为准
return 0;
}
3.右尖括号
1)在C++98 / 03标准中,连续的两个右尖括号会被编译器理解为右移操作符二非模板参数的结束,因此他们之间至少应该保留一个空格,但是右移操作符仍然会被优先解析
参见:delimiter1.cpp
2)在C++11标准中,要求编译器对模板的右尖括号做单独处理,使之能正确区分右移操作符和模板参数表结束,因此期间无需再留空格,但有时如果不加小括号,”>>”会被错误的解释为模板参数表结束和大于号,导致 编译失败
1#include<iostream>
2#include<typeinfo>
3using namespace std;
4template<typename T>
5void ptype(void){
6 cout <<typeid(T).name() << endl;
7 }
8template<typename T>
9class Dummy{};
10template<int x>
11int square(void){
12 return x*x;
13 }
14int main(void){
15 ptype<Dummy<int> >();
16 int a=square<(6>>1)>();//使用小括号
17 cout << a << endl;
18 return 0;
19 }
四,\初始列表
1.形形色色的初始化语法
参见.initlist.cpp
#include<iostream>
#include<algorithm>
#include<iterator>
using namespace std;
struct Student{
char name[256];
struct Data{
int y;
int m;
int d;
}bday;
};
class Complex{
public:
Complex(double r=0,double i=0):m_r(r),m_i(i){}
Complex(Complex const& c):m_r(c.m_r),m_i(c.m_i){
cout << "构造函数" << &c<< "->" << this<< endl;
}
friend ostream& operator<<(ostream& os,Complex const&c){
return os << c.m_r << '+' << c.m_i << 'i';
}
private:
double m_r,m_i;
};
int main(void){
//赋值形式的初始化
int a=123;
cout << a << endl;
//构造形式的初始化
double b(1.23);
cout << b << endl;
//列表形式的初始化
int c[]={100,200,300};
copy(c,c+3,ostream_iterator<int>(cout," "));
cout << endl;
//列表形式的初始化
Student d={"张飞",{2111,11,11}};
cout << d.name <<','<< d.bday.y <<','<<d.bday.m<<','<<d.bday.d << endl;
//构造形式的初始化
Complex e(1.2,3.4);
cout << e << endl;
//赋值形式
Complex f=Complex(5.6,7.8);
cout << f << endl;
Complex g=e;
cout << g << endl;
return 0;
}
2.风格统一的列表初始化
类型* 指针变量{new 类型{初始化表}}
参见,initlist2.cpp;
#include<iostream>
#include<algorithm>
#include<iterator>
using namespace std;
struct Student{
char name[256];
struct Data{
int y;
int m;
int d;
}bday;
};
class Complex{
public:
Complex(double r=0,double i=0):m_r(r),m_i(i){}
Complex(Complex const& c):m_r(c.m_r),m_i(c.m_i){
cout << "构造函数" << &c<< "->" << this<< endl;
}
Complex const operator+(Complex const& c)const{
return {m_r+c.m_r,m_i+c.m_i};
}
friend ostream& operator<<(ostream& os,Complex const&c){
return os << c.m_r << '+' << c.m_i << 'i';
}
private:
double m_r,m_i;
};
int main(void){
//赋值形式的初始化
int a{123};
cout << a << endl;
//构造形式的初始化
double b{1.23};
cout << b << endl;
//列表形式的初始化
int c[]={100,200,300};
copy(c,c+3,ostream_iterator<int>(cout," "));
cout << endl;
//列表形式的初始化
Student d={"张飞",{2111,11,11}};
cout << d.name <<','<< d.bday.y <<','<<d.bday.m<<','<<d.bday.d << endl;
//构造形式的初始化
Complex e{1.2,3.4};
cout << e << endl;
//赋值形式
Complex f=Complex{5.6,7.8};
cout << f << endl;
cout << e+f << endl;
Complex* g{new Complex {1.2,3.4}};
cout << *g << endl;
delete g;
g=new Complex[3]{{1.1,1.1},{2.2,2.2},{3.3,3.3}};
copy(g,g+3,ostream_iterator<decltype(*g)>(cout," "));
cout << endl;
delete[] g;
cout << Complex{1.2,3.4} << endl;
Complex const(&h)[3]{{1.1,1.1},{2.2,2.2},{3.3,3.3}};
copy(h,h+3,ostream_iterator<decltype(*h)>(cout," "));
cout << endl;
return 0;
}
3. 聚合类型与非聚合类型
1)聚合类型
A.任意类型(聚合类型与非聚合类型)的数组
B满足以下条件的类
a) 无自定义构造函数
b) 无私有或保护的非静态成员变量
c) 无基类
d) 无虚函数
e) 无通过”=”或”{}”在声明的同时初始化的非静态成员变量
C聚合类型的元素可以是聚合类型也可以是非聚合类型
2)非聚合类型:任何不属于聚合类型的类型都是非聚合类型
3)对于聚合类型使用列表初始化,相当于对其中的每个元素按照地址从低到高的顺序逐一初始化,而对于非聚合类型,使用列表初始化,则需要调用匹配的构造函数
参见:initlist3.cpp
#include<iostream>
using namespace std;
//非聚合类型的积累
class Base{
public:
//自定义构造函数
Base(int i):m_i(i){
cout << "Base构造:" << this << endl;
}
//虚函数
virtual ostream& operator>>(ostream& os)const{
return os<<m_i;
}
private:
int m_i;//私有的非静态成员变量
};
//非聚合类型的子类,因为有基类
class Nonagg:public Base{
public:
//自定义构造函数
Nonagg(int i,double d):Base(i),m_d(d){
cout << "Nonagg构造:" << this << endl;
}
//虚函数
ostream& operator>>(ostream& os)const{
Base::operator>>(os)<<' ' << m_d << ' ' <<m_s;
}
//通过“=”在声明的同时被初始化的非静态成员变量
char const* m_s="ABC";
protected:
//保护的非静态成员变量
double m_d;
};
//聚合类型
class Agg{
public:
ostream& operator>>(ostream& os)const{
return m_n >> os << ' ' << m_i << ' ' <<m_d << m_s;
}
Nonagg m_n;//包含了非聚合类型的类的元素没有影响
int m_i;
double m_d;
char const* m_s;
};
int main(void){
Nonagg nonagg{12,3.4};
//Nonagg::Nonagg(&nonagg,12,3.4);
Base& base=nonagg;
base >>cout <<endl;
Agg agg{{12,3.4},56,7.8,"ABC"};
//Nonagg::Nonagg(&agg.m_n,12,3.4);
//agg.m_i=12;
//agg.m_d=3.4;
//agg.m_s="ABC"
agg>> cout << endl;
return 0;
}
4. 变长初始化列表
1) 用于数组和标准容器的初始化列表其长度可以不固定的
in tai[]{1,2,3,4,5,…};
vector<int>vi{1,2,3,4,5,….}
2) 将构造参数设置成initializer_list类型,就可以接受变长初始化列表
参见,initlist4.cpp
initializer_list是一个轻量级的类模板容器,其中仅保存初始化列表元素的引用而非其拷贝.
{初始化列表}- -拷贝à重量级容器- -拷贝à成员变量(vector / list…)
{初始化列表}ß引用--重量级容器- -拷贝à成员变量(initializer_lis)
借助于这样的轻量级容器,可以是自定义类的构造函数,,或者普通函数接受初始化列表形式的参数,传递个数不定但类型相同多个实参
initializer_list只提供三个成员函数:begin / end / size,返回用于获取起始迭代器,终止迭代器以及元素个数
initializer_list只提供只读迭代器,其目标元素不可修改,但对容器可以做整体赋值,令其引用另外一组数据
initializer_list提供缺省构造函数,表示空容器
参见:initlist5.cpp
5. 防止类型收窄
利用列表初始化语法,可以只是编译器针对类型收窄的潜在风险给出必要的警告,但是编译器只是根据类型做判断,为不关心数值是否存在精度或值域的损失
参见initlist6.cpp
五,范围循环
1.for(元素类型变量:容器对象){循环体}
用变量一次结合容器对象中的每一个元素并执行循环体,直到容器对象中的元素都被处理过为止
vi.for1.cpp
#include<iostream>
#include<algorithm>
#include<vector>
using namespace std;
void print(int i){
cout << i <<' ';
}
int main(void){
int ai[]{65,66,67,68,69};
size_t size=sizeof(ai)/sizeof(ai[0]);
vector<int> vi(ai,ai+size);
for(size_t i=0;i<size;++i)
cout << ai[i] << ' ';
cout << endl;
for(size_t i=0;i<size;++i)
cout << vi[i] << ' ';
cout << endl;
for(auto it=ai;it!=ai+size;++it)
cout << *it << ' ';
cout << endl;
for(auto it=vi.begin();it!=vi.end();++it)
cout << *it << ' ';
cout << endl;
//基于泛型算法的for循环
for_each(ai,ai+size,print);
cout << endl;
for_each(vi.begin(),vi.end(),print);
cout << endl;
//基于范围的for循
for(int i:ai)
cout << i << ' ';
cout << endl;
for(int i:vi)
cout << i << ' ';
cout << endl;
//冒号前面的局部变量的类型,只要能够从容器元素的类型做隐士转换
//不要求严格一致
for(char c:ai)
cout << c << ' ';
cout << endl;
for(char i:vi)
cout << i << ' ';
cout << endl;
//可以使用auto关键字,让编译器自动推导出与容器元素一直的类型
for(auto a:ai)
cout << a << ' ';
cout << endl;
for(auto a:vi)
cout << a << ' ';
cout << endl;
//对于修改元素的循环,可以通过引用型变量,结合容器中的每个元素,
//成为该元素的别名
for(auto& a:ai)
++a;
for(auto& a:vi)
++a;
//即使是只读操作的循环,使用引用也可以获得更好的性能,
//特别适合元素是复杂的类类型对象时,常引用还够防止对容器元素的意外修改
for(auto const& a:ai)
cout << a << ' ';
cout << endl;
for(auto const& a:vi)
cout << a << ' ';
cout << endl;
return 0;
}
2.注意事项
参见for2.cpp
#include<iostream>
#include<list>
#include<map>
#include<set>
using namespace std;
list<int>foo(void){
cout <<"aaa"<< endl;
return {10,20,30};
}
int main(void){
//对map和multimap容器使用范围循环,每次拿到的元素既不是键也不是值,
//而是由键和值封装组成的pair
multimap<string,int> mm;
mm.insert(make_pair("张飞",70));
mm.insert(make_pair("赵云",75));
mm.insert(make_pair("关羽",80));
mm.insert(make_pair("张飞",85));
mm.insert(make_pair("赵云",90));
mm.insert(make_pair("关羽",95));
for(auto& a:mm){
if(a.first=="张飞")
//a.first="王菲";在使用基于范围的for循环时,注意容器本身的只读属性约束,不能修改
a.second=100;//它的值可以改
cout << a.first << ": " << a.second <<endl;
}
set<int> si{10,20,30};
//for(int&a:si)
for(auto& a:si)//a:int const&
//++a; error 不能修改
cout << a << ' ';
cout << endl;
//基于范围的for循环,无论循环体执行多少次,冒号后面的表达式只会执行一次
for(auto const& a:foo())
cout << a << ' ';
cout << endl;
list<int> li{100,200,300};
for(auto const& a:li){
cout << a <<' ;
/*基于范围的for循环,其底层实现依然要借助容器的迭代器,
* 因此任何可能导致迭代器失效的结构性改变,都将引发未定义的后果
li.pop_front();*/
}
cout << endl;
return 0;
}
3.一个类值要提供了分别获得起始和终止迭代器的begin和end方法,就可以支持基于范围的for循环
参见for3.cpp
#include<iostream>
using namespace std;
template<typename T,size_t S>
class Array{
public:
T*begin(void){
return m_array;
}
Tconst* begin(void)const{
return m_array;
}
T* end(void){
return m_array+S;
}
Tconst* end(void)const{
return m_array+S;
}
private:
Tm_array[S];
};
int main(void){
Array<int,5> ai;
int i=10;
for(auto& a:ai){ //遍历赋值
a=i;
i+=10;
}
for(auto const& a:ai) //遍历输出
cout << a <<' ';
cout << endl;
return 0;
}
六,函数绑定
1.函数对象
1)能够被函数调用的不一定就是函数,他们还可能是:
A.存放一个函数入口地址的函数指针
B.实现了小括号运算符的类对象(仿函数)
C.可以被隐式转换为函数指针的类对象
2)像函数指针\ 仿函数\ 可被隐式转换为函数指针的类对象,一般统称为可调用对象,而他们的类型则被称为可调用类型
3)C++11通过名为function的类模板包装任意类型的可调用对象,在内部封装以上三种对象,并以函数语法调用.这样的对象就叫做函数对象
参见:function1.cpp
#include<iostream>
#include<functional>//声明function类模板
using namespace std;
//函数
int fun(int x){
cout << __FUNCTION__ << '(' << x <<")->" << flush;
return x;
}
//实现了小括号的一个类
class Foo{
public:
int operator()(int x,int y)const{
cout << __FUNCTION__ << '(' << x << ',' <<y << ")->" << flush;
return x+y;
}
};
//可被转换成函数指针的类
class Bar{
using hum_t=int(*)(int,int,int);
public:
operator hum_t(void)const{
return hum;
}
private:
static int hum(int x,int y,int z){
cout << __FUNCTION__ << '(' << x << ',' <<y << ','
<< z << ")->" << flush;
return x+y+z;
}
};
//用函数对象实现回调
int run(function<int(int)>const&callback,int x){
return callback(x);
}
intrun(function<int(int,int)>const& callback,int x,int y){
return callback(x,y);
}
int run(function<int(int,int,int)>const& callback,int x,int y,int z){
return callback(x,y,z);
}
int main(void){
//1.存放函数入口地址的函数指针
int(*pfun)(int)=fun;
cout << pfun(10) << endl;;
//2.实现了小括号运算符的类对象,亦称防函数
Foo foo;
cout << foo(20,30) << endl;;
//3.可被转换为函数指针的类对象
Bar bar;
cout << bar(40,50,60) << endl;
//像pfun、foo、bar、这样的对象被成为可调用对象
//他们的类型int(*)(int)、Foo、Bar则被称为可调用类型
//function是可调用对象的包装器,
//他是一个类模板,可容纳以上三个任何一个可调用对象,并按照函数语法调用
//这样的对象就叫做函数对象
function<int(int)> ffun=pfun;
cout << ffun(10) << endl;
function<int (int ,int)> ffoo=foo;
cout << ffoo(20,30) << endl;
function<int (int ,int ,int)> fbar=bar;
//用函数对象实现回
cout << fbar(40,50,60) << endl;
cout << run(fun,10) << endl;
cout << run(foo,20,30) << endl;
cout << run(bar,40,50,60) << endl;
return0;
}
2.函数绑定器
1)函数绑定器可将任何可调用对象和需要传递给他们的参数绑定为一个函数对象.该函数对象负责调用被绑定的可调用对象,并依此传入所绑定的参数,同时返回其返回值
function<函数类型> 函数对象=bind(可调用对象,实参表);
function<返回类型(形参表)> 函数对象=bind(可调用对象,实参表);
2)placeholders::_n是一个占位符,其值将有传递给函数对象的第n个参数取代.
参见:bind1.cpp
#include<iostream>
#include<functional>//声明function类模板
using namespace std;
//函数
int fun(int x){
cout << __FUNCTION__ << '(' << x <<")->" << flush;
return x;
}
//实现了小括号的一个类
class Foo{
public:
int operator()(int x,int y)const{
cout << __FUNCTION__ << '(' << x << ',' <<y << ")->" << flush;
return x+y;
}
};
//可被转换成函数指针的类
class Bar{
using hum_t=int(*)(int,int,int);
public:
operator hum_t(void)const{
return hum;
}
private:
static int hum(int x,int y,int z){
cout << __FUNCTION__ << '(' << x << ',' <<y << ','
<< z << ")->" << flush;
return x+y+z;
}
};
int main(void){
cout << fun(10) << endl;
//function<int (void)>f1=bind(fun,10);
auto f1=bind(fun,10);
cout << f1() << endl;//cout << fun(10) << endl;
Foo foo;
//function<int(void)> f2=bind(foo,20,30);
//auto f2=bind(foo,20,30);
auto f2=bind(foo,20,placeholders::_1);
//placeholders::_1 占位符,绑定部分,1代表从下面第一个参数取
cout << f2(30) << endl;//cout<< foo.operator()(20,30) << endl;
cout << bind(Bar(),placeholders::_3,50,placeholders::_2)
(80,60,40,20) << endl;
return 0;
}
3)无论是类的普通成员函数还是静态成员函数都可以通过bind与其参数一起被绑定到函数对象中,但是要注意,如果所绑定的是普通成员函数,作为this指针实参的调用对象地址,一定不要忽略
参见:bind2.cpp
#include<iostream>
#include<functional>
using namespace std;
class A{
public:
//普通成员变量
int foo(/*A const* this*/intarg)const{
return /*this->*/m_var+arg;
}
//静态成员函数
static int bar(int arg){
return s_var+arg;
}
private:
int m_var=10;
static int const s_var=20;
};
int main(){
Aa;
function<int(int)> ffoo=bind(&A::foo,&a/*传this*/,placeholders::_1);
cout << ffoo(1) << endl;
//cout << &A::foo(&a,1) << endl;
//cout << a.foo(1) << endl;
function<int(int)> fbar=bind(A::bar,placeholders::_1);
cout << fbar(1) << endl;
//cout << &A::bar(1) << endl;
return 0;
}
4)老版本的bind1st和bind2nd只能对带有两个参数的函数绑定第一个或第二个参数,其目标就是将二元函数降级为一元函数. 新版本的bind对被绑定函数的参数个数和具体绑定那些参数都不做任何限制,更加灵活\通用且统一.通过bind还可以组合多个函数构成复合闭包
参见:bind3.cpp
#include<iostream>
#include<vector>
#include<algorithm>//旧版本绑定函数
#include<functional>//新版本绑定函数
using namespace std;
bool jige(intscore){
if(score>=60)
return true;
else
return false;
}
int main(void){
vector<int>scores{70,40,80,50,70,80,60,60,90,50};
//count_if函数用来统计一个容器中符合条件个数
cout << "及格人数:"<< count_if(scores.begin(),scores.end(),jige)
<< endl;
//less_equal函数(小于等于)
less_equal<int> le;
cout << boolalpha << le(50,60) << endl;//第一个参数是否小于后面的参数
cout << boolalpha << le(70,60) << endl;
cout << "及格人数:"<< count_if(scores.begin(),scores.end(),
bind1st (le,60)) << endl;//bind1st(绑定第一个参数)
cout << "不及格人数:"<< count_if(scores.begin(),scores.end(),
bind2nd (less<int>(),60)) << endl;//bind2nd(绑定第二个参数)
using placeholders::_1;//取别名直接用_1;
cout << "及格人数:"<< count_if(scores.begin(),scores.end(),
bind(less_equal<int>(),60,_1)) << endl;
cout << "不及格人数:"<< count_if(scores.begin(),scores.end(),
bind(less<int>(),_1,60)) << endl;
//统计70<= score < 90的个数
cout << "良:"<<count_if(scores.begin(),scores.end(),
bind(logical_and<bool>(),bind(less_equal<int>(),70,_1),
bind(less<int>(),_1,90))) <<endl;
//复合闭包
return 0;
}
七 lambda表达式
1. lambda表达式的语法形式
[捕获表] (参数表) 选项 -> 返回类型 {函数体}
auto f1 = [](int x) -> int {return x *x;}
f1是一个类类型的对象,该对象内部实现了一个小括号运算符,从本质上讲,lambda表达式的值就是一个匿名仿函数对象.形式上如同如下类
class 匿名{
public:
int operator() (int x){
returnx * x;
}
};
匿名f1;
cout << f1(10) << endl; //100
参见:lambda1.cpp:lambda2.cpp
#include<iostream>
#include<vector>
using namespace std;
int main(void){
auto f1=[](int x)->int {return x*x;};
cout << f1(10) << endl;//100
//返回类型通常可以省略,编译器可以根据return语句自动推导返回类型;
auto f2=[](int x){return x*x;};
cout << f2(11) << endl;//121
/*列表初始化不能用于返回类型的自动推导
auto f3=[](void){return {10,20,30};};*/
auto f3=[](/*void*/)->vector<int>{return {10,20,30};};
for(auto a:f3())
cout << a << ' ';
cout << endl;
//void类型的返回值和参数标可以省略不写
[](void)->void{cout << "hello,world!" <<endl;}();//直接调用
[]{cout << "hello,world!" << endl;}();
return 0;
}
#include<iostream>
#include<vector>
#include<algorithm>
using namespace std;
bool intCmp(int x,int y){
return x>y;
}
int main(void){
vector<int > vi{13,28,11,23,37,29,44};
for(auto a:vi)
cout << a << ' ';
cout << endl;
//sort(vi.begin(),vi.end());//升序
//sort(vi.begin(),vi.end(),intCmp);//降序
sort(vi.begin(),vi.end(),[](int x,int y){return x>y;});
for(auto a:vi)
cout << a << ' ';
cout << endl;
vector<int> scores{70,40,80,50,70,80,60,60,90,50};
cout << "良:" << count_if(scores.begin(),scores.end(),[](int score){
return 70<=score && score < 90;}) << endl;
return 0;
}
2. lambda表达式的捕获表
[ ] -- 不捕获任何外部变量
[d] -- 按值捕获指定的外部变量
[&d] -- 按引用捕获指定的外部变量
[this] -- 捕获this指针
[=] -- 按值捕获所有的外部变量,(也包括this指针)
[&] -- 按引用捕获所有的外部变量(包括this指针)
[=,&d] -- 按值捕获所有的外部变量(包括this指针),但是对于指定外部变量按引用捕获
参见:lambda3.cpp
#include<iostream>
#include<cmath>
using namespace std;
int a=10;
class Base{
public:
protected:
int b=20;
};
class Derived:public Base{
public:
void foo(int f){
[](int e){
cout << __FUNCTION__ << endl;
cout << a << endl;
//cout << b << endl;
//cout << c << endl;
//cout << f << endl;
cout << e << endl;
}(50);
[f](int e){
cout << __FUNCTION__ << endl;
cout << a << endl;
//cout<< b << endl;
//cout << c << endl;
cout << /*++*/f << endl;//按值捕获过来只读
cout << e << endl;
}(50);
[&f](int e){
cout << __FUNCTION__ << endl;
cout << a << endl;
//cout<< b << endl;
//cout << c << endl;
cout << ++f << endl;//按引用捕获可以修改
cout << e << endl;
}(50);
[this](int e){
cout << __FUNCTION__ << endl;
cout << a << endl;
cout << ++b << endl;
cout << ++c << endl;
//cout << f << endl;
cout << e << endl;
}(50);
[=](int e){
cout << __FUNCTION__ << endl;
cout << ++a << endl;
cout << ++b << endl;
cout << ++c << endl;
cout << /*++*/f << endl;
cout << ++e << endl;
}(50);
[&](int e){
cout << __FUNCTION__ << endl;
cout << ++a << endl;
cout << ++b << endl;
cout << ++c << endl;
cout << ++f << endl;
cout << ++e << endl;
}(50);
[=,&f](int e){
cout << __FUNCTION__ << endl;
cout << ++a << endl;
cout << ++b << endl;
cout << ++c << endl;
cout << ++f << endl;
cout << ++e << endl;//
}(50);
}
private:
int c=30;
};
int main(void){
Derived obj;
obj.foo(40);
int x=60;
auto f1=[x]{cout << x << endl;};
++x;
f1();//60
int y=70;
auto f2=[&y]{cout << y <<endl;};
++y;
f2();//71,cout 在调用时执行的
int z=80;
[=]()/**/mutable{cout<< ++z << endl;}();//81.用了mutable参数表不能省略
cout << z << endl;//80,上面改的只是个副本
[&]{cout << ++z << endl;}();//81
cout << z << endl;//81
//不捕获任何外部变量的lambda表达式可以被隐士转换为相应类型的函数指针
double(*pyth)(double,double)=[](double a,double b)->double{
return sqrt(a*a+b*b);};//sqrt求平方根
cout << pyth(3,4) << endl;//5
return 0;
}
八,泛型元组
泛型元组(tuple)可以理解为是对老版本pair的增强和扩展,其中的元素个数不再局限于两个,功能也更加丰富了.
参见:tuple1.cpp
#include<iostream>
#include<tuple>
using namespace std;
int main(void){
char const* name ="张飞";
int age =25;
double height =1.75;
//直接构造tuple对象
tuple<char const*,int,double> st1(name,age,height);
cout << get<0>(st1) << endl;
cout << get<1>(st1) << endl;
cout << get<2>(st1) << endl;
//通过make_tuple构造tuple对象
auto st2=make_tuple(name,age,height);
cout << get<0>(st2) << endl;
cout << get<1>(st2) << endl;
cout << get<2>(st2) << endl;
//按以上两种方式构造的tuple对象所保存的只是构造实参的拷贝副本
get<0>(st1)="赵云";
get<1>(st2)=20;
cout << name << endl;//不会有任何改变
cout << age << endl;
//通过tie构造tuple对象,保存构造实参的左值引用
auto st3=tie(name,age,height);//t引用
get<2>(st3)=1.65;
cout<< height << endl;
//利用tie可以解析一个tuple对象
auto st4=make_tuple("关羽",30,1.85);
tie(name,age,height)=st4;
cout << name <<' '<< age <<' '<< height<< endl;
//可以用ignore关键字作为占位符忽略掉不感兴趣的元素
tie(name,ignore/*忽略标记*/,height)=st4;
cout << name <<' '<< height << endl;
//auto st5=tie("黄忠",40,1.6);error,只能用左值引用
auto st5=forward_as_tuple("黄忠",40,1.6);
cout << get<0>(st5) << endl;
cout << get<1>(st5) << endl;
cout << get<2>(st5) << endl;
int x=10,y=20;
//通过tuple_cat可以链接多个tuple,而且其中元素的拷贝或引用属性不发生改变
auto tc=tuple_cat(make_tuple(x,y),tie(x,y),forward_as_tuple(x,y));
++x;
++y;
cout << get<0>(tc) <<' '<<get<1>(tc)<< endl;
cout << ++get<2>(tc) <<' '<<++get<3>(tc)<< endl;
cout << get<4>(tc) <<' '<<get<5>(tc)<< endl;
return 0;
}
day03$ a.out
张飞
25
1.75
张飞
25
1.75
张飞
25
1.65
关羽 30 1.85
关羽 1.85
黄忠
40
1.6
10 20
12 22
12 22
九,右值引用
1.左值和右值
可以取地址的值就是左值, 否则就是右值
参见rvr1.cpp
#include<iostream>
using namespace std;
int& foo(int& arg){
return arg;
}
int bar(int& arg){
return arg;
}
int main(void){
//10=10;
//int a=10,b=20;
//a+b=30;
int x=0;
int const y=0;
cout << &x << endl;//所谓有名变量就是左值
cout << &y << endl;//在C++11中只要可以取地址就是左值
cout << ++x << endl;//前自增减
cout << &(x=y) << endl;//赋值形
cout << &foo(x) << endl;//引用形式的函数返回值
//不可取地址的值就是右值,右值通常匿名,没名
//cout << &10 << endl; 字面值常量
//&x++; 后缀自增减表达式的值
//&(x+y);元算表达式的值
//cout << &(double)x <<endl;类型转换是产生新的变量
//&bar(x);//值形式的函数返回值
return 0;
}
2.左值引用和右值引用
左值引用只能引用左值,不能引用右值
右值引用只能引用右值,不能引用左值
常左值引用既可以引用左值,也可以引用右值,称为万能引用
临时值通常只具有语句级的生命周期,但是引用可以将其寿命延长至和引用变量本身一样.
参见rvr2.cpp
#include<iostream>
using namespace std;
class A{
public:
A(void){
cout << "A构造:"<< this << endl;
}
A(A const& a){
cout << "A 拷贝:" << &a << "->" << this<< endl;
}
~A(void){
cout << "A析构:" << this << endl;
}
};
A foo(void){
Aa;
return a;
}
int main(void){
int a=1,b=2;
//左值引用只能引用左值
int& lvr1=a;
/*左值引用不能引用右值
int& lvr2=a+b;*/
//右值引用只能引用右值
int&& rvr1=a+b;
/*右值引用不能引用左值
int&& rvr2=a;*/
//常左值引用既可以引用左值,也可以引用右值,不过不能修改他的值
int const& clvr1=a;
int const& clvr2=a+b;
cout << "---------1----------"<< endl;
foo();
cout << "---------2----------"<< endl;
A&& rvra=foo();
cout << "---------3----------"<< endl;
return 0;
}
// -fno-elide-constructors禁止优化构造器
3.通用引用
在函数模板隐式推断过程中,若实参是左值 ,则T&&被推断为左值引用,若实参是右值,则T&&bei推断为右值引用,这样的引用被称为通用引用
基于auto关键字的类型推导,与此类似S
参见rvr3.cpp
#include<iostream>
using namespace std;
template<typename T>
void foo(T&& arg){
cout << arg << endl;
}
template<typename T>
void bar(T const&& arg){
cout << arg << endl;
}
int main(void){
int a=1,b=2;
foo(a);//arg::int&左值引用
foo(a+b);//arg:int&&右值引用
/*T const&& 只能推断为常右值引用,无法引用左值a
bar(a);*/
bar(a+b);
/*不做隐士推断,没有通用引用
foo<int> (a);*/
foo<int> (a+b);
auto&& c=a;//c:就是左值引用,int&
auto&& d=a+b;//d:就是右值,int&&
/*加了const右值引用无法引用左值
auto const&& e=a;*/
auto const&& f=a+b;
return 0;
}
4.引用折叠
只有右值引用的右值引用才是右值引用,其他情况都是左值引用
参见rvr4.cpp
#include<iostream>
using namespace std;
template<typename T>
void what(void){
if(is_lvalue_reference<T>::value)
cout << "左值引用" << endl;
else
if(is_rvalue_reference<T>::value)
cout << "右值引用" << endl;
else
cout << "不是引用" << endl;
}
int main(void){
using lvr_t =int&;
using rvr_t=int&&;
what<lvr_t>();//左
what<rvr_t>();//右
what<lvr_t&>();//左的左=》左
what<rvr_t&>();//右的左=》左
what<lvr_t&&>();//左的右=》左
what<rvr_t&&>();//右的右=》右
}
5.move和forward
1)move的作用就是把无论左值还是右值,一律处理成右值
2)forward的作用就是根据引用的目标而非引用本身决定处理结果的左右值属性
参见.rvr5.cpp
#include<iostream>
using namespace std;
void foo(int& lvr){
cout << "foo(int&)" << endl;
}
void foo(int&& rvr){
cout << "foo(int&&)" << endl;
}
template<typename T>
void bar(T&& uvr){
foo(uvr);
}
template<typename T>
void hum(T&& uvr){
foo(move(uvr));
}
template<typename T>
void fun(T&& uvr){
//uvr是左值引用,被forward处理还是左值
//uvr是右值引用,被forward处理还是右
foo(forward<T>(uvr));//完美转化
}
int main(void){
int a=1,b=3;
//右值引用只能引用右值(临时变量)
int&& c=a+b;
//右值引用的目标是右值,但其本身是左值(可取地址,所以可复制修改)
cout << &c << endl;
c=100;
cout << c << endl;
//因此右值引用可以被左值引用引用
int& d=c;
//左值引用只能用引用左值(具名变量)
int& e=a;
//左值引用的目标是左值,且其本身也是左值(可取地址)
cout << &e << endl;
e=200;
cout << e << endl;
/*因此左值引用不能被右值引用引用
int&& f=e;*/
//通过move可将左值转换为右值,进而被右值引用引用
int&& g=move(e);
int&& h=move(a);
foo(a);//foo(int&)
foo(a+b);//foo(int&&)
bar(a);
bar(a+b);
hum(a);//int(int&&)
hum(a+b);//int(int&&)
fun(a);
fun(a+b);
return 0;
}
6. 移动语义
三种拷贝语义: 浅拷贝,深拷贝,移动拷贝
1) 浅层拷贝:源对象和目标对象持有相同的指针,指向共享的资源,当一个对象离开作用域而被析构以后,另一个对象也无法继续使用该资源,甚至因double free导致核心转储
参见move1.cpp
#include<cstring>
#include<iostream>
using namespacestd;
class String{
public:
String (char const* str):m_str(strcpy(newchar[strlen(str?str:"")+1],
str?str:"")) {
cout << "构造函数:" << this <<endl;
}
String (String const& str):m_str(str.m_str){
cout << "浅拷贝构造:" << &str<< "->" <<this << endl;
}
~String(void){
cout << "析构函数" << this <<endl;;
delete[] m_str;
}
friend ostream&operator<<(ostream& os,String const& str){
return os<<str.m_str;
}
private:
char* m_str;
};
Stringfoo(void){
String str("hello,world!");
cout << "foo:" << str<< endl;
return str;
}
int main(void){
String str=foo();
cout << "main:" <<str << endl;
return 0;
}
//-fno-elide-constructors
2) 深层拷贝:源对象和目标对象格子拥有独立的资源,其内容相同,当一个对象因离开作用域而被析构以后,另一个对象仍然可以继续自己的资源,也不会因为double free而导致核心转储,但是,对于一些临时的匿名对象,过于频繁的资源分配,赋值和释放,在某种程度上造成性能的下降
参见:move2.cpp
#include<cstring>
#include<iostream>
using namespacestd;
class String{
public:
String (char const* str):m_str(strcpy(newchar[strlen(str?str:"")+1],
str?str:"")) {
cout << "构造函数:" << this <<endl;
}
String (String const&str):m_str(strcpy(new char[strlen(str.m_str)+1],
str.m_str)){
cout << "深拷贝构造:" << &str<< "->" <<this << endl;
}
~String(void){
cout << "析构函数" << this <<endl;;
delete[] m_str;
}
friend ostream&operator<<(ostream& os,String const& str){
return os<<str.m_str;
}
private:
char* m_str;
};
Stringfoo(void){
String str("hello,world!");
cout << "foo:" << str<< endl;
return str;
}
int main(void){
String str=foo();
cout << "main:" <<str << endl;
return 0;
}
//-fno-elide-constructors
3) 移动拷贝:在浅拷贝的基础上把源对象中的资源转移到目标对象中,以此避免不必要的资源分配\复制和释放,提供程序的性能.
参见move3.cpp
#include<cstring>
#include<iostream>
using namespacestd;
class String{
public:
String (char const* str):m_str(strcpy(newchar[strlen(str?str:"")+1],
str?str:"")) {
cout << "构造函数:" << this <<endl;
}
String (String&&str):m_str(str.m_str){
cout << "移动拷贝构造:" << &str<< "->" <<this << endl;
str.m_str=nullptr;
}
~String(void){
cout << "析构函数" << this <<endl;;
delete[] m_str;
}
friend ostream&operator<<(ostream& os,String const& str){
return os<<str.m_str;
}
private:
char* m_str;
};
Stringfoo(void){
String str("hello,world!");
cout << "foo:" << str<< endl;
return str;
}
int main(void){
String str=foo();
//等号右边的对象是一个将亡右值,其资源即将被移动到左侧对象中
cout << "main:" <<str << endl;
String str2= move(str);
cout << "main:" <<str2 << endl;
return 0;
}
//-fno-elide-constructors
4) 完美拷贝:同时提供支持深拷贝和移动拷贝的 拷贝构造函数和拷贝赋值操作符函数,编译器会适当的时候选择适当的版本,既保证功能正确又保证高性能
参见:move4.cpp
#include<cstring>
#include<iostream>
using namespacestd;
class String{
public:
String (char const* str):m_str(strcpy(newchar[strlen(str?str:"")+1],
str?str:"")) {
cout << "构造函数:" << this <<endl;
}
String (String const&str):m_str(strcpy(new char[strlen(str.m_str)+1],
str.m_str)){
cout << "深拷贝构造:" << &str<< "->" <<this << endl;
}
String (String&&str):m_str(str.m_str){
cout << "移动构造:" << &str<< "->" << this << endl;
str.m_str=nullptr;
}
String& operator=(String const&str){
cout << "深拷赋值" << &str<< "- >" << this << endl;
if(&str!=this){
String tmp=str;
swap(m_str,tmp.m_str);
}
return *this;
}
String& operator=(String&&str){
cout << "移动赋值" << &str<< "->" << this << endl;
if(&str!=this){
//若不转为右值,将调用深拷贝构造函数
String tmp=move(str);//
swap(m_str,tmp.m_str);
}
return *this;
}
~String(void){
cout << "析构函数" << this <<endl;;
delete[] m_str;
}
friend ostream&operator<<(ostream& os,String const& str){
return os<<str.m_str;
}
private:
char* m_str;
};
Stringfoo(void){
String str("hello,world!");
cout << "foo:" << str<< endl;
return str;
}
int main(void){
Stringa("hello,C++!"),b("hello,C++14");
cout <<"----------------1--------------" << endl;
String c=a;//深拷贝构造
cout << "c:" << c<< endl;
cout << "----------------2--------------" << endl;
c=b;//c是左值,调深拷贝赋值,在深拷贝赋值里面又调用深拷贝构造
cout << "C;" << c<< endl;
cout <<"----------------3--------------" << endl;
String d=foo();//移动构
cout << "d;" << d<< endl;
cout << "----------------4--------------" << endl;
c=foo();//移动赋值
cout << "c;" << c<< endl;
cout <<"----------------5--------------" << endl;
return 0;
}
//-fno-elide-constructors
十.就地构造
push_back –----emplace_back
push_front------emplace_front
insert 插-------emplace
C++11的标准模板库中的几乎每一个容器都提供了类似emplace_xxx的接口,采用在容器内部就地构造对象的方式,以尽可能避免过多的内存复制和移动,,提供高运行性能
参见:emplace1.cpp
#include<iostream>
#include<vector>
#include<algorithm>
#include<iterator>
using namespacestd;
class Point2D {
public:
Point2D (int x, int y) : m_x (x), m_y (y) {
cout << "构造函数:" << this <<endl;
}
Point2D (Point2D const& pt) : m_x(pt.m_x),m_y (pt.m_y) {
cout << "拷贝构造:" << &pt<< "->"<< this << endl;
}
Point2D& operator= (Point2D const&pt) {
cout << "拷贝赋值:" << &pt<< "->"<< this << endl;
if (&pt != this) {
m_x = pt.m_x;
m_y = pt.m_y;
}
return *this;
}
~Point2D (void) {
cout << "析构函数:" << this <<endl;
}
friend ostream& operator<<(ostream& os,Point2D const& pt) {
return os << '(' << pt.m_x<< ','<< pt.m_y << ')';
}
private:
int m_x, m_y;
};
int main (void){
vector<Point2D> vp1;
cout << "-------- 1--------" << endl;
vp1.push_back (Point2D (1, 2));
cout << "-------- 2--------" << endl;
vp1.push_back (Point2D (5, 6));
cout << "-------- 3--------" << endl;
vp1.insert (vp1.begin () + 1,Point2D (3,4));
cout << "-------- 4--------" << endl;
copy (vp1.begin (), vp1.end(),ostream_iterator<Point2D> (cout, ""));
cout << endl;
vector<Point2D> vp2;
cout << "-------- 5--------" << endl;
vp2.emplace_back (1, 2);
cout << "-------- 6--------" << endl;
vp2.emplace_back (5, 6);
cout << "-------- 7--------" << endl;
vp2.emplace (vp2.begin () + 1, 3, 4);
cout << "-------- 8--------" << endl;
copy (vp2.begin (), vp2.end (),
ostream_iterator<Point2D> (cout,""));
cout << endl;
return 0;
}