C++的流库

1.流的概念

“流”,即“流动”的意思,是物质从一处向另一处流动的过程。在计算机这边通常是指对一种有序连续且具有方向性的数据的抽象描述。

C++ 中的流一般指两个过程的统一:

  • 信息从外部输入设备(键盘)向计算机内部(内存)输入
  • 计算机内部(内存)向外部输出设备(显示器)输出信息

这种输入输出的过程被形象的比喻为“流”,其具有有序性、连续性、方向性。

而为了实现这种流动,C++ 定义了 I/O 标准类库,每个类都称为流类。

2.C 中的流

C 语言中最常使用流输入/输出方式就是 scanf()/printf()

  • scanf():从标准输入设备(一般指键盘)读取字符数据,并将值通过指针和一定的格式存放在变量中

  • printf():将指定的字符数据通过宽度输出和精度输出控制,来输出到标准输出设备(一般指屏幕)

C 语言借助了相应的输入/输出缓冲区(缓冲区这里涉及到一点系统的知识,您可以去看看我的 Linux 系列博文)来辅助进行输入/输出。

而在实际场景中,fscanf()fprintf() 会更为实用。

输入/输出缓冲区的意义:

  1. 屏蔽低级 I/O 的实现:低级 I/O 的实现依赖于操作系统本身内核的实现(例如:LinuxIO 系统调用),如果能屏蔽这部分在系统调用上的差异,就更容易写出可移植的程序
  2. 实现“行”读取的行为:计算机没有“行”这个概念,有了缓冲区就可以定义出“行”的概念,读取一行就是指解析缓冲区的内容返回一个“行”

但是 C 语言的输入输出在面对对象和泛型上有些吃力,因此 C++ 新构建了流的类。

3.C++ 中的流

C++ 系统实现了庞大的流类库,其中 ios 为基类,其他类都是直接或间接派生自 ios 类。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

C++ 主要提供了三个流,分别为:

  1. IO 流,和控制台。终端就行流传递
  2. 文件流,和文件进行流传递
  3. 字符流,和字符间进行流传递

3.1.C++ 的 IO 流

补充:由于 IO 流的相关类继承关系比较复杂,这里只是泛泛而谈,有机会我再结合官方文档再出另外一份博文…

C++ 标准库提供了 4 个全局流对象 cincoutcerrclog

  • 使用 cin 进行标准输入(数据通过键盘输入到内存中)
  • 使用 cout 进行标准输出(数据从内存流向显示器)
  • 使用 cerr 进行标准错误输出
  • 使用 clog 进行日志输出

