《21天学通C++》[美] —全文笔记(下) 潦草版

第17 章 智能指针

  17.1 智能指针的实现

        智能指针是包含重载运算符的类,用法类似普通指针,优点是及时安全地销毁动态分配的数据,并实现明确的对象声明周期,使智能指针“智能”的是:复制构造函数、赋值运算符、析构函数,它们决定了智能指针对象被传递给函数、赋值、离开作用域时的行为。

        把指针封装在类中,给指针添加拷贝构造、赋值运算、析构,确保指针安全

class human{
public:
	int age;
	human(int a = 0) :age(a){}
	void print()
	{
		cout << "Hello human:" << age << endl;
	}
};
template<typename T>
class smart_pointer
{
private:
	T* ptr;
public:
	smart_pointer(T* p) :ptr(p){}		//构造
	~smart_pointer(){ delete ptr; }		//析构
	smart_pointer(const smart_pointer& obj);//拷贝构造
	smart_pointer& operator=(const smart_pointer& obj);//赋值运算符
	T& operator*() const	//解引用运算符,重载
	{
		return *ptr;
	}
	T* operator->() const	//成员选择运算符,重载
	{
		return ptr;
	}
};
smart_pointer<human> p(new human);//将smart_pointer数据成员ptr 指向human对象
p->print();		//成员选择->被重载,返回ptr,其指向human对象
p->age = 100;
(*p).print();	//解引用*被重载,返回*ptr

  17.2 智能指针在复制和赋值时的问题

  •     指针共享(多个指针指向同以对象)

        1、浅复制:直接复制指针,多次释放存在,悬挂指针

        2、引用计数:直接复制指针,使用引用计数,多次释放安全,如(shared_ptr)

        3、深复制:复制一份指向的对象,影响效率

  •     指针独享(一个对象只能有一个指针)

        4、破坏性复制:复制和赋值可使用,复制后原指针破坏,如(auto_ptr)

        5、禁止复制:复制和赋值私有不可调用,可用移动构造函数转移控制权,如(unique_ptr)

  17.2 浅复制

        多个指针指向同一对象,当多次析构时,发生错误!

template<typename T>
class smart_pointer
{
private:
	T* ptr;
public:
	smart_pointer(T* p) :ptr(p){}		//构造
	~smart_pointer(){ delete ptr;}		//析构
	T& operator*() const	//解引用运算符,重载
	{
		return *ptr;
	}
	T* operator->() const	//成员选择运算符,重载
	{
		return ptr;
	}
};
smart_pointer<human> p(new human);//将smart_pointer数据成员ptr 指向human对象
smart_pointer<human> p1(p);
//当析构时,多次释放

  17.3 引用计数 shared_ptr(weak_ptr)

        1、引用计数是对浅复制与深复制的折中操作,使用引用计数的规则如下:

                1、创建智能指针,初始化指针

                2、拷贝构造函数,复制指针,引用计数+1

                3、复制操作符,先使左操作数引用计数-1,如果-1后为0,则释放指针所指对象内存;然后右操作数引用计数+1

        2、通过辅助类模拟实现shared_ptr:

template<typename T>class smart_ptr;  //模板类作为友元时要先声明
template <typename T> class refptr    //辅助类
{//成员全部私有
private:
	T* p;			//对象指针
	int count;		//计数
	refptr(T* ptr) :p(ptr), count(1){}	//构造函数,count初始化为1
	~refptr(){ delete p; }				//析构函数
	friend class smart_ptr<T>;          //定义智能指针为友元类,智能指针需要直接操作辅助类
};
template<typename T>class smart_ptr    //智能指针类
{
private:
	refptr<T>* ptr;
public:
	smart_ptr(T* p) :ptr(new refptr<T>(p)){};    //构造函数
	smart_ptr(const smart_ptr<T> &p) :ptr(p.ptr){ ++ptr->count; }    //复制构造函数
	smart_ptr& operator=(const smart_ptr<T> &p)    //赋值运算符
	{
		++p.ptr->count;    //首先将右操作数引用计数+1
		if (--ptr->count == 0)    //左操作数被赋值,因此左操作数的指针-1,当count为0是删除
			delete ptr;
		ptr = p.ptr;
		return *this;
	}
	T& operator*()
	{
		return *(ptr->p);
	}
	T* operator->()
	{
		return ptr->p;
	}
	~smart_ptr()		//析构函数
	{
		if (--ptr->count == 0)    //count为0时,删除辅助类对象指针
			delete ptr;
		else
		{
			cout << "还有 " << ptr->count << "个指针指向基础对象" << endl;
		}
	}
};
int main()
{
	int *p1 = new int(10);//基础类对象指针
	{
		smart_ptr<int>p2(p1);    //count=1
		cout << *p2 << endl;
		{
			smart_ptr<int>p3(p2);    //count=2
			cout << *p3 << endl;
			{
				smart_ptr<int>p4 = p3;    //count=3
				cout << *p4 << endl;
			}    //count=2
		}    //count=1
	}    //count=0
	cout << *p1 << endl;
	return 0;
}

        3、weak_ptr

        weak_ptr配合shared_ptr使用,weak_ptr只对shared_ptr进行引用,不对其进行引用计数,shared_ptr失效时,weak_ptr也失效,不能直接通过weak_ptr来进行访问资源,访问资源时,使用lock()生成一个shared_ptr

        weak_ptr可以用于解决循环引用问题

1、weak_ptr基本用法:	
    weak_ptr<T> w;	//创建空weak_ptr
	w = p;			//p可以是shared_ptr或weak_ptr
	w.reset();		//将w置空
	w.use_count();	//返回shared_ptr的计数
	w.expired();	//若w.use_count()为0,返回true,否则false
	w.lock();		//若expired为true,返回空shared_ptr,否则返回非空shared_ptr
2、使用例子:
	shared_ptr<int>p1(new int(10));
	weak_ptr<int>p2(p1);			//使用shared_ptr初始化weak_ptr
	cout << p2.use_count() << endl;	//使用use_count()获取shared_ptr的计数,此时为1
	if (!p2.expired())				//
	{
		shared_ptr<int>p3 = p2.lock();    //使用lock()生成一个shared_ptr
		cout << p2.use_count() << endl;//计数为2
		*p3 = 100;
	}
	cout << p2.use_count() << endl;//计数为1
	cout << *p1 << endl;

  17.4 深复制

        在智能指针复制时,将对象复制一份

class human{                //类
public:
	int age;
	human(int a = 0) :age(a){}
	void print()
	{
		cout << "Hello human:" << age << endl;
	}
};
template<typename T>
class smart_pointer
{
private:
	T* ptr;
public:
	smart_pointer(T* p) :ptr(p){}		//构造
	~smart_pointer(){ delete ptr;}		//析构
	smart_pointer(const smart_pointer& obj)//拷贝构造
	{
		ptr = new T;			//重新new一个对象
		ptr->age = obj.ptr->age;//copy值
	}
	smart_pointer& operator=(const smart_pointer& obj)//赋值运算符
	{
		if (ptr != obj.ptr)
		{
			ptr = new T;			//重新new一个对象
			ptr->age = obj.ptr->age;//copy值
		}
		return (*this);
	}
	T& operator*() const	//解引用运算符,重载
	{
		return *ptr;
	}
	T* operator->() const	//成员选择运算符,重载
	{
		return ptr;
	}
};
smart_pointer<human> p(new human);    
smart_pointer<human> p1(p);        //复制
p1 = p;                            //赋值运算

  17.5 破坏性复制 auto_ptr

        智能指针在被复制时,将对象的所有权转交给目标指针,并重置原来的指针,确保任何时刻只有一个活动指针指向对象,适合从函数返回指针或需要破坏性的情况

//基于破坏性的智能指针实现:
template <typename T>
class destruct{
private:
	T* ptr;
public:
	destruct(T* p) :ptr(p){}//构造
	~destruct(){ delete ptr; }//析构

