为改变思考方式而改变
《C++11》第七章
空指针nullptr
-
指针空值
在C++11之前,表示空指针一般使用0或者NULL。因为,擦欧总系统是不允许使用地址为0的内存空间。
当函数重载了int和指针类型时,会造成错误,或者不能通过编译。
void f(char* p){} void f(int){} void main(){ f(NULL); // 调用int版本 ,或者因为二义性编译失败。取决于编译器 }
-
nullptr
为了解决二义性问题,C++11提出nullptr表示空指针。
void f(char* p){} void f(int){} void main(){ f(nullptr); // 准确调用f(char* p)版本
注:nullptr是一个编译器常量,返回的是右值常量,不能取地址。要是一定要取地址,可以用右值引用,当然,这么做没什么意义。
-
nullptr_t
该类型原型;
typedef decltype(nullptr) nullptr_t;
通常情况下,使用nullptr_t声明一个指针控制类型的变量。
规则:
-
所有nullptr_t类型的数据等价,行为完全一致
-
nullptr_t类型可以隐式转换为任意一个指针类型
-
nullptr_t类型数据不能转换位非指针类型
-
nullptr_t类型不能用于算数运算表达式
-
nullptr_t类型数据可以用于关系运算表达式,但只能与nullptr_t类型数据或者指针类数据比较。
在nullptr_t用于模板时,它只能被推到为普通类型,而不是指针类型
#include<iostream> using namespace std; template<typename T> void g(T* t) {} template<typename T> void h(T t) {} void main() { g(nullptr); //无法通过编译 g((float*)nullptr); // T = float h(0); //T = int h(nullptr); // T = nullptr_t h((float*)nullptr); //T = float* system("pause"); }
-
默认函数控制
-
默认函数
编译器默认帮助程序员生成一些未定义的函数,这些函数成为默认函数,包括:
- 构造函数
- 拷贝构造函数
- 拷贝赋值函数
- 移动构造函数
- 移动拷贝函数
- 析构函数
除此之外,还提供全局默认操作符函数:
- operator ,
- operator &
- operator &&
- operator *
- operator ->
- operator ->*
- operator new
- operator delete
-
=default
显示的让编译器生成默认函数,其必要性,看例子:
class TwoCstor { public: //TwoCstor() {} //1) TwoCstor() = default; //2) TwoCstor(int i) :data(i) {} private: int data; };
- 因为自定义了带参数的构造函数,系统不在生成默认构造函数,因此需要使用1)式。
- 使用1)式的自定义构造函数后,改自定义类型不再是平凡数据类型,因此不是POD类型数据。
- 为了生成不带参数的构造函数,且保证该类型位POD,所以使用2)式。
-
=delete
禁止编译器生成不需要的函数:
以拷贝构造函数为例:
class NoCopyCstor { public: NoCopyCstor() = default; NoCopyCstor(const NoCopyCstor&) = delete; private: //NoCopyCstor(const NoCopyCstor&); //C++11之前的做法 };
C++11之前,将需要禁止的构造函数设置为private,达到删除的目的。
C++11使用delete关键字,使得编译器不再生成拷贝构造函数
-
在类定义外声明=delete
=delete也可以在类的定义外声明,被设定为缺省版本,这样做,可以实现多个版本:
//type struct type{type();} struct type1:type::type() = default; struct type2:type::type(){/*do something*/};
-
=delete禁用普通函数
void F(int i){}; void F(char c) = delete; //删除char版本 void main(){ F(3) ; // int 版本 F('c'); //无法通过编译 1) }
如果注释掉delelte,1)会隐式转换,调用int版本的F函数
lambda
-
lambda函数的语法定义:
[capture](parameters) mutable -> return-type{statement}
-
[capture]: 捕捉列表,可以看作lambda函数的标识符
-
(parameters):参数列表,如果,不需要参数,()可以省略
-
mutable:修饰符。一般情况下,lambda函数总是const函数。使用该关键字可以去掉const属性
-
-> return-type:返回类型。不需要返回类型时,可以省略
-
{statement}:函数体。除了可以使用参数外,还可以使用捕获的参数。
-
-
捕捉列表:
-
[val]:表示值传递方式捕捉变量
-
[=]:表示值传递方式捕捉所有父作用域的变量(包括this)
-
[&val]:表示引用传递捕捉变量val
-
[&]:表示值传递方式捕捉所有父作用域的变量(包括this)
-
[this]:表示值传递方式捕捉当前的this指针
可以通过组合捕捉:
- [=,&a,&b]:
- [&,this]:
-
-
lambda与仿真函数
某种程度上说,仿真函数是实现lambda操作的一种方式。labmda是仿真函数在语法上的简化。
#include<iostream> using namespace std; void main() { //cout << __cplusplus << endl; int a = 0, b = 6; cout << __func__ << " &a : " << &a << endl; cout << __func__ << " &b : " << &b << endl; [a, b]() { cout << __func__ << " &a : " << &a << endl; cout << __func__ << " &b : " << &b << endl; }(); system("pause"); }
这个例子,侧面印证,lambda的实现就是仿真函数。
- 通过捕获的变量 与 带参数的构造函数等价。他们有自己的地址。
- _func_ 的结果是 operator () 与 重载()等价。
-
lambda的一些坑
#include<iostream> using namespace std; void main() { int a = 12; auto by_val_lambda = [=] {return a + 1; }; auto by_ref_lambda = [&] {return a + 1; }; cout << "by_val_lambda : " << by_val_lambda() << endl; cout << "by_ref_lambda : " << by_ref_lambda() << endl; a++; cout << "by_val_lambda : " << by_val_lambda() << endl; cout << "by_ref_lambda : " << by_ref_lambda() << endl; system("pause"); }
运行结果;
by_val_lambda : 13 by_ref_lambda : 13 by_val_lambda : 13 by_ref_lambda : 14
再一次证明,lambda函数与仿真函数具有类似特性。按值捕获第一次调用lambda函数,相当于使用带参数构造函数创建对象。之后,再次调用,相当于直接使用对象。
当lambda函数没有捕捉任何参数时,可以转换为函数指针。
void main() { int girls = 3, boys = 4; auto totalChild = [](int x, int y) {return x + y; }; typedef int (*allChild)(int x, int y); typedef int (*oneChild)(int x); allChild p; p = totalChild; oneChild q; //q = totalChild; //编译失败,参数不一致 decltype(totalChild) allPeople = totalChild; system("pause"); }
-
mutable关键字
取消函数const属性。
class mutableText { public: void outPut() const { cout << "OutPut()" << endl; mutable_times++; //times++; 不能通过编译 } private: mutable int mutable_times; int times; };
注:在默认情况下,lamda函数为const函数。需要使用mutable取消const属性
-
lambda和for_each
#include<iostream> #include<algorithm> //包含for_each函数的头文件 #include<vector> using namespace std; vector<int> nums; vector<int> largeNum; constexpr int ubound = 10; inline void Func(int a) { if (a > ubound) largeNum.push_back(a); } class LNums { public: LNums(int u):ubound(u){ cout << "construct Function" << endl; } void operator()(int i)const { if (i > ubound) { largeNum.push_back(i); cout << __func__ << endl; } } private: int ubound; }; void above() { //传统for循环 for (auto itr = nums.begin(); itr != nums.end(); itr++) { if (*itr > ubound) largeNum.push_back(*itr); } //使用函数指针 for_each(nums.begin(), nums.end(), Func); //使用lambda函数 for_each(nums.begin(), nums.end(), [=](int i) {if (i > ubound) largeNum.push_back(i); }); //伪函数 for_each(nums.begin(), nums.end(), LNums(ubound)); //1) }
在表达式1中,关于伪函数的使用:
- 使用*LNums(ubound)*构造临时对象作为参数
- 在使用对象过程中,重写*operate()*操作符,因此可将对象名当作函数名使用。