【C++程序员的自我修炼】简单实现 string 库的常用接口函数

天接云涛连晓雾

星河欲转千帆舞


目录

string 类环境的搭建

实现 c_str() 函数

 实现 size() 函数

重载运算符operator[]

实现简单迭代器 begin()、end()

实现 reserve() 函数

实现 push_back() 函数

实现 append() 函数 

 重载运算符operator+=

实现 insert() 函数

实现 erase() 函数

 实现 find() 函数

实现 swap() 函数

 string 的深拷贝

 string 的赋值拷贝

实现 substr() 函数

实现 string 比较大小运算符重载

实现 clear() 函数

实现 string 中的流重载 

整体代码的实现

 契子

  前面我们已经大致了解到 string 库底层的许多接口函数,为了巩固理解以及快速上手今天我们就来实现一下简单 string 库的实现

  首先这里提前说明:只是简单的实现底层接口掌握其基本逻辑,并不会涉及太多,比如说模板以及迭代器的底层实现


string 类环境的搭建

首先我们练习的话可以声明与定义分开来写,然后再用一个单独的文件进行测试,像这样:

(这里顺便提一下)在C++ 标准 string 库里一般都是放在 .h文件 中且都是写成内联的形式 ~

首先,先把我们的构造、析构函数先搭建出来:

//string.h
#pragma once
#include<iostream>
#include<assert.h>
using std::cout;
using std::endl;
using std::cin;
using std::istream;
using std::ostream;

namespace Mack
{
	class string
	{
	public:
		string(const char* str = "");
		~string();
	private:
		char* _str;
		size_t _capacity;
		size_t _size;
		const static size_t npos;
	};
}
//string.cpp
#include"string.h"
namespace Mack
{
	const size_t string::npos = -1;
	string::string(const char* str)
		:_size(strlen(str))
	{
		_str = new char[_size + 1];
		_capacity = _size;
		strcpy(_str, str);
	}

	string::~string()
	{
		delete[] _str;
		_str = nullptr;
		_capacity = _size = 0;
	}
}

 然后把在标准库中我们的要用的函数的命名空间展开 ~

实现 c_str() 函数

在 C++ 中 c_str() 库函数的作用就是获取一个字符串的首元素地址,所以我们直接返回 _str 即可

    const char* c_str() const
	{
		return _str;
	}
//测试代码
void TestString()
{
	string s1("hello world");
	cout << s1.c_str() << endl;
}


 实现 size() 函数

在 C++ 中 size() 库函数的作用就是获取一个字符串的长度(不包含 '\0' 哦)

所以我们直接返回  _size 即可

	size_t size() const
	{
		return _size;
	}

重载运算符operator[]

对于运用场景的不同我们重载了两个operator[],不过基础逻辑还是一样的,提取当前 pos 位置的字符并返回,注意要判断 pos 的合法范围哦 ~ 不要越界了还不知道

	char& operator[](size_t pos)
	{
		assert(pos < _size);
		return _str[pos];
	}

	const char& operator[](size_t pos) const
	{
		assert(pos < _size);
		return _str[pos];
	}
//测试代码
void TestString()
{
	string s1("hello world");
	for (int i = 0; i < s1.size(); i++)
	{
		cout << s1[i] << " ";
	}
}

这里和上面 size() 一起测试了,都没有问题 


实现简单迭代器 begin()、end()

对于迭代器我们的印象是这样一长串东西(是一个模板)

我们简单来看,其实可以用 指针 浅浅的代替一下:

begin() 返回的是字符串的首元素位置,我们直接返回 _str 即可

end() 返回的是字符串的末尾位置,我们之间返回 _str + _size 即可

//string.h
    class string
    {
    public:
    typedef char* iterator;
	iterator begin();
	iterator end();
    //..
    };

//string.cpp
	string::iterator string::begin()
	{
		return _str;
	}

	string::iterator string::end()
	{
		return _str + _size;
	}
//代码测试
void TestString()
{
	string s1("hello world");
	for (auto i : s1)
	{
		cout << i << " ";
	}
	cout << endl;
	string::iterator it = s1.begin();
	while (it != s1.end())
	{
		cout << *it << " ";
		it++;
	}
}

