STL —— string(3)

目录

1. 使用

1.1 c_str()

 1.2 find() & rfind()

1.3 substr()

1.4 打印网址的协议域名等

1.5 find_first_of()

2. string() 模拟实现

2.1 构造函数的模拟实现

2.2 operator[] 和 iterator 的模拟实现

2.3 push_back() & append() & += 的模拟实现

2.4 insert() & erase() 的模拟实现

2.5 resize() 模拟实现

2.6 析构函数和复制重载模拟


大部分的用法我在上一篇已经讲解过了,这边文章主要讲解还有的部分用法以及string类的模拟实现。

1. 使用

1.1 c_str()

  • c_str() 用于返回一个指向以 null 结尾的 C 字符串(即以 null 终止的字符数组)的指针,该字符串包含了 std::string 对象的内容。
void string_test1()
{
    std::string str("Hello, world!");

    // 使用 c_str() 返回指向以 null 结尾的 C 字符串的指针
    const char* cstr = str.c_str();

    // 打印返回的 C 字符串
    std::cout << "C string: " << cstr << std::endl;
}

 1.2 find() & rfind()

find 函数用于在字符串中查找子串。find 函数有多个重载版本,可以根据需要进行使用。主要的两个版本如下:

  1. size_t find(const string& str, size_t pos = 0) const; 这个版本的 find 函数在当前字符串中从位置 pos 开始查找子串 str,并返回找到的子串的第一个字符的位置索引。如果未找到子串,则返回 string::npos

  2. size_t find(const char* s, size_t pos = 0) const; 这个版本的 find 函数与上一个版本类似,但接受一个 C 风格的字符串作为参数,而不是 std::string 对象。

  3. 其他版本:

void string_test2()
{
    std::string str = "Hello, world!";
    std::string substr = "world";

    // 在字符串中查找子串
    size_t found = str.find(substr);
    if (found != std::string::npos) 
    {
        std::cout << "子串 '" << substr << "' 在字符串中的位置索引为: " << found << std::endl;
    }
    else 
    {
        std::cout << "未找到子串 '" << substr << "'." << std::endl;
    }
}

rfind 用于在字符串中从后向前查找子串,并返回找到的子串的第一个字符的位置索引。

  • 主要用法:size_type rfind (const basic_string& str, size_type pos = npos) const;
  • 其他用法:
void string_test2()
{
    std::string str = "Hello, world!";
    std::string substr = "world";

    // 在字符串中查找子串
    size_t found = str.rfind(substr);
    if (found != std::string::npos) 
    {
        std::cout << "子串 '" << substr << "' 在字符串中的位置索引为: " << found << std::endl;
    }
    else 
    {
        std::cout << "未找到子串 '" << substr << "'." << std::endl;
    }
}

1.3 substr()

substr 是 C++ 中 std::string 类的成员函数,用于从字符串中提取子串:

  • basic_string substr (size_type pos = 0, size_type len = npos) const;
  • 其他用法:

这里是这两个重载函数的参数说明:

  • pos:指定子串的起始位置,即要提取的子串在原始字符串中的起始索引。
  • len:可选参数,指定要提取的字符数目。如果不指定或者指定为 npos,则提取从 pos 开始直到字符串末尾的所有字符。
void string_test3()
{
    std::string str = "Hello, world!";

    // 提取子串,从索引位置 7 开始,包括 5 个字符
    std::string sub1 = str.substr(7, 5);
    std::cout << "子串1: " << sub1 << std::endl;

    // 提取子串,从索引位置 7 开始,直到字符串末尾的所有字符
    std::string sub2 = str.substr(7);
    std::cout << "子串2: " << sub2 << std::endl;

}

1.4 打印网址的协议域名等

  • 我们可以使用 find() 和 substr() 分开打印网址的协议和域名
void string_test4()
{
    string url1("https://legacy.cplusplus.com/reference/string/basic_string/substr/");
    string url("https://mp.csdn.net/mp_blog/creation/editor?spm=1000.2115.3001.4503");
    string protocol;
    string domain;
    string port;
    size_t pos = 0;
    pos = url.find(":");
    if (pos != string::npos)
    {
        protocol = url.substr(0, pos);
    }
    int pos1 = pos + 3;
    pos = url.find('/', pos1);
    if (pos1 != string::npos)
    {
        domain = url.substr(pos1, pos - pos1);
    }
    int pos2 = pos + 1;
    port = url.substr(pos2);
    cout << protocol << endl;
    cout << domain << endl;
    cout << port << endl;
}


