• c++11浅析


    1.字符串原始字面量

    当字符串中有一些特殊符号,如\n、\t这些,如果想让这些特殊字符按照原始的值输出,就可以使用R"XXX(需要按照原始值输出的字符串)XXX",其中XXX用作开始和结束标记,必须相同才能通过语法检查,XXX这部分也可以不写
    示例:

    cout << R"666(helloworld\nhelloworldagain)666" << endl;
    cout << R"(helloworld\nhelloworldagain)" << endl;
    
    • 1
    • 2

    运行结果:
    在这里插入图片描述

    2.指针控制nullptr

    NULL可以表示0的无类型指针,也可以表示0,有时候用起来会有歧义,因此需要一种专门表示空指针的关键字

    int* p = (int*)malloc(sizeof(int));
    if (p == nullptr) {
    	cout << "申请空间失败" << endl;
    }
    
    • 1
    • 2
    • 3
    • 4

    3.常量表达式关键字constexpr

    const可以修饰常量,也可以修饰变量为只读,为了单独表示一种常量的含义,引入了关键字constexpr,从而在编译器就能进行替换,提高程序执行的效率

    1)常量表达式

    在定义的表达式之前用constexpr关键字修饰,这样的表达式就是常量表达式
    示例:

    constexpr int num = 10;
    
    • 1

    2)常量表达式函数

    返回值是常量表达式的函数可以用constexpr修饰,constexpr放在返回值类型之前。
    constexpr可以修饰普通函数、修饰类成员函数、修饰模板函数、修饰构造函数

    a. 常量表达式函数需要满足的条件
    • 函数必须有返回值,且返回值必须是常量表达式;
      返回值为void的函数,不能定义为常量表达式函数
    • 常量表达式函数在使用之前,必须先定义,因为在编译期就要进行解析和替换;
      示例:
    #include 
    #include 
    	
    //常量表达式constexpr
    using namespace std;
    	
    int main(){
    	constexpr int res = constfunc1();
    	return 0;
    }
    	
    	
    constexpr int constfunc1(){
    	return 1;
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    报错信息:
    在这里插入图片描述

    • 函数体中只能存在常量表达式,但是using指令、typedef语句、static_assert断言、return语句除外;
    b. constexpr模板函数

    修饰模板函数时,如果实际返回值类型不是常量表达式,则constexpr会被忽略,相当于是一个普通的模板函数

    c. 构造函数

    构造函数的函数体必须为空,而初始化任务在初始化列表中进行

    4.auto自动类型推导

    当不关心数据类型,而重点关注用已经初始化的变量定义一个新的变量时,就可以使用auto关键字进行自动类型推导
    示例:

    int num = 66;
    auto num2 = num;
    
    • 1
    • 2

    1)不能使用auto的场景

    1. 不能用于函数参数的类型
    2. 不能用来定义数组
    3. 不能用于类的非静态成员变量的初始化
    4. 不能用来推导模板参数

    2)auto常见的使用场景

    1. 进行容器的遍历
    2. 返回值类型比较复杂,需要接收返回值

    5.docltype类型推导

    根据表达式类型推导出类型,表达式可长可短,最短的表达式是一个变量或者数值
    示例:

    decltype(1 + 3.4) d1 = 666;		//double
    decltype('a') ch = 'A';			//char
    decltype(2) num = 1024;			//int
    
    • 1
    • 2
    • 3

    推导规则

    1. 表达式类型是普通变量、普通表达式、类表达式(类变量相关的函数),这种情况下推导出的类型和表达式类型完全相同
    2. 表达式是函数调用,推导出的类型和函数返回值相同
      特别地,返回值是一个纯右值时,只有类类型(自定义类型)能携带const、volatile限定符,其它情况都需要忽略掉这两个限定符
    3. 表达式是左值,或者表达式被括号包围,推导出的类型是表达式类型的引用,如果有volatile、const限定符不能忽略

    6.返回值类型后置

    当函数的返回值的类型不确定,需要经过一系列运算才能得到时们就可以先不写具体的返回值类型,用auto关键字替代,在参数列表后面使用箭头(->)加上decltype(表达式),这样程序就能正常运行
    示例:

    #include 
    	  
    using namespace std;
    	
    //返回值类型后置
    template<class T1, class T2>
    //decltype表达式只要可以正确得到实际的返回值类型即可
    //不需要和函数体内的表达式完全一致
    auto add(T1 t1, T2 t2) -> decltype(t1 + t2)
    {
    	return t1 + t2;
    }
    	
    int main(){
        cout << add(1, 3.14) << endl;
    	
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    7.final

    final关键字可以防止虚函数被重写,防止类被继承

    1)修饰虚函数

    如果一个虚函数不能被子类重写,那就可以在函数的参数列表后面加上final关键字
    示例:

    #include 
    	
    using namespace std;
    	
    class Base {
    public:
    	virtual void func1() final{
    		cout << "base func1()" << endl;
    	}
    };
    	
    class Derive : Base {
    public:
    	virtual void func1() {
    		cout << "derive func1()" << endl;
    	}
    };
    
    int main() {
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    报错信息:
    在这里插入图片描述

    2)修饰类

    如果不想让一个类被继承,就可以给类名后面加上final,当这个类被其它类继承时,就会出错
    示例:

    #include 
    	
    using namespace std;
    	
    class Base final{
    public:
    	virtual void func1(){
    		cout << "base func1()" << endl;
    	}
    };
    	
    class Derive : Base {
    public:
    	virtual void func1() {
    		cout << "derive func1()" << endl;
    	}
    };
    	
    int main() {
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    报错信息:
    在这里插入图片描述

    8.override

    如果在子类中要求必须重写父类的虚函数,就可以在子类的虚函数参数列表后面使用override关键字进行检查,如果重写了父类的虚函数则编译不会报错,如果没有重写则编译报错
    示例:

    #include 
    	
    using namespace std;
    	
    class Base{
    public:
    	virtual void func1(){
    		cout << "base func1()" << endl;
    	}
    };
    	
    class Derive : public Base {
    public:
    	//正确重写
    	virtual void func1() override{
    		cout << "derive func1()" << endl;
    	}
    	
    	//没有重写
    	virtual void func1lll() override {
    		cout << "derive func1()" << endl;
    	}
    };
    
    int main() {
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27

    报错信息:
    在这里插入图片描述

    9.优化模板右尖括号

    c++11之前模板中出现连续的右尖括号就会被当作右移符号,需要在两个尖括号之间用空格隔开,cpp11中可以支持连续的右尖括号

    10.默认模板参数

    cpp11允许在函数模板中指定默认的数据类型,也允许在类模板中指定默认的数据类型
    示例:

    //函数模板的默认参数
    template<class T1 = double , class T2 = char>
    void show(T1 t1 = 10, T2 t2 = 'x') {
    	cout << t1 << "  " << t2 << endl;
    }
    
    void test() {
    	show(2, 'A');						//2 A
    	show();								//10 x
    	show<int>('a', 'a');				//97 a
    	show<char>('a', 'a');				//a a
    	show<int, int>('a', 'a');			//97 97 
    	show<char, int>('a', 'a');			//a 97
    }
    
    
    //类模板的默认类型,类型必须从右往左连续
    template<class T1, class T2 = char>
    class Base1 {
    private:
    	T1 _t1;
    	T2 _t2;
    };
    
    template<class T1 = long, class T2 = char>
    class Base2 {
    private:
    	T1 _t1;
    	T2 _t2;
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30

    函数的默认模板参数和根据参数类型自动推导同时进行从高到低的优先级:
    如果有指定的数据类型,则使用指定的数据类型;
    如果可以推导出,则使用推导出的类型;
    函数模板推导不出实际类型,则使用默认模板参数;
    无法推导出模板参数类型并且也没有默认模板参数,编译器就会报错

    11.using定义别名

    cpp11之前typedef可以定义别名,但是对于比较复杂的类型而言不够直观,可读性不好,于是cpp11中的using有了定义别名的功能。
    示例1:

    using DataType = int;		//定义int类型的别名
    using func = int(*)(int, int);	//定义函数指针
    
    • 1
    • 2

    示例2:

    //方式1
    template<class T>
    struct MyMap{
    	typedef map<int, T> DataType;
    };
    
    //方式2 
    template<class T>
    using MyMap2 = map<int, T>;
    	
    
    void test() {
    	//和下一行等价: MyMap::DataType d1;
    	MyMap2<string> d1;
    	d1.insert(make_pair(1, "efwfw"));
    	d1.insert(make_pair(2, "efwfw"));
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    12.委托构造函数

    将初始化数据成员的一部分工作交给其它构造函数,这就是委托构造函数。

    示例:

    #include 
    #include 
    #include 
    #include 
    
    using namespace std;
    //template   
    //刚才出错的原因是不小心,没有将class上一行的模板声明删除,所以
    class Test{
    private:
    	int _a;
    	int _b;
    	int _c;
    public:
    	Test() {}
    	Test(int a) :_a(a) {}
    	Test(int a, int b) :Test(a){
    		_b = b;
    	}
    	Test(int a, int b, int c) :Test(a, b) {
    		_c = c;
    	}
    	void showData() {
    		cout << _a << " " << _b << " " << _c << endl;
    	}
    };
    
    int main() {
    	Test test1;
    	Test test2(10);
    	Test test3(10, 24);
    	Test test4(10, 24, 66);
    	test1.showData();
    	test2.showData();
    	test3.showData();
    	test4.showData();
    
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39

    运行结果:
    在这里插入图片描述

    13.继承构造函数

    当子类中需要使用父类中的变量,并且子类中没有其它数据成员,这个时候就不需要逐一写出子类中的构造函数,直接继承父类中的所有构造函数,语法如下:

    	using 父类类名::父类类名;
    
    • 1

    示例:

    #include 
    #include 
    #include 
    #include 
    
    using namespace std;
    
    class Base {
    public:
    	int _num1 = 0; 
    	int _num2 = 0;
    	Base() {}
    	Base(int num1, int num2) :_num1(num1) ,_num2(num2){}
    
    	int getSum() {
    		return _num1 + _num2;
    	}
    };
    
    class Derive : public Base{
    public:
    	//继承父类所有的构造函数
    	using Base::Base;
    };
    
    int main() {
    	Derive d(1, 2);
    	Derive d2;
    	cout << d.getSum() << endl;
    	cout << d2.getSum() << endl;
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32

    运行结果:
    在这里插入图片描述

    14.初始化列表

    cpp11中允许使用初始化列表初始化任何数据类型,通过在{}内指定数据来进行初始化工作

    示例:

    #include 
    
    using namespace std;
    
    class Person {
    private:
    	string _name;
    	int _age;
    public:
    	//默认构造
    	Person(string name = "somebody", int age = 0) :_name(name) ,_age(age) {}
    };
    
    int main() {
    	int tmp = 1;
    	double d = 33.33;
    	float f = 1.2;
    	int arr[] = { 1, 2, 3, 4 };
    	Person p("zhangsan", 11);
    
    	//初始化列表
    	int tmp2 = { 1 };
    	double d2{ 33.33 };
    	float f2 = { 1.2 };
    	int arr2[]{ 1, 2, 3, 4 };		
    	Person p2 = { "lisi", 22 };
    	Person p3{ "wangwu", 66 };
    
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30

    1)聚合体初始化

    聚合体可以直接使用初始化列表进行初始化

    聚合体的范围
    • 普通数组
    • 满足以下条件的类(class、struct、union):
      没有自定义构造函数、
      没有私有或保护的非静态数据成员、
      没有基类、
      没有虚函数、
      类中不能有使用{}和=直接初始化的非静态数据成员(c++14开始支持)

    2)非聚合体初始化

    聚合体范围之外的对象在使用初始化列表时,必须有对应的构造函数存在。

    15.initializer_list模板类

    cpp11中提供了一种可以存储多个同类型数据的模板类initializer_list,相比vector而言,initializer_list可以在创建的时候接收任意个数的数据,常作为函数的参数使用。
    示例:

    #include 
    #include 
    
    using namespace std;
    
    int main(){
            initializer_list<int> l{1, 2 ,3 ,4, 5};
            for(auto* it = l.begin(); it != l.end(); ++it){
                    cout << *it << " ";
            }
            cout << endl;
    
            return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    16.范围for循环

    对于范围确定的变量,cpp11提出了一种简化for循环的形式,对于自定义类型如果要使用范围for必须也提供begin和end方法

    格式

    for(变量定义 : 变量的定义域){
    	循环体
    }
    
    • 1
    • 2
    • 3

    变量的定义域可以是数组、容器、初始化列表、表达式
    示例:

    #include 
    #include 
    #include 
    
    using namespace std;
    
    int main(){
            initializer_list<string> l{"I", "am", "a", "good", "man"};
            for(auto e : l){
                    cout << e << " ";
            }
            cout << endl;
    
            return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    17.可调用对象包装器、绑定器

    1)可调用对象

    可调用对象泛指c++中可以使用函数方式调用的对象,当然这种说法对于大多数的可调用对象是适用的,也有一些不适用,对于下文所说的第四种可调用对象这种说法就不适用

    四种可调用对象

    可调用对象根据类型的不同有以下四种

    1. 函数指针
    2. 仿函数: 重载了operator()运算符的类对象
    3. 可以被转换为函数指针的类对象
    4. 类成员函数指针或者类成员指针

    示例1:

    #include 
    #include 
    
    using namespace std;
    
    //第一种可调用对象:函数指针
    void printInfo(string name, int age) {
    	cout << "name:" << name << "\tage:" << age << endl;
    }
    
    int main() {
    	cout << "第一种可调用对象:函数指针" << endl;
    	printInfo("cpp11", 12);
    
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    运行结果:
    在这里插入图片描述
    示例2:

    #include 
    #include 
    
    using namespace std;
    
    //第二种可调用对象:仿函数
    class ObjectTest {
    public:
    	void operator()(string name) {
    		cout << "My name is " << name << endl;
    	}
    };
    
    int main() {
    	cout << "第二种可调用对象:仿函数" << endl;
    	ObjectTest test;
    	test("zhangsan");
    
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    运行结果:
    在这里插入图片描述
    示例3:

    #include 
    #include 
    
    using namespace std;
    
    using funcPtr1 = void(*)(double, double);
    
    //第三种可调用对象:可以被转换为函数指针的类对象
    class Caculator{
    public:
    	static void minus(double num1, double num2) {
    		cout << num1 << " - " << num2 << " = " << num1 - num2 << endl;
    	}
    	
    	operator funcPtr1() {
    		return minus;
    	}
    };
    
    
    int main() {
    	cout << "第三种可调用对象:可以被转换为函数指针的类对象" << endl;
    	
    	Caculator cal;
    	cal(1, 3);
    
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28

    运行结果:
    在这里插入图片描述
    示例4:

    #include 
    #include 
    
    using namespace std;
    
    
    //第四种可调用对象:类成员函数指针或者类成员指针
    class Year{
    public:
    	int _year = 2022;
    
    	void printInfo() {
    		cout << "今年是" << _year << "年" << endl;
    	}
    };
    
    
    int main() {
    	cout << "第四种可调用对象:类成员函数指针或者类成员指针" << endl;
    
    	//(1)类成员函数指针
    	using printMessage = void(Year::*)(void);		//定义函数指针
    	printMessage p1 = &Year::printInfo;				//定义对象
    	Year year;
    	//必须使用()将year.*p1括起来,因为p1后面的()优先级高于*
    	(year.*p1)();
    
    	//(2)类成员指针
    	using Ptr = int(Year::*);		//定义Year作用域下的int指针
    	Ptr p2 = &Year::_year;
    	year.*p2 = -2022;
    	year.printInfo();
    
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35

    运行结果:
    在这里插入图片描述

    2) 可调用对象包装器

    function作为一种类模板,可以对除了类成员函数指针之外的所有可调用对象进行包装,实现统一方式的调用。

    a.语法
    function<返回值类型(参数类型)> 变量名 = 可调用对象;
    
    • 1
    b.使用示例

    示例1:

    #include 
    #include 
    #include 
    
    using namespace std;
    
    
    void print1(string name) {
    	cout << "print1, name is " << name << endl;
    }
    
    class Test {
    public:
    	static void print2(string name) {
    		cout << "print2, name is " << name << endl;
    	}
    };
    
    class Test2 {
    public:
    	void operator()(string name) {
    		cout << "print3, name is " << name << endl;
    	}
    };
    
    //定义函数指针
    using funcPtr = void(*)(string);
    
    class Test3 {
    public:
    	operator funcPtr() {
    		return print4;
    	}
    
    	static void print4(string name) {
    		cout << "print4, name is " << name << endl;
    	}
    };
    
    //可调用对象包装器
    int main() {
    	//1.可调用对象包装器包装普通函数
    	function<void(string)> f1 = print1;
    	f1("张三");
    
    	//2.可调用对象包装器包装类的静态函数
    	function<void(string)> f2 = Test::print2;
    	f2("李四");
    
    	//3.可调用对象包装类包装仿函数
    	Test2 test2;
    	function<void(string)> f3 = test2;
    	f3("王麻子");
    
    	//4.可调用对象包装类包装可转换为函数指针的类
    	Test3 test3;
    	function<void(string)> f4 = test3;
    	f4("乔峰");
    
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61

    运行结果:
    在这里插入图片描述

    3) 可调用对象绑定器

    绑定器bind可以将可调用对象绑定到一起,作为一个仿函数输出,可以通过funcion接收绑定后的结果
    bind的两种形式如下:

    	//形式1:绑定非类成员函数/变量
    	bind(可调用对象地址,参数或占位符);
    	//形式2:绑定类成员函数/变量
    	bind(可调用对象地址,类实例对象地址, 参数或占位符);
    
    • 1
    • 2
    • 3
    • 4
    a. 占位符placeholders

    bind在绑定参数时,如果参数只有在实际传参的时候才能确定,就可以使用placeholder进行占位,第一个参数就是placeholders::_1,第一个参数就是placeholders::_2,第N个参数就是placeholders::_N(N为自然数)

    b. 两种绑定形式
    • 形式1:绑定非类成员函数/变量
      示例1(绑定非类成员函数):
    #include 
    #include 
    #include 
    
    using namespace std;
    
    
    string mystrAdd(string str1, string str2) {
    	return str1 + str2;
    }
    
    //绑定非类成员函数
    int main() {
    	//1.绑定时传入全部参数
    	auto func1 = bind(mystrAdd, "hello ", "world");
    	//即使调用时传入参数,但发挥作用的还是绑定时的参数
    	string res = func1("FFF", "lll");	
    	cout << res << endl;
    
    	//2.绑定时传入部分参数
    	auto func2 = bind(mystrAdd, "hello ", placeholders::_1);
    	res = func2("china");
    	cout << res << endl;
    
    	//3.绑定时不传入参数,使用占位符
    	auto func3 = bind(mystrAdd, placeholders::_1, placeholders::_2);
    	res = func3("good", " afternoon");
    	cout << res << endl;
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30

    运行结果:
    在这里插入图片描述

    • 形式2:绑定类成员函数/变量
      示例1(绑定类成员函数):
    #include 
    #include 
    #include 
    
    using namespace std;
    
    int globalVar = 10;
    
    //绑定类成员函数
    class TestClass1 {
    public:
    	//求a的b次方
    	int mypow(int a, int b) {
    		int ret = 1;
    		for (int i = 0; i < b; ++i) {
    			ret *= a;
    		}
    		return ret;
    	}
    };
    
    int main() {
    	TestClass1 c1;
    	//1.绑定时传入全部参数
    	auto f1 = bind(&TestClass1::mypow, &c1, 2, 10);
    	cout << "2的10次方为: " << f1(3, 2) << endl;
    	//2.绑定时传入部分参数
    	auto f2 = bind(&TestClass1::mypow, &c1, 2, placeholders::_1);
    	cout << "2的6次方为: " << f2(6) << endl;
    	//3.绑定时不传入参数,全部使用占位符
    	auto f3 = bind(&TestClass1::mypow, &c1, placeholders::_2, placeholders::_1);
    	cout << "5的3次方为: " << f3(3, 5) << endl;
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34

    运行结果:
    在这里插入图片描述
    示例2(绑定类成员变量):

    #include 
    #include 
    #include 
    
    using namespace std;
    
    int globalVar = 10;
    
    //绑定类成员变量
    class TestClass2{
    public:
    	string key = "gwjpgjwjgowjgwojgobbb";
    };
    
    int main() {
    	TestClass2 c;
    	//设置返回值为引用类型就可以对key进行修改
    	function<string&(void)> f = bind(&TestClass2::key, &c);
    	cout << f() << endl;
    	//修改key
    	f() = "67890vsvns09inbgtyuj";
    	cout << f() << endl;
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    运行结果:
    在这里插入图片描述

    18.lambda表达式

    在c++11之前,每次需要调用一个函数之前就需要提前定义,为了随时进行函数的定义和使用,cpp11引入了一种lambda表达式,它可以方便地进行业务逻辑的定义和调用,并且也可以捕获父作用域的变量。
    这种表达式本质上被当作匿名仿函数使用

    1) 语法

    //定义
    auto func = [捕获列表](参数列表)->返回值类型 mutable{
    	函数体
    };
    //调用
    func(参数);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    2)使用相关细节介绍

    • 捕获列表
      当需要以值的方式捕捉父作用域的所有变量时,捕获列表中用=表示;
      当需要以引用的方式捕捉父作用域的所有变量时,捕获列表中用&表示;
      当需要捕获外部的类的所有成员时,捕获列表中用this表示;
      当捕获列表需要通过值的方式捕捉某个变量时,直接在捕获列表中用变量名表示;
      当捕获列表需要通过引用的方式捕捉某个变量时,直接在捕获列表中用&变量名表示;
      捕捉列表允许捕捉多个变量,中间用逗号隔开;
      捕获列表中既可以以值的形式捕捉变量,也可以通过引用的方式捕捉变量,但是同一个变量不能同时通过两种方式捕捉;
      示例:
    #include 
    #include 
    #include 
    
    using namespace std;
    
    int main() {
    
    	int a = 10;
    	int b = 29;
    	int c = 30;
    	auto f = [&a, b](){
    		a += b;
    	};
    	f();
    	cout << "a = " << a << endl;
    
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    运行结果:
    在这里插入图片描述

    • 参数列表
      和普通函数的参数列表等同,如果参数列表为空,可以省略,但是加上语义更加明确
    • 返回值类型
      返回值类型不写则根据实际返回值类型进行推导,但是如果返回值类型不明确,需要指定返回值类型,在参数列表后通过->返回值类型来指定
      示例:
    #include 
    #include 
    #include 
    
    using namespace std;
    
    int main() {
    	auto arr = [](string str1, string str2) ->vector<string>{
    		return { str1, str2 };
    	}("blue ", "air");
    	for (auto e : arr) {
    		cout << e << endl;
    	}
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    运行结果:
    在这里插入图片描述

    • mutable关键字的作用
      当通过值捕获父作用域变量时,这些变量默认是只读的,不能进行修改,加上mutable关键字就可以在表达式内部对这些变量的值进行修改
      示例:
      没有使用mutable关键字:
      在这里插入图片描述
      使用mutable关键字之后:
      在这里插入图片描述
    • lambda表达式之间不支持相互赋值,即使类型相同

    19.右值引用

    1)右值

    在c++中,能够取地址的被称为左值,不能取地址的被称为右值,例如12就是右值,不能取地址,而一个变量num则是左值,num可以取到他的地址。
    c++11中右值分为两种,一种是字面量,如1、2.3这些数值,"helloworld"这类字符串,另一种是将亡值(生命周期即将结束的变量)。

    2)右值引用

    右值引用是一种只能引用右值的引用类型,它是一个右值的别名,通常在变量类型后面加上&&表示正是在定义一个右值引用类型。
    示例如下:

    	int num = 10;
    	int& r1 = num;			//左值引用
    	int&& r2 = 115;			//右值引用
    	const int& ra = num;	//常量的左值引用可以引用同类型的左值
    	ra = 111;		//error,常量的左值引用的值不能发生变化
    	const int&& rb = 1;		//常量的右值引用只能引用同类型的右值
    
    	const int& rc = 1;		//常量的左值引用可以引用同类型的右值
    	const int& rd = ra;		//常量的左值引用可以引用同类型的左值引用
    	const int& re = rb;		//常量的左值引用可以引用同类型的右值引用
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    a. 右值引用的作用
    • 延长对象的生命周期
      当对象的生命周期即将结束的时候,可以使用右值引用来引用这个变量,这个变量的生命周期就延长到了和右值引用的生命周期相同的程度。
    • 为中间变量取别名
    • 进行完美转发

    为中间变量取别名示例:

    	int&& tmp = 1 + 2;		
    
    • 1
    b. 右值引用的自动类型推导

    在泛型编程中,当使用T&& 或者auto&& 来推导一个变量的类型时,除了const T&& 和const auto&& 标识一个右值引用,其它都遵循下面两条规则:

    • 使用右值推导出的是右值引用
    • 使用非右值推导出的是左值引用,非右值包括左值、左值引用、常量左值引用、右值引用、常量右值引用

    示例:

    template<class T>
    class Test{
    public:
    	void func() {
    		auto&& ra = 1;			//右值引用	
    		int num = 1;			//左值
    		int& rb = num;			//左值引用
    		const int& rc = num;	//常量的左值引用
    		const int&& rd = 1;		//常量的右值引用
    
    		//非右值使用auto&&推导出的都是左值引用
    		auto&& r1 = ra;		//使用右值引用进行推导
    		auto&& r2 = rd;		//使用常量的右值引用进行推导
    		auto&& r3 = num;	//使用左值进行推导
    		auto&& r4 = rb;		//使用左值引用进行推导
    		auto&& r5 = rc;		//使用常量的左值引用进行推导
    
    		//使用const auto&& 推导出的一定是右值引用
    		const auto&& r11 = 1;		//使用右值进行推导
    		const auto&& r12 = ra;		//使用右值引用
    		const auto&& r13 = rd;		//使用常量的右值引用进行推导
    		const auto&& r14 = num;		//使用左值进行推导
    		const auto&& r15 = rb;		//使用左值引用进行推导
    		const auto&& r16 = rc;		//使用常量的左值引用进行推导
    	}
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    c.右值引用的传递

    右值引用在作为函数参数进行传递的时候就会被句柄化,编译器就会把这个右值引用视为一个左值引用

    示例:

    #include 
    
    using namespace std;
    
    void printValue(int& val) {
    	cout << "val是左值引用, 引用的值是 " << val;
    }
    
    void printValue(int&& val) {
    	cout << "val是右值引用, 引用的值是 " << val;
    }
    
    void forward(int&& ra) {
    	//右值引用被句柄化,成为了一个左值引用
    	printValue(ra);
    }
    
    
    int main() {
    	forward(1);		//右值会被ra接收,成为一个右值引用
    
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    运行结果:
    在这里插入图片描述

    d. 移动构造函数

    一种参数类型为当前类型右值引用的构造函数,移动的意思指的是将右值引用对象引用的对象内的资源移动到当前构造的对象中。在c++11中如果使用一个右值来构造对象,有移动构造函数优先使用移动构造函数,没有移动构造则调用拷贝构造函数。

    移动构造示例:

    #include s
    
    using namespace std;
    
    class Counter {
    public:
    	int* _counter;
    	Counter(int num = 0) :_counter(new int(num)){
    		cout << "构造函数,新资源的地址: " << _counter << endl;
    	}
    
    	Counter(const Counter& counter) :_counter(new int(*(counter._counter))) {
    		cout << "拷贝构造函数, 新资源的地址: " << _counter << endl;
    	}
    
    	Counter(Counter&& counter) :_counter(counter._counter) {
    		cout << "移动构造函数, 资源发生了转移,当前资源的地址是: " << _counter << endl;
    		counter._counter = nullptr;
    	}
    
    	~Counter() {
    		cout << "析构函数" << endl;
    		if (_counter != nullptr) {
    			cout << _counter << "资源释放了一次" << endl;
    			delete _counter;
    			_counter = nullptr;
    		}
    	}
    };
    
    Counter getInstance() {
    	Counter c(1);
    	return c;
    }
    
    int main() {
    	Counter c1 = getInstance();
    
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40

    运行结果:
    在这里插入图片描述

    e. move

    move函数的作用是将左值转换为右值
    当一个左值不再使用,而需要新建一个同类型的左值时,就可以使用move将不用的左值转换为右值,调用移动构造,实现资源的转移。
    示例:

    #include 
    #include 		//move
    
    using namespace std;
    
    class Test {
    public:
    	Test() {
    		cout << "构造函数" << endl;
    	}
    
    	~Test() {
    		cout << "析构函数" << endl;
    	}
    
    	Test(const Test& test) {
    		cout << "拷贝构造" << endl;
    	}
    
    	//这是个不太规范的移动构造,没有移动任何资源,只是作为move的演示使用
    	Test(Test&& test) {
    		cout << "移动构造" << endl;
    	}
    };
    
    
    int main() {
    	Test t;
    	Test t2 = move(t);		//把左值t转换为右值
    	Test t3 = t;
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32

    运行结果:
    在这里插入图片描述

    f. 完美转发forward

    forward函数可以根据模板参数类型进行转换,传入的参数类型为左值引用则返回参数的左值引用,模板参数类型不是左值引用则返回参数的右值引用形式,因为返回的类型和实际传入的类型相同,不受到右值引用传递的影响,所以叫做完美转发。
    forward函数的原型:

    //模板参数类型是左值引用则返回参数,而不修改类型
    template <class T> T&& forward (typename remove_reference<T>::type& arg) noexcept;
    //模板参数类型是非左值,则返回参数的右值引用
    template <class T> T&& forward (typename remove_reference<T>::type&& arg) noexcept;
    
    • 1
    • 2
    • 3
    • 4

    示例:

    #include 
    #include 		//forward
    
    using namespace std;
    
    template<class T>
    void printValue(T&& val) {
    	cout << "参数类型为右值引用, val : " << val << endl;
    }
    
    template<class T>
    void printValue(T& val) {
    	cout << "参数类型为左值引用, val : " << val << endl;
    }
    
    template<class T>
    void fun1(T&& val) {
    	cout << "不进行完美转发如下: " << endl;
    	printValue(val);			//右值引用被传递为左值
    	cout << "使用完美转发如下:" << endl;
    	printValue(forward<T>(val));
    }
    
    int main() {
    	fun1<int>(1);	//右值作为参数会被转换为右值引用
    
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28

    运行结果:
    在这里插入图片描述

    20. 智能指针

    智能指针是一种通过对象生命周期来进行资源管理的模板类,在构造智能指针对象时传入要管理的资源,智能指针对象会在析构时自动释放资源,从而可以帮助我们进行资源管理,防止内存泄漏。同时智能指针对象可以通过->运算符调用管理的资源,拥有指针的特性。
    比较实用的智能指针有三种shared_ptr,weak_ptr,unique_ptr

    1)shared_ptr

    shared_ptr是一种通过引用计数方式来进行资源管理的智能指针模板类,多个shared_ptr对象共同管理一份资源会使用计数器进行计数,当有shared_ptr对象和资源断开联系时会计数减1,当有新的智能指针对象和其它shared_ptr对象共同管理一份资源时会计数加1。
    经常使用的方法如下:

    a.产生一个shared_ptr对象
    • shared_ptr<管理的资源类型>(管理的资源指针)
    • shared_ptr<管理的资源类型>(管理的资源指针, 删除器)
    • shared_ptr<管理的资源类型>(一个shared_ptr对象)
    • shared_ptr<管理的资源类型>(一个生命周期即将结束的shared_ptr对象)
    • make_shared(要管理对象的参数列表)
      示例:
    #include 
    #include 
    #include 
    #include 
    
    using namespace std;
    
    class Person {
    public:
    	string name_;
    	Person(string name = "xxx")
    		:name_(name) {
    		cout << "Person构造函数" << endl;
    	}
    	~Person() {
    		cout << "Person析构函数" << endl;
    	}
    };
    
    using delPtr = void(*)(Person p[]);
    
    int main() {
    	Person* person = new Person("张三");
    	shared_ptr<Person> ptr1(person);		//构造函数
    	shared_ptr<Person> ptr2(ptr1);			//拷贝构造
    	shared_ptr<Person> ptr3(move(ptr1));	//移动构造
    	//定制删除器
    	shared_ptr<Person[]> ptr4(new Person[2], [](Person p[]) {
    		delete[] p;
    	});
    	shared_ptr<Person> ptr5 = make_shared<Person>("李四");
    
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34

    运行结果:
    在这里插入图片描述

    b.use_count函数

    返回和当前shared_ptr对象共同管理同一份资源的share_ptr对象的个数
    示例:

    #include 
    #include 
    #include 
    #include 
    
    using namespace std;
    
    class Person {
    public:
    	string name_;
    	Person(string name = "xxx")
    		:name_(name) {
    		cout << "Person构造函数" << endl;
    	}
    	~Person() {
    		cout << "Person析构函数" << endl;
    	}
    };
    
    int main() {
    	Person* person = new Person("张三");
    	shared_ptr<Person> ptr1(person);		//构造函数
    	cout << "ptr1.usecount : " << ptr1.use_count() << endl;
    	shared_ptr<Person> ptr2(ptr1);			//拷贝构造
    	cout << "ptr1.usecount : " << ptr1.use_count() << endl;
    	cout << "ptr2.usecount : " << ptr2.use_count() << endl;
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28

    运行结果:
    在这里插入图片描述

    c.reset

    让shared_ptr对象和当前管理的资源断开联系,有新资源的话建立和新资源的联系
    两种形式如下:

    shared_ptr对象.reset();	//断开和管理的资源的联系
    shared_ptr对象.reset(新资源指针);	//断开和之前管理的资源的联系,并管理新资源
    
    • 1
    • 2

    示例:

    #include 
    #include 
    #include 
    #include 
    
    using namespace std;
    
    class Person {
    public:
    	string name_;
    	Person(string name = "xxx")
    		:name_(name) {
    		cout << "Person构造函数" << endl;
    	}
    	~Person() {
    		cout << "Person析构函数" << endl;
    	}
    };
    
    int main() {
    	Person* person = new Person("张三");
    	shared_ptr<Person> ptr1(person);		//构造函数
    	ptr1.reset();
    
    	shared_ptr<Person> ptr2(new Person("李四"));
    	ptr2.reset(new Person("王麻子"));
    
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29

    运行结果:
    在这里插入图片描述

    d.获取管理的资源

    有两种方式可以获取到管理的资源,一种直接使用->运算符,另一种是使用成员函数get
    示例如下:

    #include 
    #include 
    #include 
    #include 
    
    using namespace std;
    
    class Person {
    public:
    	string name_;
    	Person(string name = "xxx")
    		:name_(name) {
    		cout << "Person构造函数" << endl;
    	}
    	~Person() {
    		cout << "Person析构函数" << endl;
    	}
    	void sayName() {
    		cout << "my name is " << name_ << endl;
    	}
    };
    
    int main() {
    	Person* person = new Person("张三");
    	shared_ptr<Person> ptr1(person);
    	//通过指针特性调用管理的资源
    	ptr1->sayName();
    	//通过get方法获取管理的资源
    	Person* p = ptr1.get();
    	p->sayName();
    
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33

    运行结果:
    在这里插入图片描述

    2)weak_ptr

    weak_ptr是为了解决shared_ptr循环引用而产生的一种智能指针,当shared_ptr管理的资源中也存在shared_ptr成员时,如果两个shared_ptr对象管理的资源中的shared_ptr对象相互指向就会出现资源无法释放的问题,使用weak_ptr就可以解决循环引用问题,weak_ptr对象可以监控shared_ptr对象,且不会增加引用计数

    a.产生weak_ptr对象
    weak_ptr()    //构造一个空对象
    weak_ptr(shared_ptr对象) //使用shared_ptr对象构造一个weak_ptr对象
    
    • 1
    • 2

    示例:

    #include 
    #include 
    #include 
    #include 
    
    using namespace std;
    
    class Person {
    public:
    	string name_;
    	Person(string name = "xxx")
    		:name_(name) {
    		cout << "Person构造函数" << endl;
    	}
    	~Person() {
    		cout << "Person析构函数" << endl;
    	}
    };
    
    int main() {
    	weak_ptr<Person> ptr;		//构造一个空对象
    	Person* person = new Person("张三");
    	shared_ptr<Person> ptr1(person);		
    	weak_ptr<Person> ptr2(ptr1);
    	
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27

    运行结果:
    在这里插入图片描述

    b.use_count

    返回共同管理同一份资源的shared_ptr对象个数
    示例:

    #include 
    #include 
    #include 
    #include 
    
    using namespace std;
    
    class Person {
    public:
    	string name_;
    	Person(string name = "xxx")
    		:name_(name) {
    		cout << "Person构造函数" << endl;
    	}
    	~Person() {
    		cout << "Person析构函数" << endl;
    	}
    };
    
    int main() {
    	weak_ptr<Person> ptr;		//构造一个空对象
    	Person* person = new Person("张三");
    	shared_ptr<Person> ptr1(person);		
    	weak_ptr<Person> ptr2(ptr1);
    	cout << "ptr2.use_count:" << ptr2.use_count() << endl;
    	shared_ptr<Person> ptr3 = ptr1;
    	cout << "ptr2.use_count:" << ptr2.use_count() << endl;
    	
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30

    运行结果:
    在这里插入图片描述

    c.reset

    和监控的shared_ptr对象断开联系

    weak_ptr对象.reset()
    
    • 1

    示例:

    #include 
    #include 
    #include 
    #include 
    
    using namespace std;
    
    class Person {
    public:
    	string name_;
    	Person(string name = "xxx")
    		:name_(name) {
    		cout << "Person构造函数" << endl;
    	}
    	~Person() {
    		cout << "Person析构函数" << endl;
    	}
    };
    
    int main() {
    	Person* person = new Person("张三");
    	shared_ptr<Person> ptr1(person);		
    	weak_ptr<Person> ptr2(ptr1);
    	ptr2.reset();
    
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    d.expired

    判断weak_ptr对象是否为空,空则返回true,否则返回false

    weak_ptr对象.expired()
    
    • 1

    示例:

    #include 
    #include 
    #include 
    #include 
    
    using namespace std;
    
    class Person {
    public:
    	string name_;
    	Person(string name = "xxx")
    		:name_(name) {
    		cout << "Person构造函数" << endl;
    	}
    	~Person() {
    		cout << "Person析构函数" << endl;
    	}
    };
    
    int main() {
    	Person* person = new Person("张三");
    	shared_ptr<Person> ptr1(person);		
    	weak_ptr<Person> ptr2(ptr1);
    	if (ptr2.expired()) {
    		cout << "ptr is empty" << endl;
    	}
    	else {
    		cout << "ptr is not empty" << endl;
    	}
    	ptr2.reset();
    	if (ptr2.expired()) {
    		cout << "ptr is empty" << endl;
    	}
    	else {
    		cout << "ptr is not empty" << endl;
    	}
    
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39

    运行结果:
    在这里插入图片描述

    e.lock

    返回weak_ptr对象监控的shared_ptr对象
    示例:

    #include 
    #include 
    #include 
    #include 
    
    using namespace std;
    
    class Person {
    public:
    	string name_;
    	Person(string name = "xxx")
    		:name_(name) {
    		cout << "Person构造函数" << endl;
    	}
    	~Person() {
    		cout << "Person析构函数" << endl;
    	}
    	void sayName() {
    		cout << "my name is " << name_ << endl;
    	}
    };
    
    int main() {
    	Person* person = new Person("张三");
    	shared_ptr<Person> ptr1(person);		
    	weak_ptr<Person> ptr2(ptr1);
    	shared_ptr<Person> ptr3 = ptr2.lock();
    	ptr3->sayName();
    
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31

    运行结果:
    在这里插入图片描述

    f.解决循环引用示例

    不使用weak_ptr的情况:

    #include 
    #include 
    #include 
    #include 
    
    using namespace std;
    
    class Person {
    public:
    	string name_;
    	shared_ptr<Person> sptr_;
    	Person(string name = "xxx")
    		:name_(name) {
    		cout << "Person构造函数" << endl;
    	}
    	~Person() {
    		cout << "Person析构函数" << endl;
    	}
    	void sayName() {
    		cout << "my name is " << name_ << endl;
    	}
    };
    
    int main() {
    	shared_ptr<Person> ptr1(new Person("张三"));
    	shared_ptr<Person> ptr2(new Person("李四"));
    	ptr1->sptr_ = ptr2;
    	ptr2->sptr_ = ptr1;
    
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31

    运行结果:
    在这里插入图片描述
    使用weak_ptr的情况:

    #include 
    #include 
    #include 
    #include 
    
    using namespace std;
    
    class Person {
    public:
    	string name_;
    	weak_ptr<Person> sptr_;
    	Person(string name = "xxx")
    		:name_(name) {
    		cout << "Person构造函数" << endl;
    	}
    	~Person() {
    		cout << "Person析构函数" << endl;
    	}
    	void sayName() {
    		cout << "my name is " << name_ << endl;
    	}
    };
    
    int main() {
    	shared_ptr<Person> ptr1(new Person("张三"));
    	shared_ptr<Person> ptr2(new Person("李四"));
    	ptr1->sptr_ = ptr2;
    	ptr2->sptr_ = ptr1;
    
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31

    运行结果:
    在这里插入图片描述

    3)unique_ptr

    一种不能使用拷贝构造和赋值运算符重载函数的指针

    a.构造对象
    unique_ptr<资源类型>()
    unique_ptr<资源类型>(管理的资源指针)
    unique_ptr<资源类型, 删除器类型>(管理的资源指针, 删除器)
    unique_ptr<资源类型>(生命周期即将结果的unique_ptr对象)
    
    • 1
    • 2
    • 3
    • 4

    示例:

    #include 
    #include 
    #include 
    #include 
    
    using namespace std;
    
    class Person {
    public:
    	string name_;
    	Person(string name = "xxx")
    		:name_(name) {
    		cout << "Person构造函数" << endl;
    	}
    	~Person() {
    		cout << "Person析构函数" << endl;
    	}
    };
    
    using delPtr = void(*)(Person p[]);
    
    int main() {
    	unique_ptr<Person> ptr;		//构造一个空对象
    	unique_ptr<Person> ptr2(new Person);	
    	cout << "------------------------------------" << endl;
    	unique_ptr<Person, delPtr> ptr3(new Person[3], [](Person p[]) {
    		delete[] p;
    	});
    	unique_ptr<Person> ptr4(move(ptr2));	//移动构造
    
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32

    运行结果:
    在这里插入图片描述

    b.get

    获取unique_ptr管理的资源指针
    示例:

    #include 
    #include 
    #include 
    #include 
    
    using namespace std;
    
    class Person {
    public:
    	string name_;
    	Person(string name = "xxx")
    		:name_(name) {
    		cout << "Person构造函数" << endl;
    	}
    	~Person() {
    		cout << "Person析构函数" << endl;
    	}
    	void sayName() {
    		cout << "我的名字是" << name_ << endl;
    	}
    };
    
    using delPtr = void(*)(Person p[]);
    
    int main() {
    	unique_ptr<Person> ptr(new Person("愿望"));	
    	//通过指针特性调用
    	ptr->sayName();
    	//通过get获取资源指针调用
    	Person* p = ptr.get();
    	p->sayName();
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33

    运行结果:
    在这里插入图片描述

    c.reset

    断开和管理资源的联系,如果有新资源指针则管理新资源
    示例:

    #include 
    #include 
    #include 
    #include 
    
    using namespace std;
    
    class Person {
    public:
    	string name_;
    	Person(string name = "xxx")
    		:name_(name) {
    		cout << "Person构造函数" << endl;
    	}
    	~Person() {
    		cout << "Person析构函数" << endl;
    	}
    	void sayName() {
    		cout << "我的名字是" << name_ << endl;
    	}
    };
    
    using delPtr = void(*)(Person p[]);
    
    int main() {
    	unique_ptr<Person> ptr(new Person("秋天"));	
    	ptr.reset();
    	cout << "--------------------------------" << endl;
    	unique_ptr<Person> ptr2(new Person("冬天"));
    	ptr2.reset(new Person("春天"));
    	ptr2->sayName();
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33

    运行结果:
    在这里插入图片描述

  • 相关阅读:
    文本分类从入门到精通—代码展示
    [lesson60]数组类模板
    黑猫带你学Makefile第10篇:如何将未被编译的代码/自己写的驱动编译进uboot
    npm 实现原理
    日常问题: SQL优化
    国产低功耗MCU芯片:Si24R03
    leetcode(力扣) 509. 斐波那契数 (动态规划入门,模板代码)
    单例模式实现及防止反射与序列化
    安全!稳定!可信!选OceanBase就对了
    Vue源码学习(十九):router基本原理
  • 原文地址:https://blog.csdn.net/m0_51765966/article/details/127416289