从类继承的图中可以看出:coutcerrclog 是同属于 ostream 类的三个不同的对象,因此这三个对象的内部原理基本没有区别,只是应用场景和一些细节不同,这里只点出一些细节问题:

  1. cin 将键盘输入的数据保存在缓冲区中,当要提取时,就从缓冲区中拿。如果一次输入较多,会留在缓冲区中慢慢使用,如果输入发生错误,必须在回车之前修改,如果回车键按下就无法挽回了。只有把输入缓冲区中的数据取完后才会要求输入新数据。

    而输入的数据类型与要提取的数据类型不一致时出错,出错仅仅在流的状态字 state 字段中将对应位进行置位操作(置为 1 即可),但程序依旧继续运行。

    //使用 cin 过程中出错并且处理的过程 
    #include <iostream> //streamsize
    #include <string>
    #include <limits> //numeric_limits
    
    using namespace std;
    
    int main()
    {
    	int number = 0;
    	cin >> number;
    	if (cin.fail()) //判断 cin 的状态,若 cin 为错误状态则返回 true, 正常状态则返回 false
    	{
    		cout << "发生错误" << endl;
    		cin.clear(); //清除cin的错误状态(重要)
    		in.ignore(numeric_limits<streamsize>::max(), '\n');  //忽略缓冲区中的所有字符,直到遇到换行符为止(重要)
    	
    		/* 
    		(1)std::numeric_limits 是一个模板类,用于获取各种数值类型(如整数、浮点数等)的特征信息 
    		(2)std::streamsize 是一个类型,用于表示输入/输出流操作的字节数或字符数的整数类型,通常情况下等同于 std::ptrdiff_t,也就是指针之间的差值类型
    		*/
    	}
    	else
    	{
    		cout << "number:" << number << endl;
    	}
    
    	//如果不清除状态,代码后续的 cin 都不会被执行,并且原先的输入数据仍然留在缓冲区中
    	//如果清除状态后,没有清除缓冲区,则原先留在缓冲区的数据会被 cin 继续读取
    	char character = 0;
    	cin >> character;
    	cout << "character:" << character << endl;
    	return 0;
    }
    
  2. 空格和回车都可以作为数据之间的分格符,所以多个数据可以在一行输入,也可以分行输入。但如果是字符型或字符串,则空格(ASCII 码为 32)无法用 cin 输入,字符串中也不能有空格,回车符也无法被读入(这里可以了解一下 C++ 的原始字符串)。

    //cin 无法直接获取空格字符和回车字符
    #include <iostream>
    #include <string>
    using namespace std;
    
    int main()
    {
    	string str;
    	cin >> str;
    	cout << "str:" << str; //输入空格字符和换行字符无效
    	return 0;
    }
    

    这也使得读取读取带有空格的长字符串成为困难,因此可以使用 getline() 替代 >> 输入:

    注意:getline 有两个版本,还挺乱的,待补充…

  3. cincout 可以直接输入和输出内置类型数据,这是因为标准库已经将所有内置类型的输入方法和输出方法全部重载了。而对于自定义类型,若要支持 cincout 的标准输入输出,需要对 <<>> 进行重载

  4. istream 类型对象可以转换为逻辑条件判断值。使用 while(cin >> i){/*...*/} 去流中提取对象数据时, 调用的是 istream& operator >>(),但其返回值是 istream 类型的对象,为什么这里可以直接做逻辑条件呢?

    这是因为 istream 对象自动调用了 operator bool()(类似隐式类型转载)。因此如果接收流失败,或者有结束标志,则可以返回转化后的 bool 类型,值为 false,正常读取则返回 true

    operator bool() 的本质是将自定义类型转化为内置类型,这是我们第一次遇到这种情况。以往我们都是:

    (1)内置类型转内置类型的(C++ 默认支持,是自动的)

    (2)内置类型转自定义类型的(通过构造函数)

    (3)自定义类型转自定义类型的(通过构造函数)

    补充:之前关于自定义类型转自定义类型的例子

    //通过构造函数来转化两个不相干的类型
    list<int> lt;
    list<int>::const_iterator it = lt.begin();
    

    但是没有尝试过自定义类型转化为内置类型(不过内置类型转化为自定义类型是有的,构造函数就可以做到,例如:std::string str = "limou"),这种行为默认不支持,即便用户使用强制类型转化也做不到。

    但用户可以借助 operator bool() 这来达到逆向转化的目的(类似的也有 operatou int() 等),该重载可以算作是关键字的重载,没有返回值,可以让用户有更为强大的转化方法。

    //自定义类型转为内置类型
    #include <iostream>
    using namespace std;
    
    class Data
    {
    public:
        operator int()
        {
            return _data2;
        }
    private:
        int _data1 = 10;
        int _data2 = 20;
    };
    int main()
    {
        Data d;
        int number = d;
        cout << number << '\n';
        return 0;
    }
    

    有这个重置也可以玩出一些比较“奇怪”的玩法:

    //通过自定义隐式转化来控制循环
    #include <iostream>
    using namespace std;
    
    class Data
    {
    	friend istream& operator>>(istream& in, Data& d);
    	friend ostream& operator<<(ostream& out, const Data& d);
    
    public:
    	operator bool() const
    	{
    		if (_data == 0)
    			return false;
    		else
    			return true;
    	}
    
    private:
    	int _data = 10;
    };
    
    istream& operator>>(istream& in, Data& d)
    {
    	in >> d._data;
    	return in;
    }
    
    ostream& operator<<(ostream& out, const Data& d)
    {
    	cout << "save data:" << d._data << '\n';
    	return out;
    }
    
    int main()
    {
    	Data d;
    	while (d == true) //如果用户的输入导致 _data 为 0,则停止循环
    	{
    		cin >> d;
    		cout << d;
    	}
    	return 0;
    }
    

    如果在该重载前加上 explicit (意思为“明确的、坦率地”)就不允许隐式类型转化,但允许强制类型转化。

    //通过自定义显式转化来控制循环
    #include <iostream>
    using namespace std;
    
    class Data
    {
    	friend istream& operator>>(istream& in, Data& d);
    	friend ostream& operator<<(ostream& out, const Data& d);
    
    public:
    	explicit operator bool() const //添加 explicit 防止隐式类型转化
    	{
    		if (_data == 0)
    			return false;
    		else
    			return true;
    	}
    
    private:
    	int _data = 10;
    };
    
    istream& operator>>(istream& in, Data& d)
    {
    	in >> d._data;
    	return in;
    }
    
    ostream& operator<<(ostream& out, const Data& d)
    {
    	cout << "save data:" << d._data << '\n';
    	return out;
    }
    
    int main()
    {
    	Data d;
    	//while (d == true) //这里会报错
    	//while ((bool)d == true) //这样写就不会报错
    	//while (bool(d) == true) //或者这样写也不会报错
    	while (static_cast<bool>(d) == true) //或者这样写也不会报错
    	{
    		cin >> d;
    		cout << d;
    	}
    	return 0;
    }
    

    需要注意的是,库内不是所有类都有这个重载的…

    补充:早期的 C++operator void*() const 来实现上述类似的转化…

  5. 关于 sync_with_stdio(bool sync = true) 的使用:在 C++sync_with_stdio(false) 是一种提升 cincout 效率的手段。

    尽可能在使用 cincout 时,调用成员函数 sync_with_stdio(false),因为 cincout 的输入输出缓冲区和 C 是两套缓冲区逻辑,而 C++ 又需要兼容 C,因此以下代码可以正常运行:

    //两套输入方法之间的兼容关系
    #define _CRT_SECURE_NO_WARNINGS 1
    #include <iostream>
    #include <cstdio>
    using namespace std;
    
    int main()
    {
    	int number = 0;
    
    	//下面两种输入互不不影响,等同于用两次 cin >> number 或 scanf("%d", &number)
    
    	cin >> number;
    	cout << number << '\n';
    
    	scanf("%d", &number);
    	printf("%d", number);
    
    	//前者输入兼容后者
    
    	return 0;
    }
    

    而调用成员函数 sync_with_stdio(false) 即可关闭这个兼容(告诉编译器不用考虑 C 语言标准库的输入输出流的兼容问题)。

    //使用 sync_with_stdio(false) 关闭兼容
    #include <iostream>
    using namespace std;
    
    int main()
    {
        cin.sync_with_stdio(false);
        cout.sync_with_stdio(false);
    
        int value;
        cin >> value;
        cout << value << endl;
    
        return 0;
    }
    

    而调用 sync_with_stdio(false) 后,scanf()printf()cincout 等混用将存在输入顺序/输出顺序与调用顺序不一致的问题(因为它们不再共享相同的缓冲区),且在代码中调用 sync_with_stdio(false) 属于不可逆操作。

    注意:我简单测试了一些平台,暂时没有遇到哪些调用顺序混乱的情况,以后遇到了再补充上来…

  6. 终止字符输入的方法主要有两种,比较暴力的就使用 [ctrl+c],而更为正常的操作是 [ctrl+z+enter],相当于流读取到结束标志。