我们知道我们 范围for 的底层逻辑就是迭代器,这里浅浅的实现了一下 


实现 reserve() 函数

根据我们前面所学,我们知道 resserve 是对空间的预留,我们可以开辟一个我们自己预定的空间tmp,再将已有数据拷贝到该空间,并释放原 _str ,最后将 _str 指向 tmp 即可

	void reserve(size_t n)
	{
		if (n > _capacity)
		{
			char* tmp = new char[n + 1];
			strcpy(tmp, _str);
			delete[] _str;
			_str = tmp;
			_capacity = n;
		}
	}


实现 push_back() 函数

push_back() 相当于我们顺序表的尾插,要先判断是否需要扩容,再考虑末尾插入,别忘了 '\0' 哦 

    void string::push_back(char ch)
	{
		if (_capacity == _size)
		{
			size_t newcapacity = _capacity == 0 ? 4 : 2 * _capacity;
			reserve(newcapacity);
		}
		_str[_size] = ch;
		_str[_size + 1] = '\0';
		_size++;
	}
//代码测试
void TestString()
{
	string s1("hello world");
	s1.push_back('x');
	cout << s1.c_str() << endl;
	s1.push_back('y');
	cout << s1.c_str() << endl;
}


实现 append() 函数 

append() 的常见用法就是在末尾追加一个字符串,可以将目标字符串 str 拷贝到源字符串 _str 中

(拷贝时覆盖 _str 中 '\0' 的位置,但是 str 末尾的 '\0' 会保留,所以我们不需要补 '\0' )

    void append(const char* str)
	{
		size_t len = strlen(str);
		if (len + _size > _capacity)
		{
			reserve(len + _size);
		}
		strcpy(_str + _size, str);
		_size += len;
	}
//测试
void TestString()
{
	string s1("hello world");
	s1.append("xxxxx");
	cout << s1.c_str() << endl;
}


 重载运算符operator+=

在我们的印象中,operator+= 可以追加一个字符或者字符串,这样我们就可以复用一下

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

	string& operator+=(const char* str)
	{
		append(str);
		return *this;
	}
//测试
void TestString()
{
	string s1("hello world");
	s1 += 'x';
	cout << s1.c_str() << endl;
	s1 += "yyy";
	cout << s1.c_str() << endl;
}


实现 insert() 函数

insert() 函数常用于我们的头插,但是经过我们上次的学习我们知道 insert() 的功能不仅于此

insert() 不仅可以指定位置插入字符,还可以指定位置插入字符串

<1> 指定位置插入字符:跟我们顺序表的指定位置插入差不多,挪动数据在进行插入即可

<2> 指定位置插入字符串:我们可以挪动将指定位置后的数据挪动 len 个(目标字符串 str 长度)然后将 str 拷贝到指定位置即可

	void insert(size_t pos, char ch)
	{
		assert(pos <= _size);
		if (_size == _capacity)
		{
			size_t newcpacity = _capacity == 0 ? 4 : 2 * _capacity;
			reserve(newcpacity);
		}
		size_t len = _size + 1;
		while (pos < len)
		{
			_str[len] = _str[len - 1];
			len--;
		}
		_str[pos] = ch;
		_size++;
	}

	void insert(size_t pos, const char* str)
	{
		size_t len = strlen(str);
		if (_size + len > _capacity)
		{
			reserve(_size + len);
		}
		size_t end = len + _size;
		while (end > pos)
		{
			_str[end] = _str[end - len];
			end--;
		}
		memcpy(_str + pos, str, len);
		_size += len;
	}
//代码测试
void TestString()
{
	string s1("hello world");
	s1.insert(0, 'x');
	cout << s1.c_str() << endl;
	s1.insert(0, "yyy");
	cout << s1.c_str() << endl;
	s1.insert(6, 'z');
	cout << s1.c_str() << endl;
	s1.insert(9, "mmm");
	cout << s1.c_str() << endl;
}


实现 erase() 函数

根据我们之前对 string 库的理解,可以知道 erase() 可以做到 删除指定位置 pos 的任意字符 len

一开始我们可以分类讨论一下:

