- 复习前面介绍过的C++11功能
decltype
-
关键字
decltype
将变量的类型声明为表达式指定的类型。 -
示例
double x; int n; decltype(x*n)q;//q same type as x*n,double. decltype(&x)pd;//pd same as &x,double *
-
返回类型后置,能够使用
decltype
来指定函数的返回类型template<typename T,typename U> auto eff(T t,U u)->decltype(T*U) { ... }
-
- 模板别名:
using=
- 对于冗长或复杂的标识符,可以用
typedef
和using=
typedef std::vector<std::string>::iterator itType; using itType=std::vector<std::string>::iterator;
- 可用于模板部分具体化
template<typename T> using arr12=std::array<T,12>; arr12<double>a1; arr12<string>a2;
- 对于冗长或复杂的标识符,可以用
nullptr
- C++11新增关键字,用于表示空指针;它是指针类型,不能转换为整型类型。
- 可以向后兼容,C++11仍允许使用0来表示空指针,因此表达式
nullptr==0
为true
- 显示转换运算符
explicit
-
禁止单参数构造函数和转换函数的自动转换
class Plebe { Plebe(int); explicit Plebe(double); operator int()const; explicit operator double()const; ... }; Plebe a,b; a=5;//allowed b=0.5;//not allowed b=Plebe(0.5);//allowed // int n=a;//allowed double x=b;//not allowed double x=double(b);//allowed
-
移动语义和右值引用
-
将右值关联到右值引用导致该右值被存储到特定的位置,且可以获取该位置的地址。
#include<bits/stdc++.h> using namespace std; int main() { double a=12.4; double &&p1=7.07; double &&p2=12*a+3.4; cout<<p1<<' '<<&p1<<endl; cout<<p2<<' '<<&p2<<endl; } /* output: 7.07 0x6afee0 152.2 0x6afee8 */
-
为何需要移动语义
- 减少从临时变量复制数据的步骤,提高效率
-
移动构造函数可能修改其实参,故右值引用参数不应是
const
-
在引入右值引用前,左值引用不能指向右值,那么这时候会产生一个临时变量。
-
如果想对一个左值来使用移动构造函数,可以使用
move()
,其头文件为utility
-
示例
class ABC{...} ABC a; ABC b; b=std::move(a);
-
如果
ABC
没有定义移动赋值运算符,编译器将使用复制赋值运算符。如果也没有定义复制赋值运算符,将根本不允许上述赋值。一般情况下这些都是默认的成员函数,故一般都可以允许赋值。
-
新的类功能
- 如果提供了复制构造函数或复制赋值运算符,编译器将不会自动提供移动构造函数和移动赋值运算符;如果提供了移动构造函数或移动赋值运算符,编译器将不会自动提供复制构造函数和复制赋值运算符。
- 默认的方法和禁用的方法
-
假定要使用某个默认的函数,而这个函数由于某种原因不会自动创建。例如,您提供了移动构造函数,因此编译器不会自动创建默认的构造函数,复制构造函数和复制赋值构造函数。在这些情况下,可以使用关键字
default
显式地声明这些方法的默认版本:class Someclass { public: Someclass(Someclass &&); Someclass()=default; Someclass(const Someclass &)=default; Someclass &operator=(const Someclass &)=default; ... };
-
另一方面,如果要禁止编译器使用特定方法,可使用关键字
delete
,例如禁止复制对象,可禁用复制构造函数和复制赋值运算符:class Someclass { public: Someclass(Someclass &&)=default; Someclass &operator=(Someclass &&)=default; Someclass()=default; Someclass(const Someclass &)=delete; Someclass &operator=(const Someclass &)=delete; ... };
-
要禁止复制,可将复制构造函数和赋值运算符放在类定义的
private
部分,但使用delete
也能达到这个目的,且更不容易犯错,更容易理解。 -
关键字
default
只能用于6个特殊成员函数,但delete
可用于任何成员函数。delete
的一种可能用法是禁止特定的转换。
-
- 管理虚方法:
override
和final
-
如果基类声明了一个虚方法,在派生类提供不同的版本将会覆盖旧版本,但如果特征标不匹配,那么就会将隐藏而不是覆盖就旧版本:
class ABC { public: virtual void show(int a)const{cout<<a<<endl;} }; class P:public ABC { public: virtual void show()const{cout<<"hello world"<<endl;} }; int main() { P tmp; tmp.show();//allowed tmp.show(1);//not allowed }
-
使用虚说明符
override
指出一个要覆盖的虚函数,那么如果该虚函数在派生类声明与基类方法不匹配,编译器将视为错误。 -
使用虚说明符
final
可禁止派生类覆盖特定的虚方法virtual void show(int a)const override{cout<<a<<endl;}//override virtual void show(int a)const final{cout<<a<<endl;}//final
-
Lambda
函数
-
当
lambda
表达式完全由一条语句组成时,自动类型推断才管用;否则,需要使用新增的返回型后置语法:[](int x){return x%3==0}; [](double x)->double{int y=x;return x-y;}//return type is double
-
为何使用
lambda
-
相比于函数,程序员可以较快了解该语句的功能,而不需要翻阅函数的定义
-
相比于函数符类,它更加简洁明了
-
它可以这样使用
auto mod3=[](int x){return x%3==0;} count1=std::count_if(n1.begin(),n1.end(),mod3); count2=std::count_if(n2.begin(),n2.end(),mod3); bool result=mod3(3);
-
lambda
可访问作用域内的任何动态变量:要捕获要使用的变量,可将其名称放在中括号内。如果只指定了变量名,如[z]
,将按值访问变量;如果在名称前加上&
如[&count]
,将按引用访问变量。[&]
让你能够按引用访问所有动态变量,而[=]
让你能够按值访问所有动态变量。还可以混合使用这两种方式,例如,[ted,&ed]
让你能够按值访问ted
以及按引用访问ed
,[&,ted]
让你能够按值访问ted
以及按引用访问其他所有动态变量,[=,&ed]
让你能够按引用访问ed
以及按值访问其他所有动态变量。 -
一个例子
#include<bits/stdc++.h> using namespace std; int main() { int times=390000; srand(time(0)); int count3=0,count13=0; auto mod3=[&count3](int x){count3+=x%3==0;}; auto mod13=[&count13](int x){count13+=x%13==0;}; vector<int>V(times); generate(V.begin(),V.end(),rand); for_each(V.begin(),V.end(),mod3); for_each(V.begin(),V.end(),mod13); cout<<count3<<' '<<1.0*count3/times<<endl; cout<<count13<<' '<<1.0*count13/times<<endl; }
-
包装器
-
C++提供了多个包装器(wrapper,也叫适配器[adapter]).这些对象用于给其他编程接口提供更一致或更合适的接口。如
bind1st
和bind2nd
-
当模板参数为函数名,函数指针,函数对象,或有名称的
lambda
表达式时,会导致模板的效率极低。这时则需要利用function
从调用特征标的角度定义一个对象,使得模板函数适用于特征标相同的函数指针,函数对象或lambda
表达式。 -
function
的使用示例std::function<double(char,int)>fdci;
它接受一个
char
参数和一个int
参数,并返回一个double
值。那么我们可以将特征标与之相同的任何函数指针,函数对象或lambda
表达式可以赋给它。 -
一个例子
#include<bits/stdc++.h> using namespace std; template <typename T,typename F> T use_f(T v,F f) { static int count=0; count++; cout<<count<<' '<<&count<<' '; return f(v); } class Fp { private: double z; public: Fp(double _z):z(_z){} Fp(){} double operator()(double p){return z*p;} }; double dub(double x){return 2.0*x;} int main() { auto f1=[](double x){return x+10.4;}; cout<<use_f(3,f1)<<endl; cout<<use_f(4,dub)<<endl; cout<<use_f(5,Fp(2.3))<<endl; cout<<endl<<"after use function()"<<endl; function<double(double)>F1=f1; function<double(double)>F2=dub; function<double(double)>F3=Fp(2.3); cout<<use_f(3,F1)<<endl; cout<<use_f(4,F2)<<endl; cout<<use_f(5,F3)<<endl; } /* output: 1 0x48c00c 13 1 0x480254 8 1 0x480250 11 after use function() 1 0x480258 13 2 0x480258 8 3 0x480258 11 */
-
其他使用方式
typedef function<double(double)>fdd;//simplify the type declaration cout<<use_f(y,fdd(dub))<<endl; ... // template<typename T> T use_f(T v,std::function<T(T)>f) { ... return f(v); } cout<<use_f<double>(y,dub)<<endl;