1.5 find_first_of()

find_first_of 用于在字符串中查找第一个与指定字符序列中的任何一个字符匹配的字符,并返回其位置索引,主要用法如下:

  • size_type find_first_of (const basic_string& str, size_type pos = 0) const;

这里是参数说明:

  • str:指定要搜索的字符序列。
  • pos:可选参数,指定搜索的起始位置。默认值为 0,表示从字符串的开头开始搜索。
  • 其他用法:

用法示例:

void string_test5()
{
    std::string str("PLease, replace the vowels in this sentence by asterisks.");
    std::string::size_type found = str.find_first_of("aeiou");
    while (found != std::string::npos)
    {
        str[found] = '*';
        found = str.find_first_of("aeiou", found + 1);
    }

    std::cout << str << '\n';
}

2. string() 模拟实现

2.1 构造函数的模拟实现

  • 无参构造,切忌不能全都初始化为0,这里会导致未定义的错误。
string()
    :_string(nullptr)
	,_size(0)
	,_capacity(0)
	{}

const char* c_str()
{
	return _string;
}
  • 正确定义方法如下:
string()
    :_string(new char[1])
    ,_size(0)
    ,_capacity()
    {
	    _string[0] = '\0';
    }
  • 但一般不会这样定义,一般定义全缺省的形式:
string(const char* ch = "")
		:_size(strlen(ch))
		{
			_capacity = _size;
            // 多开一个空间用来存放 '\0'
			_str = new char[_capacity + 1];
			strcpy(_str, ch);
		}

2.2 operator[] 和 iterator 的模拟实现

  • operator[] 是一个运算符重载,返回第pos个位置的值,代码如下:
size_t size() const
{
	return _size;
}

// 注意这里是传引用返回,不是传值返回,传值就不能改变_str() 里面的值了
char& operator[](size_t pos)
{
	assert(pos >= 0 && pos < _size);
	return _str[pos];
}



void string_test2()
{
	string str1("hello world");
	for (size_t i = 0; i < str1.size(); ++i)
	{
		cout << str1[i] << " ";
	}
	cout << endl;
}
  • 如果对于const的成员,就要用const来修饰函数,并且要求返回值不能修改,那么返回值也要加上const来修饰:
const char& operator[](size_t pos) const
{
	assert(pos >= 0 && pos < _size);
	return _str[pos];
}
  • 测试代码
    void string_test2()
	{
		string str1("hello world");
        // const 类型对象
		const string str2("hello handsome boy");
		for (size_t i = 0; i < str1.size(); ++i)
		{
			cout << str1[i] << " ";
		}
		cout << endl;
        // 可以进行修改
		for (size_t i = 0; i < str1.size(); ++i)
		{
			str1[i]++;
		}
		cout << endl;
		for (size_t i = 0; i < str1.size(); ++i)
		{
			cout << str1[i] << " ";
		}
		cout << endl;
		for (size_t i = 0; i < str2.size(); ++i)
		{
			cout << str2[i] << " ";
		}
		cout << endl;

		// 会报错,不能进行修改
		/*for (size_t i = 0; i < str2.size(); ++i)
		{
			str2[i]++;
		}*/

	}

  • iterator 在上一篇文章将结果,是类似于指针的形式,但是实际上可以拿指针来实现,也可以不用指针实现,以下是用指针实现的模式。
  • 首先要有begin() 和 end() 两个函数,如下:
    typedef char* iterator;
    iterator begin()
    {
    	return &_str[0];
    }
    
    iterator end()
    {
    	return &_str[_size];
    }
  •  对于const类型与非const构成重载:
    typedef const char* const_iterator;
    const_iterator begin() const
    {
    	return &_str[0];
    }
    
    const_iterator end() const
    {
    	return &_str[_size];
    }
  •  测试代码
        void string_test3()
    	{
    		string str1("hello world");
    		const string str2("hello handsome boy");
    		string::iterator it = str1.begin();
    		while (it != str1.end())
    		{
    			cout << *it << " ";
    			++it;
    		}
    		cout << endl;
    		it = str1.begin();
    		while (it != str1.end())
    		{
    			++(*it);
    			++it;
    		}
    		while (it != str1.end())
    		{
    			cout << *it << " ";
    			++it;
    		}
    		cout << endl;
    		string::const_iterator itt = str2.begin();
    		while (itt != str2.end())
    		{
    			cout << *itt << " ";
    			++itt;
    		}
    		// 会报错
    		/*itt = str2.begin();
    		while (itt != str2.end())
    		{
    			++(*itt);
    			++itt;
    		}*/
    	}