3.2.C++ 的文件流

C++ 根据文件内容的数据格式分为二进制文件和文本文件。采用文件流对象操作文件的一般步骤:

  1. 定义一个文件流对象
    • ifstream ifile(只输入)
    • ofstream ofile(只输出)
    • fstream iofile(既输入又输出)
  2. 使用文件流对象的成员函数打开磁盘文件,使得文件流对象和磁盘文件之间建立联系
  3. 使用流提取运算符和流插入运算符对文件进行读写操作,或直接使用成员函数进行读写
  4. 关闭文件对象,释放资源(有析构函数的存在,因此可以不显示调用)
//文件流接口演示
#include <string>
#include <fstream>
#include <iostream>
using namespace std;

//描述数据的类
struct Data
{
	int _data1;
	int _data2;
	int _data3;
};

//描述服务器的类
struct Serverinfo
{
	char _address[32]; //地址
	//这里的 _address 不能使用 string 类型,否则二进制写入就回变成写入指向字符串的“指针信息”,而不是“字符串”本身(string 本身的成员有:指向字符串的指针、字符串的大小、存储的容量),
	//在 string 释放后就会导致指向字符串的指针变成“野指针”,因此读取的时候就会变成野指针解引用,这有可能导致程序奔溃(本质上就是浅拷贝问题)
	//(1)在同一个进程下,可能只是浅拷贝问题(输出结果有可能对)
	//(2)但是在两个进程的情况下,就必然会出现野指针解引用(写入的进程将指针指向的资源销毁了)
	//因此二进制读写的时候,最好不要用容器,否则很容易出现类似的问题
	//但是文本输入不会在这方面出问题,因为 string 对象会被提前转化为“字符串”后再写入,不会简单进行浅拷贝