<1> len 超过了从 pos 位置开始的剩余字符长度 -- 全部删完(将该位置用 '\0' 覆盖)

<2> len 没有超过 pos 位置开始的剩余字符长度 -- 删中间(将剩余字符拼接到 pos 位置)

	void erase(size_t pos, size_t len)
	{
		assert(pos < _size);
		if(len >= _size - pos)
		{
			_str[pos] = '\0';
			_size = pos;
		}
		else
		{
			strcpy(_str + pos, _str + pos + len);
			_size -= len;
		}
	}
//代码测试
void TestString()
{
	string s1("hello world");
	s1.erase(8, 5);
	cout << s1.c_str() << endl;
	s1.erase(2, 3);
	cout << s1.c_str() << endl;
}


 实现 find() 函数

根据我们之前的学习,我们知道 find() 函数有两种常见的功能:
<1> 从 pos 位置开始在主串中查找单个字符

<2> 从 pos 位置开始在主串中查找与子串相匹配的位置:我们可以使用 BF 暴力算法, strstr 的底层就是 BF

	size_t find(char ch, size_t pos)
	{
		for (int i = pos; i < size(); i++)
		{
			if (_str[i] == ch)
				return i;
		}
		return npos;
	}

	size_t find(const char* sub, size_t pos)
	{
		char* p = strstr(_str + pos, sub);
		return  p - _str;
	}


实现 swap() 函数

为什么 string 库里面要单独搞一个 swap 出来呢?用下面这个不香吗

虽然上面这个也能完成交换,但是造成了很大的代价!!!

简单来说明一下:

<1> 我们想交换 s1、s2 的数据首先要拷贝构造一个与 s1 一模一样的空间 c

<2> 然后开个与 s2 一样大的空间并将字符串拷贝过去,让 s1指向这块空间,并释放原空间

<3> 最后 s2 开一个与 c 一样大的空间并将字符串拷贝过去,让 s2 指向这块空间,并释放原空间

以上用了一个拷贝构造和两次赋值构造,都是深拷贝要开空间拷贝数据

所以 string 库里专门提供了一个内部的 swap :

这个 swap 只要借助一个变量交换一下指针指向即可

    void swap(string& s)
	{
		std::swap(s._str, _str); 
		std::swap(s._size, _size); 
		std::swap(s._capacity, _capacity); 
	}

只是交换指针指向的话,我们可以借助 std 内部的

//代码测试
void TestString()
{
	string s1("hello");
	string s2("world");
	s1.swap(s2);
	cout << s1.c_str() << endl;
	cout << s2.c_str() << endl;
}


 string 的深拷贝

    string(const string& s)
    {
        _str = new char[s._capacity + 1];
        strcpy(_str, s._str);
        _size = s._size;
        _capacity = s._capacity;
    }

 string 的赋值拷贝

 string& operator=(const string& s)
    {
        if (this != &s)
        {
            char* tmp = new char[s._capacity + 1];
            strcpy(tmp, _str);
            delete[] _str;
            _str = tmp;
            _size = s._size;
            _capacity = s._capacity;
        }
        return *this;
    }
//代码测试
void TestString()
{
	string s1("hello world");
	string s3;
	s3 = s1;
	cout << s1.c_str() << endl;
}


实现 substr() 函数

substr 常被我们用来提取字符串

<1> 从 pos 位置开始剩余字符串小于 len (要提取的字符串长度)我们就直接提取完

<2> 从 pos 位置开始剩余字符串不小于 len 则从 pos 位置开始一个字符一个字符的拷贝 len 个字符

    string substr(size_t pos, size_t len)
	{
		if (_size - pos < len)
		{
			string sub(_str + pos);
			return sub;
		}
		else
		{
			string sub;
			sub.reserve(len);
			for (int i = pos; i < pos + len; i++)
			{
				sub += _str[i];
			}
			return sub;
		}
	}
//代码测试
void TestString()
{
	string s1("hello world");
	string s3 = s1.substr(6, 5);
	cout << s3.c_str() << endl;
}


 

实现 string 比较大小运算符重载

关于 string 的比较大小,我们可以借助 strcmp :

1.strcmp常用于比较字符串的大小