2.3 push_back() & append() & += 的模拟实现

  • 既然要插入字符串,那么肯定是需要扩容的,在库中可以使用 reserve() 进行扩容,我们也可以手动实现一个 reserve() 函数。

实现思路如下:

  1. 先new一块大小为n的空间;
  2. 然后把_str中的内容拷贝到tmp中;
  3. 接着释放掉_str指向的空间;
  4. 最后再让_str指向tmp。
void reserve(size_t n = 0)
{
	if (n > _capacity)
	{
		char* tmp = new char[n];
		strcpy(tmp, _str);
		delete[] _str;
		_str = tmp;
        _capacity = n;
	}
}

push_back的实现思路:

  1. 首先检查_size 和 _capacity 相不相等,如果相等就需要扩容;
  2. 在_size的位置插入字符,然后_size++;
  3. 在_size的位置插入'\0'。
void push_back(char ch)
{
	if (_size == _capacity)
	{
		int newcapacity = _capacity == 0 ? 4 : 2 * _capacity;
		reserve(newcapacity);
	}
	_str[_size++] = ch;
	_str[_size] = '\0';
}

append插入的实现思路:

  1. 首先计算要插入的字符串的长度len;
  2. 如果_size + len > _capacity 就需要扩容;
  3. 再用strcpy() 把字符串拷贝进_str。
string& append(const char* ch)
{
	size_t len = strlen(ch);
	if (_size + len > _capacity)
	{
		reserve(_size + len);
	}
	strcpy(_str + _size, ch);
	_size = _size + len;
	return *this;
}

operator+=的实现就直接调用append 和 push_back 就可以了

string& operator+=(char ch)
{
	push_back(ch);
	return *this;
}

string& operator+=(const char* ch)
{
	append(ch);
	return *this;
}
  • 测试代码
    void string_test4()
	{
		string str1("hello world");
		str1.push_back(' ');
		str1.push_back('h');
		str1.push_back('e');
		str1.push_back('l');
		str1.push_back('l');
		str1.push_back('o');
		str1.push_back(' ');
		str1.push_back('h');
		str1.push_back('a');
		str1.push_back('n');
		str1.push_back('d');
		str1.push_back('s');
		str1.push_back('o');
		str1.push_back('m');
		str1.push_back('e');
		str1.push_back(' ');
		str1.push_back('b');
		str1.push_back('o');
		str1.push_back('y');

		str1.append(" hello handsome boy");

		for (size_t i = 0; i < str1.size(); ++i)
		{
			cout << str1[i] << " ";
		}
		cout << endl;
		cout << str1.size() << endl;
	}
    void string_test5()
	{
		string str1("hello world");
		str1 += ' ';
		str1 += "hello handsome boy";
		for (size_t i = 0; i < str1.size(); ++i)
		{
			cout << str1[i] << " ";
		}
		cout << endl;
		cout << str1.size() << endl;
	}

2.4 insert() & erase() 的模拟实现

insert() 就是在某个位置插入字符或者字符串,首先讲一下插入字符思路如下:

  1. 判断pos是否合法;
  2. 检查是否需要扩容;
  3. 将pos位置之后的字符全都向后移动一位;
  4. 在pos位置插入字符;
  5. _size++;

        注意这里的end要给_size + 1,如果给_size,当pos == 0的时候,最后一轮进循环end会变为-1,然而end是size_t类型的,-1就会变为整形的最大值,就会死循环。

        就算把end改为int类型也不行,因为括号内比较的类型是end和pos,而pos是size_t类型的,会发生类型转换,还是会把end转换成size_t类型,因此还是会死循环。

string& insert(size_t pos, char ch)
{
	assert(pos >= 0 && pos <= _size);
	if (_size == _capacity)
	{
		size_t newcapacity = _capacity == 0 ? 4 : 2 * _capacity;
		reserve(newcapacity);
	}
	size_t end = _size + 1;
	while (end > pos)
	{
		_str[end] = _str[end - 1];
		--end;
	}
	_str[pos] = ch;
	++_size;
	return *this;
}