	int _port; //端口
	Data _data; //数据
};

//描述操作的管理器
class ConfigManager
{
	/* 封装各种文件流的操作 */
public:
	ConfigManager(const char* fileName)
		: _fileName(fileName)
	{}

	void WriteBin(const Serverinfo& info)
	{
		ofstream ofs(_fileName, ofstream::out | ofstream::binary); //二进制写入(ofstream::out 可忽略,ofstream::binary 表示二进制操作)
		ofs.write((char*)&info, sizeof(info));
	}

	void ReadBin(Serverinfo& info)
	{
		ifstream ifs(_fileName, ofstream::in | ofstream::binary); //二进制读取(ofstream::in 可忽略,ofstream::binary 表示二进制操作)
		ifs.read((char*)&info, sizeof(info));
	}

	//文本写入/读取时,C 语言需要将数据不断转化为字符串再读写,但是 C++ 通过运算重载自动化了这个过程
	
	void WriteText(const Serverinfo& info)
	{
		ofstream ofs(_fileName); //文本写入,直接使用流操作符即可
		ofs << info._address << " "
			<< info._port << " "
			<< info._data._data1 << " "
			<< info._data._data2 << " "
			<< info._data._data3 << '\n';
	}

	void ReadText(Serverinfo& info)
	{
		ifstream ifs(_fileName); //文本读取,直接使用流操作符即可
		ifs >> info._address
			>> info._port
			>> info._data._data1
			>> info._data._data2
			>> info._data._data3;
	}

private:
	string _fileName;
};

int main()
{
	Serverinfo winfo = {
		"190.0.0.0", 80, {1, 2, 3}
	}; //服务器传递的信息

	//模拟程序 A(二进制读写)
	Serverinfo rinfo1; //读取服务器的信息
	ConfigManager cmb("limouBin"); //信息文件路径
	cmb.WriteBin(winfo); //写入信息
	cmb.ReadBin(rinfo1);
	cout << rinfo1._address << "-" << rinfo1._port << ":"
		<< rinfo1._data._data1 << "-"
		<< rinfo1._data._data2 << "-"
		<< rinfo1._data._data3 << '\n';

	//模拟程序 B(文本读写)
	Serverinfo rinfo2; //读取服务器的信息
	ConfigManager cmt("limouText.txt"); //信息文件路径
	cmt.WriteText(winfo); //写入信息
	cmt.ReadText(rinfo2);
	cout << rinfo2._address << "-" << rinfo2._port << ":"
		<< rinfo2._data._data1 << "-"
		<< rinfo2._data._data2 << "-"
		<< rinfo2._data._data3 << '\n';

	return 0;
}

补充:可以看到,写入文件的字符默认使用空格分割,但是有些时候我们需要输入带有空格的数据,这种情况可以了解一下 fstream 下的成员函数 getline(),这个接口和之前 IO 流的功能类似…

3.3.C++ 的字符流

