【C++11】学习笔记(六)

为改变思考方式而改变

《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声明一个指针控制类型的变量。

    规则:

    1. 所有nullptr_t类型的数据等价,行为完全一致

    2. nullptr_t类型可以隐式转换为任意一个指针类型

    3. nullptr_t类型数据不能转换位非指针类型

    4. nullptr_t类型不能用于算数运算表达式

    5. 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");
    }
    

默认函数控制

  • 默认函数

    编译器默认帮助程序员生成一些未定义的函数,这些函数成为默认函数,包括:

    1. 构造函数
    2. 拷贝构造函数
    3. 拷贝赋值函数
    4. 移动构造函数
    5. 移动拷贝函数
    6. 析构函数

    除此之外,还提供全局默认操作符函数:

    1. operator ,
    2. operator &
    3. operator &&
    4. operator *
    5. operator ->
    6. operator ->*
    7. operator new
    8. operator delete
  • =default

    显示的让编译器生成默认函数,其必要性,看例子:

    class TwoCstor {
    public:
    	//TwoCstor() {}          //1)
    	TwoCstor() = default;    //2)
    	TwoCstor(int i) :data(i) {}
    private:
    	int data;
    };
    
    1. 因为自定义了带参数的构造函数,系统不在生成默认构造函数,因此需要使用1)式。
    2. 使用1)式的自定义构造函数后,改自定义类型不再是平凡数据类型,因此不是POD类型数据。
    3. 为了生成不带参数的构造函数,且保证该类型位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}

    1. [capture]: 捕捉列表,可以看作lambda函数的标识符

    2. (parameters):参数列表,如果,不需要参数,()可以省略

    3. mutable:修饰符。一般情况下,lambda函数总是const函数。使用该关键字可以去掉const属性

    4. -> return-type:返回类型。不需要返回类型时,可以省略

    5. {statement}:函数体。除了可以使用参数外,还可以使用捕获的参数。

  • 捕捉列表:

    1. [val]:表示值传递方式捕捉变量

    2. [=]:表示值传递方式捕捉所有父作用域的变量(包括this)

    3. [&val]:表示引用传递捕捉变量val

    4. [&]:表示值传递方式捕捉所有父作用域的变量(包括this)

    5. [this]:表示值传递方式捕捉当前的this指针

    可以通过组合捕捉:

    1. [=,&a,&b]:
    2. [&,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的实现就是仿真函数。

    1. 通过捕获的变量 与 带参数的构造函数等价。他们有自己的地址。
    2. _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中,关于伪函数的使用:

    1. 使用*LNums(ubound)*构造临时对象作为参数
    2. 在使用对象过程中,重写*operate()*操作符,因此可将对象名当作函数名使用。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值