2.第一个字符串大于第二个字符串,则返回大于0的值

3.第一个字符串等于第二个字符串,则返回0

4.第一个字符串小于第二个字符串,则返回小于0的值

5.比较两个字符串对应位置上的字符ASCLL码值的大小来判断字符串的大小

完成一部分比较后,我们还可以对结果进行复用 

	bool operator<(const string& s) const
	{
		return strcmp(_str, s._str) < 0;
	}

	bool operator>(const string& s) const
	{
		return !(*this <= s);
	}

	bool operator<=(const string& s) const
	{
		return *this < s || *this == s;
	}

	bool operator>=(const string& s) const
	{
		return !(*this < s);
	}

	bool operator==(const string& s) const
	{
		return strcmp(_str, s._str) == 0;
	}

	bool operator!=(const string& s) const
	{
		return !(*this == s);
	}

 


实现 clear() 函数

clear 在我们 Linux 中经常使用就是清空的意思,而这里就是清空整个字符串

我们只需要将 '\0' 塞在开头即可

    void clear()
	{
		_str[0] = '\0';
		_size = 0;
	}


 

实现 string 中的流重载 

我们之前写日期类的时候是将流重载写成友元函数,而这里我们可以不写成友元,因为我们可以不访问类中元素就可以实现相关操作

我们先来看我们的流提取

	istream& operator>> (istream& is, string& str)
	{
		str.clear();
		char ch;
		ch = is.get();
		while (ch != ' ' && ch != '\n')
		{
			str += ch;
			ch = is.get();
		}
		return is;
	}

cin 进行提取会将原内容进行覆盖,所以我们要提前清空一下数据

这里我们使用了 get() -- 显示提取一个字符 

我们把取到的字符追加到对应的字符串即可,但我们输入 空格 或者 回撤 键就会退出提取

	ostream& operator<< (ostream& os, const string& str)
	{
		for (int i = 0; i < str.size(); i++)
		{
			os << str[i];
		}
		return os;
	}

流插入我们可以一个字符一个字符的输出到我们的显示屏上

//代码测试
void TestString()
{
	string s1;
	cin >> s1;
	cout << s1 << endl;
}

整体代码的实现

string.h

#pragma once
#include<iostream>
#include<assert.h>
using std::cin;
using std::cout;
using std::endl;
using std::istream;
using std::ostream;


namespace Mack
{
	class string
	{
	public:
		typedef char* iterator;
        string(const string& s);
		string& operator=(const string& s);
		string(const char* str = "");
		~string();

		const char* c_str() const;
		size_t size() const;

		char& operator[](size_t pos);
		const char& operator[](size_t pos) const;

		iterator begin();
		iterator end();

		void reserve(size_t n = 0);
		void push_back(char ch);
		void append(const char* str);

		string& operator+=(char ch);
		string& operator+=(const char* ch);

		void insert(size_t pos, char ch);
		void insert(size_t pos, const char* str);
		void erase(size_t pos, size_t len);

		size_t find(char ch, size_t pos);
		size_t find(const char* sub, size_t pos);

		void swap(string& s);

		string substr(size_t pos, size_t len);

		bool operator<(const string& s) const;
		bool operator<=(const string& s) const;
		bool operator>(const string& s) const;
		bool operator>=(const string& s) const;
		bool operator==(const string& s) const;
		bool operator!=(const string& s) const;

		void clear();


	private:
		char* _str;
		size_t _capacity;
		size_t _size;
		const static size_t npos;
	};
	ostream& operator<< (ostream& os, const string& str);
	istream& operator>> (istream& is, string& str);
}

string.cpp

#include"string.h"

namespace Mack
{
    string::string(const string& s)
    {
        _str = new char[s._capacity + 1];
        strcpy(_str, s._str);
        _size = s._size;
        _capacity = s._capacity;
    }

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

	const size_t string::npos = -1;
	string::string(const char* str)
		:_size(strlen(str))
	{
		_str = new char[_size + 1];
		_capacity = _size;
		strcpy(_str, str);
	}

	string::~string()
	{
		delete[] _str;
		_str = nullptr;
		_capacity = _size = 0;
	}

	const char* string::c_str() const
	{
		return _str;
	}