insert() 插入字符串类型:

  1. 计算是否需要扩容以及pos位置是否合法;
  2. 将第pos位置之后的元素移动len个字符;
  3. 在pos位置插入字符串;
  4. _size加上len;

这里的移动有两种思路,一种是每次移动一次,移动len次

另一种是直接移动len次

先看一下每次移动一次的,时间复杂度为 O(N^2) , 比较容易理解:

string& insert(size_t pos, const char* ch)
{
	assert(pos >= 0 && pos <= _size);
	size_t len = strlen(ch);
	if (len + _size > _capacity)
	{
		reserve(len + _size);
	}
	size_t pos1 = pos;
	while (len--)
	{
		size_t end = _size + 1;
		while (end > pos1)
		{
			_str[end] = _str[end - 1];
			--end;
		}
		_str[pos1] = *ch;
		++ch;
		++pos1;
		++_size;
	}
	return *this;
}

一次直接移动len次,以下是动图图解:

string& insert(size_t pos, const char* ch)
{
	assert(pos >= 0 && pos <= _size);
	size_t len = strlen(ch);
	if (len + _size > _capacity)
	{
		reserve(len + _size);
	}
	int end = _size + len;
	while (end >= pos + len)
	{
		_str[end] = _str[end - len];
		--end;
	}
	strncpy(_str + pos, ch, len);
	_size += len;
	return *this;
}

erase()就是删除某个位置的元素,较为容易,直接上代码:

string erase(size_t pos = 0, size_t len = npos)
{
	assert(pos >= 0 && pos < _size);
	if (len >= _size - pos)
	{
		_str[pos] = '\0';
		_size = pos;
	}
	else
	{
		strcpy(_str + pos, _str + pos + 1);
		_size = _size - len;
	}
	return *this;
			
}
  • 测试代码
    void string_test6()
	{
		string str1("hello world");
		str1.insert(0, 'x');
		for (size_t i = 0; i < str1.size(); ++i)
		{
			cout << str1[i] << " ";
		}
		cout << endl;
		cout << str1.size() << endl;
		str1.insert(12, 'y');
		for (size_t i = 0; i < str1.size(); ++i)
		{
			cout << str1[i] << " ";
		}
		cout << endl;
		cout << str1.size() << endl;
		str1.insert(0, "zzz");
		for (size_t i = 0; i < str1.size(); ++i)
		{
			cout << str1[i] << " ";
		}
		cout << endl;
		cout << str1.size() << endl;
		str1.insert(16, "dddddddddddd");
		for (size_t i = 0; i < str1.size(); ++i)
		{
			cout << str1[i] << " ";
		}
		cout << endl;
		cout << str1.size() << endl;
		str1.erase(16,12);
		for (size_t i = 0; i < str1.size(); ++i)
		{
			cout << str1[i] << " ";
		}
		cout << endl;
		cout << str1.size() << endl;
	}

2.5 resize() 模拟实现

之前讲过 resize() 的三种形式,这里就不详细讲解了,有兴趣的可以看上一篇文章:

STL —— string(2)-CSDN博客icon-default.png?t=N7T8https://blog.csdn.net/m0_68617301/article/details/136954250

 这里直接讲模拟实现方式:

void resize(size_t n, char ch = '\0')
{
	if (n < _size)
	{
		_str[n] = '\0';
	}
	else if (n >= _size)
	{
		reserve(n);
		for (size_t i = _size; i < n; i++)
		{
			_str[i] = ch;
		}
		_str[n] = '\0';
		_size = n;
	}
	return *this;
}
  •  测试代码
    void string_test7()
	{
		string str1("hello world");
		str1.resize(20, 'x');
		for (size_t i = 0; i < str1.size(); ++i)
		{
			cout << str1[i] << " ";
		}
		cout << endl;
		cout << str1.size() << endl;

	}

2.6 析构函数和复制重载模拟

  • 这里的析构函数同样也涉及到深浅拷贝的问题,因为这里也同样有一个在堆上开辟的空间,如果调用编译器自己生成的析构函数,就会造成对同一块空间重复释放的问题,需要我们自己编写析构函数:
~string()
{
	_size = _capacity = 0;
	if (_str != nullptr)
	{
		delete[] _str;
	}
}