	destruct(destruct& obj)
	{
		ptr = obj.ptr;        //传递指针
		obj.ptr = 0;          //破坏原指针
	}
	destruct& operator=(destruct& obj)
	{
		if (ptr != obj.ptr)
		{
			delete ptr;
		}
		ptr = obj.ptr;        //传递指针
		obj.ptr = 0;          //破坏原指针
	}
};
destruct<int>num(new int);
destruct<int>copy = num;	//原指针将失效

  17.6 禁止复制 unique_ptr

        unique_ptr的拷贝构造函数与赋值运算符是私有的,因此不能按值传递,只能按引用传递,C++11引入的delete关键字:强制默认拷贝构造和赋值重载无法被生成。

        1、unique_ptr基本操作:

	unique_ptr<int> num1;		    //创建空智能指针
	unique_ptr<int> num2(new int(1));//创建时指定
	num1.reset(new int(10));		//reset()重新指定

	int *p = num1.release();	    //release()释放所有权
	cout << *p << endl;

	num2.reset(num1.release());	//转移所有权
	cout << *num2 << endl;

	num2 = move(num1);			//所有权转移
	cout << *num2 << endl;
	num2.reset();				//清空智能指针,等价num2 = nullptr
	num2 = nullptr;

        2、unique_ptr禁止复制和赋值操作: 

#include<memory>
class human{
public:
	void print()
	{
		cout << "human" << endl;
	}
};
void func(const unique_ptr<human>& p)
{
	p->print();
}
unique_ptr<human> num1(new human);
func(num1);		//可以使用引用参数形式
unique_ptr<human> num2(new human);
num2 = num1;	//无法使用赋值运算符

       3、如果unique_ptr是个临时右值,编译器允许拷贝:

unique_ptr<int> func(int num)
{
	unique_ptr<int> temp(new int(num));//创建临时unique_ptr
	return temp;
}
unique_ptr<int> n;
n = func(10);	//func返回一个临时的unique_ptr

         4、unique_ptr扩展auto_ptr不能完成的功能:

	//1、unique_ptr可放在容器中
	vector <unique_ptr<string>> num{ new string("Hello"), new string("World") };
	vector <unique_ptr<string>> num1;
	//2、管理动态数组,重载版本:unique_ptr<X[]>
	unique_ptr<int[]> num2(new int[3]{11, 22, 33});
	num2[0] = 111;
	//3、自定义资源删除操作(delete)

第18 章 使用流进行输入输出

1、常见流类:
    cout		//标准输出流
	cin			//标准输入流
	cerr		//用于显示错误的标准输出流
	fstream		//用于操作文件的输入输出流,继承了ofstream和ifstream
	ofstream	//用于操作文件的输出流,
	ifstream	//用于操作文件的输入流,
	stringstream//用于操作字符串的输入输出流,在字符串和其它类型中进行转换
2、流控制符:
	setbase			//设置基数,与使用dec、oct、hex等价
	dec				//十进制
	oct				//八进制
	hex				//十六进制
	setiosflag		//通过类型为std::ios_base::fmtflags的掩码输入参数设置标志
	resetiosflags	//将setiosflag指定的参数重置为默认参数
	setprecision	//设置小数精度
	fixed			//以定点表示法显示数据
	scientific		//以科学计数法显示数据
	setw			//设置字段宽度
	setfill			//设置填充字符

  18.1 cout

        std::out用于将数据写入标准输出流

1、修改显示格式:
	int input = 0;
	cin >>input;//setiosflags()设置格式,参数1:八进制,参数2:显示基数,参数3:大写				
	cout << setiosflags(ios_base::hex | ios_base::showbase | ios_base::uppercase);
	cout << oct << input << endl;	//oct设置八进制
	cout << hex << input << endl;	//hex设置十六进制,使用setiosflags设置的格式
	cout << resetiosflags(ios_base::hex | ios_base::showbase | ios_base::uppercase);
	cout << input << endl;//resetiosflags()重置setiosflags()设置的格式