	size_t string::size() const
	{
		return _size;
	}

	char& string::operator[](size_t pos) 
	{
		assert(pos < _size);
		return _str[pos];
	}

	const char& string::operator[](size_t pos) const
	{
		assert(pos < _size);
		return _str[pos];
	}

	string::iterator string::begin()
	{
		return _str;
	}

	string::iterator string::end()
	{
		return _str + _size;
	}

	void string::reserve(size_t n)
	{
		if (n > _capacity)
		{
			char* tmp = new char[n + 1];
			strcpy(tmp, _str);
			delete[] _str;
			_str = tmp;
			_capacity = n;
		}
	}

	void string::push_back(char ch)
	{
		if (_capacity == _size)
		{
			size_t newcapacity = _capacity == 0 ? 4 : 2 * _capacity;
			reserve(newcapacity);
		}
		_str[_size] = ch;
		_str[_size + 1] = '\0';
		_size++;
	}

	void string::append(const char* str)
	{
		size_t len = strlen(str);
		if (len + _size > _capacity)
		{
			reserve(len + _size);
		}
		strcpy(_str + _size, str);
		_size += len;
	}

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

	string& string::operator+=(const char* str)
	{
		append(str);
		return *this;
	}

	void string::insert(size_t pos, char ch)
	{
		assert(pos <= _size);
		if (_size == _capacity)
		{
			size_t newcpacity = _capacity == 0 ? 4 : 2 * _capacity;
			reserve(newcpacity);
		}
		size_t len = _size + 1;
		while (pos < len)
		{
			_str[len] = _str[len - 1];
			len--;
		}
		_str[pos] = ch;
		_size++;
	}

	void string::insert(size_t pos, const char* str)
	{
		size_t len = strlen(str);
		if (_size + len > _capacity)
		{
			reserve(_size + len);
		}
		size_t end = len + _size;
		while (end > pos)
		{
			_str[end] = _str[end - len];
			end--;
		}
		memcpy(_str + pos, str, len);
		_size += len;
	}

	void string::erase(size_t pos, size_t len)
	{
		assert(pos < _size);
		if(len >= _size - pos)
		{
			_str[pos] = '\0';
			_size = pos;
		}
		else
		{
			strcpy(_str + pos, _str + pos + len);
			_size -= len;
		}
	}

	size_t string::find(char ch, size_t pos)
	{
		for (int i = pos; i < size(); i++)
		{
			if (_str[i] == ch)
				return i;
		}
		return npos;
	}

	size_t string::find(const char* sub, size_t pos)
	{
		char* p = strstr(_str + pos, sub);
		return  p - _str;
	}

	void string::swap(string& s)
	{
		std::swap(s._str, _str); 
		std::swap(s._size, _size); 
		std::swap(s._capacity, _capacity); 
	}

	string string::substr(size_t pos, size_t len)
	{
		if (_size - pos < len)
		{
			string sub(_str + pos);
			return sub;
		}
		else
		{
			string sub;
			sub.reserve(len);
			for (int i = pos; i < pos + len; i++)
			{
				sub += _str[i];
			}
			return sub;
		}
	}

	bool string::operator<(const string& s) const
	{
		return strcmp(_str, s._str) < 0;
	}

	bool string::operator==(const string& s) const
	{
		return strcmp(_str, s._str) == 0;
	}

	bool string::operator!=(const string& s) const
	{
		return (*this) != s;
	}

	bool string::operator<=(const string& s) const
	{
		return (*this<s) || (*this == s);
	}
	bool string::operator>(const string& s) const
	{
		return !(*this < s);
	}
	bool string::operator>=(const string& s) const
	{
		return !(*this < s) || (*this == s);
	}

	void string::clear()
	{
		_str[0] = '\0';
		_size = 0;
	}

	istream& operator>> (istream& is, string& str)
	{
		str.clear();
		char ch;
		ch = is.get();
		while (ch != ' ' && ch != '\n')
		{
			str += ch;
			ch = is.get();
		}
		return is;
	}

	ostream& operator<< (ostream& os, const string& str)
	{
		for (int i = 0; i < str.size(); i++)
		{
			os << str[i];
		}
		return os;
	}

}