string& operator=(string& str)
{
	if (this != &str)
	{
		_size = str._size;
		_capacity = str._capacity;
		char* tmp = new char[_capacity];
		delete[] _str;
		strcpy(tmp, str._str);
		_str = tmp;
	}
			
	return *this;
}

 

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

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

相关文章

海外媒体宣发:商务视频推广数字化变革全解析-华媒舍

在当今数字化时代&#xff0c;商务视频推广正迎来一场革命性的数字化变革。本文将为您解析这场变革的核心元素和相关内容。 商务视频推广 商务视频推广是一种通过视频形式来宣传和推广产品、服务或品牌的方法。传统的推广方式主要是通过文字和图片进行&#xff0c;而商务视频推…

【真题解析】题目 3151: 蓝桥杯2023年第十四届省赛真题-飞机降落【C++ DFS 超详解注释版本】

爆搜冥想 暴力枚举每一辆飞机对于每一个飞机都只存在两种情况&#xff0c;可以降落和不可以降落如果可以降落&#xff0c;计算降落后最早可以降落的时间pre&#xff0c;作为下一次递归的传参如果不可以降落&#xff0c;枚举下一辆飞机 注意这辆的降落有盘旋这种量子叠加态&…

Vant Weapp小程序 van-uploader 文件上传点击无反应,删除无反应

Vant Weapp 1.0 版本开始支持van-uploader组件&#xff0c;请先确认好版本号和引用路径正确&#xff01;&#xff01; <van-uploader file-list"{{ fileList }}" deletable"{{ true }}" />1. 上传无反应 微信小程序用了van-uploader&#xff0c;但是…

无人直播(视频推流)

环境搭建 我这里采用的是ffmpeg来进行推流直播 yum -y install wgetwget --no-check-certificate https://www.johnvansickle.com/ffmpeg/old-releases/ffmpeg-4.0.3-64bit-static.tar.xztar -xJf ffmpeg-4.0.3-64bit-static.tar.xzcd ffmpeg-4.0.3-64bit-staticmv ffmpeg /u…

【Linux】线程同步{死锁/线程同步相关接口/由浅入深理解线程同步}

文章目录 1.死锁1.1概念1.2死锁的必要条件 2.线程同步相关接口2.1pthread_cond_init/destroy()2.2int pthread_cond_wait2. 3linux下的条件变量及其作用2.4int pthread_cond_signal/broadcast();2.5Linux下 阻塞和挂起的异同2.6阻塞&#xff0c;挂起&#xff0c;和进程切换的关…

【Flask】Flask项目部署上线

Flask 项目部署上线 1.Gunicorn Gunicorn 是一个纯 Python WSGI 服务器&#xff0c;配置简单&#xff0c;多工作者实现&#xff0c;方便 性能调优。 它倾向于与主机平台轻松集成。 它不支持 Windows &#xff08;但可以在 WSL 上运行&#xff09;。 它很容易安装&#xff0…

Postwoman 安装

Postwoman作为Postman的女朋友&#xff0c;具有免费开源、轻量级、快速且美观等特性&#xff0c;是一款非常好用的API调试工具。能帮助程序员节省时间&#xff0c;提升工作效率。 Github地址&#xff1a;GitHub - hoppscotch/hoppscotch: &#x1f47d; Open source API devel…

【Linux】-Linux下的编辑器Vim的模式命令大全及其自主配置方法

目录 1.简单了解vim 2.vim的模式 2.1命令模式 2.2插入模式 2.3底行模式 3.vim各模式下的命令集 3.1正常&#xff08;命令模式下&#xff09; 3.1.1光标定位命令 3.1.2 复制粘贴 3.1.3 删除 3.1.4 撤销 3.1.5大小写转换 3.1.6替换 「R」&#xff1a;替换光标所到之处的字符&…

鸿蒙(HarmonyOS)Navigation如何实现多场景UI适配?

场景介绍 应用在不同屏幕大小的设备上运行时&#xff0c;往往有不同的UI适配&#xff0c;以聊天应用举例&#xff1a; 在窄屏设备上&#xff0c;联系人和聊天区在多窗口中体现。在宽屏设备上&#xff0c;联系人和聊天区在同一窗口体现。 要做好适配&#xff0c;往往需要开发…

Git相关命令(一)

一、简介 Git 是一个开源的分布式版本控制系统。 当然&#xff0c; git 不会傻傻的把你的每一个版本完整的存储下来&#xff0c;他仅仅会存储每次修改的位置和内容&#xff08;可持久化&#xff09;&#xff0c;每一次 commit 可以理解为产生一个版本&#xff0c;接下来的版本…