2、以定点表示法和科学计数法显示数据:
	const double Pi = (double)22.0 / 7;
	cout << Pi << endl;							//正常输出double
	cout << setprecision(10) << Pi << endl;		//设置小数精度
	cout << fixed << Pi << endl;				//设置定点表示法输出
	cout << scientific << Pi << endl;			//设置科学计数法输出
3、字段宽度和填充
	cout << setw(25);					//设置字段宽度
	cout << Pi << endl;
	cout << setw(25) << setfill('*');	//设置填充字符
	cout << Pi << endl;

  18.2 cin

        使用cin将标准输入读取到给定的变量中

1、基本输入:
    int num1;
	cin >> num1;		//输入
	cout << num1 << endl;
	double num2;
	cin >> num2;		//输入
	cout << setprecision(10)<<num2<<endl;
	char s1, s2, s3;
	cin >> s1 >> s2 >> s3;	//输入
	cout << s1 << s2 << s3 << endl;
2、字符串读入:
    char str[10];
	cin >> str;				//输入字符串,可越界
	cin.get(str, 10);		//get()限制读入的数量
	cout << str << endl;

	string name;
	cin >> name;			//读入空格停止插入
	cout << name << endl;	//
	getline(cin, name);		//可读入空格
	cout << name << endl;

  18.3 fstream

1、打开open与关闭close
    文件打开模式:
        ios_base::app;		//追加
	    ios_base::ate;		//切换到文件尾
	    ios_base::trunc;	//入锅文件存在则删除再创建
	    ios_base::binary;	//创建二进制文件,(默认文本文件)
	    ios_base::in;		//只读
	    ios_base::out;		//只写
    fstream file;		    //文件可读可写
	//ofstream file1;		//文件只写
	//ifstream file2;		//文件只读
    file.open("1.txt", ios_base::in | ios_base::out |ios_base::trunc);//打开文件
    if (file.is_open())	//判断文件是否打开成功
	{	
        file.close();       //关闭文件	
    }
2、文件读写
    file << "Hello World"<<endl;	    //写入文件:插入运算符<<进行文件写入
    string str;
	char s[100];
	char c;
	file >> str;						//读方式1:读入空格时会终止
	getline(file, str);					//读方式2:读入一行
	file.getline(s, 100);				//读方式3:读入一行
	while ((c = file.get()) != EOF)		//读方式4:循环读取字符
3、文件指针
    istream的seekg("seek get")和ostream的seekp("seek put")
	ios::beg;文件头,默认
	ios::cur;文件当前位置
	ios::end;文件尾
    file.seekp(0, ios::beg);	//将文件指针移动到文件头
4、二进制文件读写
        打开方式:ios_base::binary
        二进制读:read
        二进制写:write
    struct human{
	    string name = "wang";
	    int age = 20;
	    double score = 99.99;
    };
    human p;    //构造函数打开,二进制格式
	fstream file("1.dat", ios_base::in | ios_base::out | ios_base::binary);
	if (file.is_open())
	{
		file.write(reinterpret_cast<char*>(&p),sizeof(p));    //强制转换类型
		human h;		
		file.read((char*)&h, sizeof(h));            //强制转换类型
		cout << h.name << endl;
		cout << h.age << endl;
		cout << h.score << endl;
		file.close();
	}

  18.4 stringstream

        stringstream,可以用分割字符串或类型转换,例如:将字符串值100转为整数值100,或者将整数100转为字符串

1、stringstream类型转换:
	#include<sstream>
    int num1 = 100,num2=0;
	string str;
	stringstream int_to_str, str_to_int;
	int_to_str << num1;		//int放入stringstream对象
	int_to_str >> str;		//转给string
	cout << str << endl;
	str_to_int << str;		//str放入stringstream对象
	str_to_int >> num2;		//转给int
	cout << num2 << endl;

第19 章 异常处理

  19.1 使用try和catch捕获异常

        1、使用catch块对try块可能引发的异常进行处理:

#include<iostream>
using namespace std;
int main()
{
	try        //try块
	{
		int n;
		cin >> n;
		int* p = new int[n];//输入-1,分配内存失败,异常类型为std::bad_alloc
		delete[] p;
	}
	catch (...)    //catch块,发生异常catch将捕获
	{
		cout << "error!" << endl;
	}
	return 0;
}

        2、捕获特定类型异常:

int main()
{
	try
	{
		int n;
		cin >> n;
		int* p = new int[n]; 
		delete[] p;
	}
	catch(bad_alloc& exp)    //捕获bad_alloc异常
	{
		cout<<exp.what()<<endl;
	}
	catch (...)
	{
		cout << "error!" << endl;
	}
	return 0;
}

        3、使用throw引发特定类型的异常:

double divide(double a,double b)// 功能:a/b
{
	if(b == 0)
		throw "除数不能为0!";	//字符串 
	return a/b; 
}
int main()
{
	try
	{
		cout<<divide(10,0);
	}
	catch(const char* exp)		//捕获类型为char*的异常 
	{
		cout<<exp<<endl;
	}
	return 0;
}

  19.2 异常处理的工作原理

        1、异常逻辑工作原理

        try引发异常后,先在函数内寻找能处理该异常的catch,没有则将在调用函数中进行寻找,异常逻辑沿调用栈向上逐个寻找,直到找到可处理异常的catch,在退栈的每一步中,都将销毁当前函数的局部变量。不要再析构函数中引发异常!

#include<iostream>
#include<string>
using namespace std;
struct structA
{
	string name;
	structA(const string str) :name(str){ cout << name << ": structA 构造" << endl; }
	~structA(){ cout << name << ": structA 析构" << endl; }
};
struct structB
{
	string name;
	structB(const string str) :name(str){ cout << name << ": structB 构造" << endl; }
	~structB(){ cout << name << ": structB 析构" << endl; }
};
void func_B()
{
	cout << "---- At func_B ----" << endl;
	structA objA("func_B");
	structB objB("func_B");
	throw "Throwing!";
	cout << "---- End func_B ----" << endl;
}
void func_A()
{
	structA objA("func_A");
	try
	{
		cout << "---- At func_A ----" << endl;
		structA objA("func_A");
		structB objB("func_A");
		func_B();
		cout << "---- End func_A ----" << endl;
	}
	catch (const char* exp)
	{
		cout << "func_A catch:" << exp << endl;
		//throw;		将异常传播给func_A的调用者,main也会收到该异常
	}
}
int main()
{
	try
	{
		func_A();
	}
	catch (const char* exp)
	{
		cout << "main catch:" << exp << endl;
	}
	system("pause");
	return 0;
}

  19.3 从std::exception派生出自定义异常类

        在捕获std::bad_alloc时,实际上捕获的是new引发的std::bad_alloc对象,bad_alloc继承自exception类,std::exception类是异常基类,定义了虚函数what(),exception类是很多异常类型的基类,因此可以使用catch(const exception&)捕获所有将exception作为基类的异常。

    #include<exception>
    try
    {
    }
	catch (const exception& exp)
	{
		cout << exp.what() << endl;
	}

        派生出自定义异常类:

class myexception :public exception
{
private:
	string reason;
public:
	myexception(const char* why) :reason(why){}//构造函数
	virtual const char* what()const throw()	//实现虚函数what() 最后的throw()表示这个函数不会引发异常
	{											 //throw(int)表示该函数可能引发类型为int的异常
		return reason.c_str();
	}
};
double divide(double a, double b)
{
	if (b == 0)//除数为0
		throw myexception("myexception: divide by 0");	//此时实例化一个对象,并引发它
	return a / b;
}
int main()
{
	try
	{
		cout << divide(10, 0);
	}
	catch (exception& exp)
	{
		cout << exp.what() << endl;
	}
	return 0;
}

第20 章 C++20

  20.1 概念

        1、使用模板进行泛型编程时,模板类可支持不同类型的属性,调用模板函数时可提供不同类型的参数,具体取决于实例化。