先介绍到这里啦~

有不对的地方请指出💞

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

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

相关文章

【RAG 论文】UPR:使用 LLM 来做检索后的 re-rank

论文&#xff1a;Improving Passage Retrieval with Zero-Shot Question Generation ⭐⭐⭐⭐ EMNLP 2022, arXiv:2204.07496 Code: github.com/DevSinghSachan/unsupervised-passage-reranking 论文&#xff1a;Open-source Large Language Models are Strong Zero-shot Query…

【大数据·hadoop】在hdfs上运行shell基本常用命令

一、准备工作 1.1格式化并启动Hadoop服务 参见Hadoop在ubuntu虚拟机上的伪分布式部署|保姆级教程的4.7节 二、HDFS常用命令 接着&#xff0c;就愉快地在刚刚的命令行里敲命令啦 1.显示hdfs目录结构 hadoop fs -ls -R /hadoop fs: 这是Hadoop文件系统命令行的一部分&#x…

【LeetCode刷题】136.只出现一次的数字(Ⅰ)

【LeetCode刷题】136.只出现一次的数字&#xff08;Ⅰ&#xff09; 1. 题目&#xff1a;2.思路分析&#xff1a;思路1&#xff1a;一眼异或&#xff01; 1. 题目&#xff1a; 2.思路分析&#xff1a; 思路1&#xff1a;一眼异或&#xff01; 看到题目&#xff0c;如果有一定基…

第十六篇:数据库性能优化:从基础到高级的全面指南

数据库性能优化&#xff1a;从基础到高级的全面指南 1. 引言 在数字化的浪潮中&#xff0c;数据库作为信息系统的核心组件&#xff0c;其性能的优劣直接关系到企业的运营效率和市场竞争力。数据库性能优化不仅是一项技术挑战&#xff0c;更是一项战略任务。它要求我们深入理解…

“等保测评实施策略:保障企业信息安全合规“

等保测评&#xff0c;即网络安全等级保护测评&#xff0c;是企业保障信息安全合规的重要实施策略。以下是企业实施等保测评的一些关键策略&#xff1a; 理解等保测评的重要性&#xff1a; 等保测评有助于企业识别和评价信息系统的安全性能&#xff0c;提供改进建议&#xff0c;…

iOS--底层学习--GCD的简单认识

iOS--底层学习--GCD的简单认识 前言什么是GCDGCD的优点GCD中的任务和队列任务队列 GCD的使用队列的创建和获取任务的创建队列嵌套任务和队列中的一些要点 GCD线程间的通信从后台线程切换到主线程通过队列传递数据使用Dispatch Group进行线程间协调 GCD的方法dispatch_barrier_a…

苹果macOS无法给App麦克风授权解决办法

好久没有在电脑上录制课程了&#xff0c;有些东西还是录下来记忆深刻&#xff0c;却意外发现MAC系统升级后无法授权给第三方的App使用摄像头和麦克风&#xff0c;而录屏软件是需要开启麦克风和摄像头才能录制屏幕上的操作和声音&#xff0c;官方提示在第三方APP若有使用摄像头和…

Qt开发问题总结(1)

1、在使用QGraphicsView/Scene时需要将内存导出到pdf&#xff0c;有view.render和scene.render两种方式&#xff0c;在使用view.render时&#xff0c;注意item的cacheMode要设为QGraphicsItem::NoCache&#xff0c;否则pdf可能在多页的情况下文件很大、导出耗时。原因是次数每一…

15-ps命令

常用选项 aux axjf a&#xff1a;显示一个终端所有的进程u&#xff1a;显示进程的归属用户及内存使用情况x&#xff1a;显示没有关联控制终端j&#xff1a;显示进程归属的进程组idf&#xff1a;以ASCII码的形式显示出进程的层次关系 ps aux其中| more是只显示一部分内容&…

Jenkins 备份恢复插件 ThinBackup

系统环境&#xff1a; Jenkins 版本&#xff1a;2.213 一、简介 在部署完 Jenkins 后首先要准备的就是数据备份问题&#xff0c;尤其是在生产环境下的 Jenkins&#xff0c;如果数据丢失很可能导致项目上线和开发时间受到影响&#xff0c;所以备份数据很重要。还好&#xff0c;…