数据结构与算法 顺序表的基本运算

一、实验内容 编写一个程序实现&#xff0c;实现顺序表的各种基本运算&#xff08;假设顺序表的元素类型为char&#xff09;&#xff0c;并以此为基础设计一个程序完成下列功能&#xff1a; &#xff08;1&#xff09;初始化顺序表&#xff1b; &#xff08;2&#xff09;采…

开源推荐榜【Sejil一个 .NET带界面的日志管理组件】

Sejil 是一个库&#xff0c;使您能够直接从应用程序捕获、查看和过滤 ASP.net Core 应用程序的日志事件。它支持结构化日志记录、查询以及保存日志事件查询。 开源地址&#xff1a;https://github.com/ZiggyCreatures/FusionCache 使用方法&#xff1a; 安装 Sejil 软件包 do…

鸿蒙HarmonyOS应用开发——跨端迁移

在用户使用设备的过程中&#xff0c;当使用情境发生变化时&#xff08;例如从室内走到户外或者周围有更适合的设备等&#xff09;&#xff0c;之前使用的设备可能已经不适合继续当前的任务&#xff0c;此时&#xff0c;用户可以选择新的设备来继续当前的任务&#xff0c;原设备…

【前端Vue】Vue3+Pinia小兔鲜电商项目第3篇:静态结构搭建和分类实现,1. 整体结构创建【附代码文档】

Vue3ElementPlusPinia开发小兔鲜电商项目完整教程&#xff08;附代码资料&#xff09;主要内容讲述&#xff1a;认识Vue3&#xff0c;使用create-vue搭建Vue3项目1. Vue3组合式API体验,2. Vue3更多的优势,1. 认识create-vue,2. 使用create-vue创建项目,1. setup选项的写法和执行…

v4l2采集视频

Video4Linux2&#xff08;v4l2&#xff09;是用于Linux系统的视频设备驱动框架&#xff0c;它允许用户空间应用程序直接与视频设备&#xff08;如摄像头、视频采集卡等&#xff09;进行交互。 linux系统下一切皆文件&#xff0c;对视频设备的操作就像对文件的操作一样&#xff…

标题:深入了解 JavaScript 中的字符串分割:遇到 / 和 | 都分割

在 JavaScript 中&#xff0c;处理字符串是一项常见的任务。有时候&#xff0c;我们需要将字符串按照特定的字符进行分割&#xff0c;以提取或操作其中的各个部分。在这篇博客中&#xff0c;我们将深入探讨如何使用 JavaScript 的字符串分割功能&#xff0c;特别是当遇到斜杠 /…

汽车后视镜反射率检测光纤光谱仪:安全驾驶的守护神

在汽车的日常使用中&#xff0c;后视镜扮演着至关重要的角色。它不仅帮助驾驶员观察车辆后方的情况&#xff0c;还确保了行车的安全性。然而&#xff0c;由于各种原因&#xff0c;后视镜的反射率可能会降低&#xff0c;从而影响到驾驶员的视线范围和判断能力。为了解决这一问题…

Reactor 模式全解:实现非阻塞 I/O 多路复用

Reactor网络模式是什么&#xff1f; Reactor网络模式时目前网络最常用的网络模式。如果你使用Netty&#xff0c;那么你在使用Reactor;如果你使用Twisted,那么你子啊使用Reactor;如果你使用netpoll&#xff0c;那么你在使用Reactor。 这里先给出答案&#xff1a;Reactor I/O多…

Python与供应链-2预测误差及指数平滑需求预测模型

主要介绍预测误差和指数平滑模型的相关理论,然后再通过Python的statsmodels封装的指数平滑函数预测需求。 1预测误差 预测误差是指预测结果与预测对象发展变化的真实结果之间的差距。这种误差分为绝对误差和相对误差。绝对误差是预测值与实际观测值的绝对差距,而相对误差则…

Spring学习——什么是循环依赖及其解决方式

文章目录 前言一、什么是循环依赖二、解决思路1、循环依赖分类2、对象初始化步骤及对象分类3、spring是如何解决的4、图解5、三级缓存1、区别2、ObjectFactory是什么 三、源码debug1、spring创建对象过程1、dubug第一步——找到getBean2、dubug第二步——getBean与doGetBean3、…