template <typename T>
double divide(T a,T b)
{
	return a/b;
}
divide(22.0,7.0);		//正常情况 
divide("abc","123");	//编译错误!

         2、编译器报告错误,改进后编译器拒绝编译:

template <floatint_point T>    //floatint_point是一个概念,约束T的类型为浮点型
double divide(T a,T b)
{
	return a/b;
}

        3、标准库提供的一些重要概念: 

概念描述
integral验证整数
signed_integral验证有符号整数 
unsigned_integral验证无符号整数 
floatint_point验证浮点数 
same_as验证两种类型相同 
derived_from验证一种类型是从另一种类型派生的 
convertible_to验证一种类型可转换为另一种类型

        4、使用关键字requires自定义概念

#include<iostream>
#include<concepts>
using namespace std;
template <typename T>
concept myconcept = floating_point<T> || integral<T>;	//自定义概念myconcept,整形或浮点型 

template <typename T>	//integral概念 限制整形 
requires integral<T>	//对T的类型进行检查,integral限制整型 
double divide1(T a,T b)
//requires integral<T>	//可放在这里 
{
	return a/b;
}

template <typename T1,typename T2>	//两个参数类型为整形或浮点型,两者类型可以不同 
requires myconcept<T1> && myconcept<T2>
double divide2(T1 a,T2 b)
{
	return a/b;
}

template <typename T1,typename T2>	//两个参数类型相同 
requires same_as<T1,T2>
double divide3(T1 a,T2 b)
{
	return a/b;
}

int main()
{
	cout<<divide1(22,7)<<endl;		//OK
	cout<<divide1(22.0,7)<<endl;	//error,限定整型
	
 	cout<<divide2(22.0,7)<<endl;	//OK
	cout<<divide2(22.0,7.0)<<endl;	//OK
	cout<<divide2(22.0,'7')<<endl;	//OK
	cout<<divide2("22.0",'7')<<endl;//error,限定整型,浮点型 
	
	cout<<divide3(22,7)<<endl;		//OK
	cout<<divide3(22.0,7.0)<<endl;	//OK
	cout<<divide3(22.0,7)<<endl;	//error,类型不相同 
	return 0;
}

        5、将概念用于类和对象

template <typename T>
concept myconcept = floating_point<T> || integral<T>;	//自定义概念 myconcept,整形或浮点型 

template<myconcept T1,myconcept T2>		//使用自定义概念myconcept进行类型限制 
class human
{
public:
	T1 age;
	T2 score;
	human(T1 num1, T2 num2):age(num1),score(num2){}	
}; 
int main()
{
	human<int,double>p1(21,3.14);
	human<double,float>p2(32,3.14f);
	human<string,double>p3("wang",3.14);//error
	return 0;
}

         6、使用概念derived_from验证继承关系

class human{}; 
class student:public human{};
class teacher:public human{};
class monkey:private human{}; 
class dog{};

template<derived_from<human> T>
void func(T& input)
{
	cout<<"HELLO WORLD"<<endl;
}
int main()
{
	student s;
	func(s); //OK
	teacher t;
	func(t);//OK
	
	dog d;
	func(d);//error 非继承human
	
	int n=10;
	func(n); //其它类型不行 
	
	monkey m;
	func(m);//error	 私有继承human
	
	return 0;
}

  20.2 范围库、视图和适配器

        1、范围

        范围是对集合的抽象,支持begin()和end()方法的集合都是范围,例如:vector、list、set、map等容器属于范围,stack、forward_list等不满足 begin()和end()方法的不属于范围 。

        2、视图和适配器

        视图是复制、移动和赋值时间固定的范围,是一种特殊的范围,所有视图都是范围,但范围不一定都是视图。

        适配器是算法,使用适配器 创建集合 的各种视图。

        STL提供的常用适配器:

适配器描述
std::views::reverse反序查看集合中的元素
std::views::all查看集合中所有元素
std::views::filter(p)只查看满足谓词p的元素
std::views::drop(n)查看集合中元素,忽略前n个的元素
std::views::take(n)查看前n个元素
std::views::transform(f)查看函数f作用于范围后的各个元素的结果

        3、按照相反顺序查看集合的视图