西门子博途WINCC精致触摸屏配方实用一例

我们现场有一台设备&#xff0c;是用来锯切钢坯的&#xff0c;里面有几个重要的参数&#xff0c;一开始投产的时候厂家没有做配方功能&#xff0c;需要操作人员每次换钢坯就需要手动计算然后输入&#xff0c;后来有时间我就做了个这个定尺管理的功能&#xff0c;方便了操作人员…

五丰黎红针对主厨开展精准营销,“星厨俱乐部”平台助力调味品快速动销。

以“质量”为核心&#xff0c;以“绿色发展”为引领&#xff0c;致力于打造中国味道的调味品企业五丰黎红&#xff0c;长期以来不断改革生产设备及创新工艺&#xff0c;已发展成为国家农&#xff08;林&#xff09;业产业化龙头企业、省卓越绩效模式先进企业、省服务业企业50强…

由于安全设置错误,远程桌面连接失败怎么办?

问题&#xff1a;远程桌面安全设置错误&#xff1f; “我是一名IT经理&#xff0c;需要经常使用远程桌面连接到办公室的电脑。近期&#xff0c;我在使用远程桌面时&#xff0c;远程桌面提示‘由于安全设置错误&#xff0c;客户端无法连接到远程计算机。’我不清楚是什么原因所…

中国目前比较有影响力的人物颜廷利:物质与无知通音

既然是在中国优秀传统文化之根-汉语当中&#xff0c;汉字‘物质’二字跟‘无知’通音&#xff0c;因此&#xff0c;面对当前金钱肆虐、物欲横流的现实生活&#xff0c;当人类众生把‘物质’&#xff08;无知&#xff09;生活看的太真、太重时&#xff0c;那么&#xff0c;这就很…

手撸XXL-JOB(一)——定时任务的执行

SpringBoot执行定时任务 对于定时任务的执行&#xff0c;SpringBoot提供了三种创建方式&#xff1a; 1&#xff09;基于注解(Scheduled) 2&#xff09;基于接口&#xff08;SchedulingConfigurer&#xff09; 3&#xff09;基于注解设定多线程定时任务 基于Scheduled注解 首…

c#多态性的应用

设计一个电脑游戏&#xff0c;游戏中有猪、牛、兔子、青蛙、鸭子等动物&#xff0c;这些动 物都继承于Vertebrata 类&#xff08;脊椎动物类&#xff09;&#xff0c;Vertebrata类有一个抽象方法Display()&#xff0c;每个动 物都从Vertebrata 类那里继承并重写了Display()方法…

Naive RAG 、Advanced RAG 和 Modular RAG 简介

简介&#xff1a; RAG&#xff08;Retrieval-Augmented Generation&#xff09;系统是一种结合了检索&#xff08;Retrieval&#xff09;和生成&#xff08;Generation&#xff09;的机制&#xff0c;用于提高大型语言模型&#xff08;LLMs&#xff09;在特定任务上的表现。随…

【Vue】Vue指令与生命周期以及组件化编码

目录 常用内置指令v-text与v-htmlv-text : 更新元素的 textContentv-html : 更新元素的 innerHTML注意&#xff1a;v-html有安全性问题&#xff01;&#xff01;&#xff01;&#xff01; v-once与v-prev-oncev-pre ref与v-cloakrefv-cloak 自定义指令案例定义语法配置对象中常…

Visual Studio Community : The prerelease has expired

最近使用旧版的离线安装包安装社区版Visual Studio2022&#xff08;Visual Studio Community 2022&#xff09;&#xff0c;安装完成后显示&#xff1a;The prerelease has expired。使用自己的微软账号登录还是一样的情况&#xff0c;只能强制退出。经过摸索后发现了一个解决方…

选择法(数值排序)(C语言)

一、运行结果&#xff1b; 二、源代码&#xff1b; # define _CRT_SECURE_NO_WARNINGS # include <stdio.h>//声明排序函数sort; void sort(int a[], int n);int main() {//初始化变量值&#xff1b;int i, a[10];//填充数组&#xff1b;printf("请输入10个整数\n&…