C 中若想要将一个“自定义类型数据转化为字符串类型数据”或者“字符串类型数据转化为自定义类型数据”,有以下常见方式:

  • 使用 itoa()、使用 sprintf() 输出字符串数据
  • 使用 sscanf() 读取字符串数据

但都需要给出保存结果的空间大小,而空间的大小不容易界定,且转化格式不匹配时,还会得到错误的结果甚至是程序崩溃,因此 C++ 又设计了新的 sstream 头文件,该文件包含四个类:

  1. istringstream:进行流的输入
  2. ostringstream:进行流的输出
  3. stringstream:进行流的输入输出操作

我们主要介绍 stringstream,该类在其底层维护了一个 string 类型的对象用来保存结果。

注意:这个类用起来还是很不错的,推荐使用…

3.3.1.将数值类型数据格式化为字符串

//将数值类型数据格式化为字符串
#include <sstream> //stringstream
#include <iostream>
#include <string>
using namespace std;

int main()
{
    string str;
    stringstream ss;

    //将一个整形变量转化为字符串,存储到 string 类对象中
    int int_a = 114514;
    ss << int_a; //输入数据
    ss >> str; //输出数据

    cout << str << '\n';
    cout << ss.str() << '\n'; //str() 返回 stringsteam 中管理的 string 成员

    //stringstreams 在转换结尾时(即最后一次转换输出后),会将其内部状态设置为 badbit
    //因此如果我们需要多次转换,就必须使用 clear() 将上次转换状态清空掉,将状态重置为 goodbit 才可以转换
    
    ss.clear();

    //但是 clear() 不会将 stringstreams 底层字符串清空掉(第一次使用很容易误解 clean() 的作用)
    //如果不将 stringstream 底层管理 string 对象设置成 "",则多次转换时,就会将结果全部累积在底层 string 对象中
    
    ss.str("");

    //将一个浮点变量转化为字符串,存储到 string 类对象中
    double d = 3.14159;
    ss << d; //输入数据
    ss >> str; //输出数据

    cout << str << '\n';
    cout << ss.str() << '\n'; //str() 返回 stringsteam 中管理的 string 成员

    return 0;
}

3.3.2.字符串拼接

//字符串拼接
#include <sstream>
#include <iostream>
using namespace std;

int main()
{
	stringstream ss;
	//将多个字符串放入 sstream 中
	sstream << "first" << " " << "string,";
	sstream << " second string";
	cout << "strResult is: " << sstream.str() << endl;
	
	//清空 sstream
	sstream.str("");
	sstream << "third string";
	cout << "After clear, strResult is: " << sstream.str() << endl;
	return 0;
}

3.3.3.序列化和反序列化数据

//序列化和反序列化数据
#include <sstream>
#include <iostream>
using namespace std;

struct Date
{
    int _year = 2023;
	int _month = 12;
	int _day = 19;
};
istream& operator>>(istream& in, Date& d)
{
    return in >> d._year >> d._month >> d._day;
}
ostream& operator<<(ostream& out, const Date& d)
{
    return out << d._year << " " << d._month << " " << d._day;
}

struct ChatInfo
{
    string _name = "name";      //名字
    int _id = 0;                //id
    Date _date = { 2000,1,1 };  //时间
    string _msg = "msg";        //聊天信息
};