vector<int> num{1,5,202,-99,42,50}; 
auto view_reverse = num | views::reverse;	//使用适配器reverse创建一个名为view_reverse的视图 
for(int n:num)				//基于范围遍历 
	cout<<n<<endl; 
for(int n:view_reverse)		//视图view_reverse属于范围 
	cout<<n<<endl;

        3、使用适配器生成集合的视图

template<ranges::view T>	//使用概念ranges::view限制模板类型
void display(T& view)
{
	for(auto element:view)
		cout<<element<<endl;
	cout<<endl;
} 
int main()
{
	vector<int>num{1,5,202,-99,42,50};
	auto view_all = num | views::all;	//1、适配器:views::all 
	display(view_all);
	
	auto view_filter = num | views::filter([](auto num){return ((num%2)==0);}); 
	display(view_filter);			//2、适配器:views::filter(只查看满足谓词的元素 ) 
	
	auto view_drop = num | views::drop(2);//3、适配器:views::drop 
	display(view_drop);
	
	auto view_take = num | views::take(3);//4、适配器:views::take 
	display(view_take);
	
	auto view_reverse = num | views::reverse;//5、适配器:views::reverse 
	display(view_reverse);

	auto view_transform = num | views::transform([](auto num){return (num%2);});
	display(view_transform);//6、适配器:views::transform(返回函数对每个元素作用的结果) 
	

        4、使用管道符号 ( | ) 合并多个适配器

template<ranges::view T>	//使用概念ranges::view限制模板类型
void display(T& view)
{
	for(auto element:view)
		cout<<element<<" ";
	cout<<endl;
} 
int main()
{
	vector<int>num{1,5,202,-99,42,50};
	
	auto lambda_even = [](auto num){return ((num%2)==0);};//lambda
	auto view1 = num | views::reverse | views::filter(lambda_even);
	display(view1);
	
	auto view2 = num | views::reverse | views::filter(lambda_even) | views::take(2);
	display(view2);
	
	return 0;
}

  20.3 多线程

        不同操作系统的系统调用接口不同,线程API也不同,C++多线程封装后,可在不同平台使用统一接口,实现可移植性,C++20引入协程。

#include<thread>
#include<iostream>
using namespace std;
void thread_func(stop_token stop)
{
	while(true)
	{
		cout<<"Worker thread: hello"<<endl;
		this_thread::sleep_for(1s);	//等1s 
		if(stop.stop_requested())	//接收停止信号 
		{
			cout<<"Worker thread: ending"<<endl;
			break;
		}
	}
}
int main()
{
    //jthread类
	jthread myThread(thread_func);//thread_func作为构造函数的参数 
	this_thread::sleep_for(5s);	// this_thread类,成员函数sleep_for()使当前线程挂起
	myThread.request_stop();	//让线程停止 
	cout<<"Main thread: Waiting"<<endl;
	myThread.join();			//等待线程停止 
	cout<<"Main thread: yes"<<endl;
	return 0;
} 

  20.4 模块

        #include<header>头文件问题:

        1、重复包含多个文件

        2、展开后代码量增大,包含与当前编译程序无关的代码,增加编译器负担

        3、减慢编译器速度,可能导致重复定义错误

        4、包含的顺序很重要

import std.core;	//模块core包括iostream的函数,取代#include<iostream>
int main()
{
	std::cout << "Hello World!" << std::endl;
	return 0;
}
//1、模块声明:(文件mymodule.ixx):
export module mymodule;			//声明模块名mymodule,export表示外部可见
export int add(int a, int b);	//模块中的函数add(),export表示外部可见
//2、模块实现:(文件mymodule.cpp)
module mymodule;
int add(int a, int b)
{
	return a + b;
}

//3、模块使用:(文件main.cpp)
import mymodule;
int main()
{
	int sum = add(10, 10);		//使用模块中的函数add()
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值