int main()
{
    //1.结构信息序列化为字符串
    ChatInfo winfo = { "limou", 114514, { 2023, 12, 19 }, "今天晚上吃什么?" };
    ostringstream oss;
    oss << winfo._name << " "
        << winfo._id << " " 
        << winfo._date << " "
        << winfo._msg;

    string str = oss.str();
    cout << str << '\n';
    //假设通过网络,将这个字符串发送给对象
    //一般会选用 JSON、XML 等方式进行更好的序列化支持(是描述信息用的文件)
    
    //2.字符串解析成结构信息
    ChatInfo rInfo;
    istringstream iss(str); //将字符传递给 iss
    iss >> rInfo._name >> rInfo._id >> rInfo._date >> rInfo._msg;
    cout << "-------------------------------------------------------" << '\n';
    cout << "姓名:" << rInfo._name << "(" << rInfo._id << ") " << '\n';
    cout << "时间:" << rInfo._date << '\n';
    cout << "消息:" << rInfo._name << ":>" << rInfo._msg << endl;
    cout << "-------------------------------------------------------" << '\n';
    return 0;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/340555.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

网页设计-用户体验

Use Cases (用例) 用例是用户如何在网站上执行任务的书面描述&#xff0c;从用户的角度描述了系统响应请求时的行为。每个用例都是用户实现目标的一系列简单的步骤。简言之&#xff0c;用例是一种用于描述系统如何满足用户需求的方法。 用例的好处 1. 明确需求&#xff1a; Use…

【测试入门】测试用例经典设计方法 —— 因果图法

01、因果图设计测试用例的步骤 1、分析需求 阅读需求文档&#xff0c;如果User Case很复杂&#xff0c;尽量将它分解成若干个简单的部分。这样做的好处是&#xff0c;不必在一次处理过程中考虑所有的原因。没有固定的流程说明究竟分解到何种程度才算简单&#xff0c;需要测试…

Flutter 滚动布局:sliver模型

一、滚动布局 Flutter中可滚动布局基本都来自Sliver模型&#xff0c;原理和安卓传统UI的ListView、RecyclerView类似&#xff0c;滚动布局里面的每个子组件的样式往往是相同的&#xff0c;由于组件占用内存较大&#xff0c;所以在内存上我们可以缓存有限个组件&#xff0c;滚动…

每天五分钟计算机视觉:掌握迁移学习使用技巧

本文重点 随着深度学习的发展,迁移学习已成为一种流行的机器学习方法,它能够将预训练模型应用于各种任务,从而实现快速模型训练和优化。然而,要想充分利用迁移学习的优势,我们需要掌握一些关键技巧。本文将介绍这些技巧,帮助您更好地应用迁移学习技术。 迁移学习的关键…

HCIP-BGP选路实验

一.实验拓扑图 二.详细配置 R1 interface GigabitEthernet0/0/0 ip address 12.1.1.1 255.255.255.0interface LoopBack0 ip address 1.1.1.1 255.255.255.0interface LoopBack1 ip address 10.1.1.1 255.255.255.0bgp 1 router-id 1.1.1.1 peer 12.1.1.2 as-number 2ipv4-fa…

Unity New Input System 及其系统结构和源码浅析【Unity学习笔记·第十二】

转载请注明出处&#xff1a;&#x1f517;https://blog.csdn.net/weixin_44013533/article/details/132534422 作者&#xff1a;CSDN|Ringleader| 主要参考&#xff1a; 官方文档&#xff1a;Unity官方Input System手册与API官方测试用例&#xff1a;Unity-Technologies/InputS…

c/c++公交管理系统(星穹轨道可视化EasyX)

注&#xff1a;全代码由博主个人耗时两个星期开发&#xff0c;实现了基础的公交管理系统功能&#xff1a;1.前端用户查询站点以及查看所有站点。2.后端管理员权限&#xff0c;实现登录系统检验&#xff0c;添加路线以及删除路线。最重要的是使用EasyX实现了星穹轨道的启动以及抽…

ROS2手册的离线编译安装

ROS开发中经常要查询相关API&#xff0c;把文档下载到本地离线使用方便快捷&#xff0c;极大提高开发效率 下载ROS2文档 git clone https://github.com/ros2/ros2_documentation.gitcd ros2_documentation安装sphinx pip install Sphinx配置sphinx sphinx-quickstart按提示…

快速上手的AI工具-文心辅助学习

前言 大家好晚上好&#xff0c;现在AI技术的发展&#xff0c;它已经渗透到我们生活的各个层面。对于普通人来说&#xff0c;理解并有效利用AI技术不仅能增强个人竞争力&#xff0c;还能在日常生活中带来便利。无论是提高工作效率&#xff0c;还是优化日常任务&#xff0c;AI工…

【Linux】vim的使用

个人主页 &#xff1a; zxctsclrjjjcph 文章封面来自&#xff1a;艺术家–贤海林 如有转载请先通知 目录 1. 前言2. vim的基本概念3. vim的基本操作4. vim正常模式命令集4.1 命令模式4.1.1 光标定位 4.2 插入模式4.3 底行模式 这个是在网上找的一张关于vim键盘的图 1. 前言 …

爬虫requests+综合练习详解

Day2 - 1.requests第一血_哔哩哔哩_bilibili requests作用&#xff1a;模拟浏览器发请求 requests流程&#xff1a;指定url -> 发起请求 -> 获取响应数据 -> 持续化存储 爬取搜狗首页的页面数据 import requests# 指定url url https://sogou.com # 发起请求 resp…

Netty和Reactor设计模式

Netty Netty一个线程通过多路复用&#xff0c;能够实现多个socket的非阻塞的调用。 Reactor Reactor 模式是一种设计模式,也叫响应器模式。 以下是 Reactor 模式的基本组成部分&#xff1a; 事件处理器&#xff08;Event Handlers&#xff09;&#xff1a; 这些是实际处理特…

【深度学习目标检测】十七、基于深度学习的洋葱检测系统-含GUI和源码(python,yolov8)

使用AI实现洋葱检测对农业具有以下意义&#xff1a; 提高效率&#xff1a;AI技术可以快速、准确地检测出洋葱中的缺陷和问题&#xff0c;从而提高了检测效率&#xff0c;减少了人工检测的时间和人力成本。提高准确性&#xff1a;AI技术通过大量的数据学习和分析&#xff0c;能够…

Simulink之Signal

Simulink.Signal 指定信号的属性 描述 此类使您能够创建工作区对象,用于分配或验证信号或离散状态的属性,如其数据类型、数字类型、维度等。您可以使用信号对象来: 将值指定给信号源未指定的信号属性(值为-1或auto)。 验证其值由信号源显式指定的信号属性。此类属性的…

Windows系统如何修改Nginx配置实现远程访问多个本地站点

文章目录 1. 下载windows版Nginx2. 配置Nginx3. 测试局域网访问4. cpolar内网穿透5. 测试公网访问6. 配置固定二级子域名7. 测试访问公网固定二级子域名 1. 下载windows版Nginx 进入官方网站(http://nginx.org/en/download.html)下载windows版的nginx 下载好后解压进入nginx目…

IDEA配置Maven

打开idea选择Configure-->Settings 配置Mavne的路径信息 创建一个Maven项目 创建完成后默认会打开我们的pom.xml文件 添加一个junit的maven 分享一个maven资源的下载网址 Maven Repository: Search/Browse/Explore (mvnrepository.com) 打开网址 选中它进入首页 搜索jun…

Go新项目-Gin中wire的依赖注入方式实战(6)

选型Go项目过程中&#xff0c;针对依赖注入方式的分析和使用 参考资料 https://go.dev/blog/wirehttps://medium.com/dche423/master-wire-cn-d57de86caa1bhttps://toutiao.io/posts/et0t2lk/previewhttps://imlht.com/archives/223/https://lailin.xyz/post/go-training-week…

右值引用和move语义

文章目录 一、左值和右值二、左值引用和右值引用三、move语义四、右值引用的作用1.左值引用的作用和局限2.右值引用的作用3.重谈std::move()4.完美转发 一、左值和右值 在《C Primer Plus》书中这样提到左值&#xff1a;左值是用于标识或定位存储位置的标签。 对于早期的 C 语…

Kafka框架详解

Kafka 1、Kafka介绍 ​ Kafka是最初由linkedin公司开发的&#xff0c;使用scala语言编写&#xff0c;kafka是一个分布式&#xff0c;分区的&#xff0c;多副本的&#xff0c;多订阅者的消息队列系统。 2、Kafka相比其他消息队列的优势 ​ 常见的消息队列&#xff1a;Rabbit…

【设计模式】张一鸣笔记:责任链接模式怎么用?

我将通过一个贴近现实的故事——请假审批流程&#xff0c;带你了解和掌握责任链模式。 什么是责任链模式&#xff1f; 责任链模式是一种行为设计模式&#xff0c;它让你可以避免将请求的发送者与接收者耦合在一起&#xff0c;让多个对象都有处理请求的机会将这个对象连成一条…