C++——string的模拟实现(下)

目录

成员函数

3.4 修改操作

(3)insert()函数

(4)pop_back()函数

(5)erase()函数

(6)swap()函数

3.5 查找操作

(1)find()函数

 (2)substr()函数

3.6 重载函数

(1)operator=赋值函数

(2)其他比较函数

(3)流插入和流提取

完整代码

结束语


第一篇链接:C++——string的模拟实现(上)

成员函数

3.4 修改操作
(3)insert()函数

insert() 函数用于在字符串的指定位置插入一个字符或字符串。

注意事项:

在移动字符串内容时,需要确保不会覆盖或丢失任何字符。

使用 memcpy() 时,要确保源和目标内存区域不重叠,否则可能导致未定义行为。

string.h:

	// insert()函数 void insert(size_t pos, char ch);	//插入字符void insert(size_t pos, const char* str);	//插入字符串

string.cpp:

// insert()函数
// 插入字符
void string::insert(size_t pos, char ch)
{assert(pos <= _size);if (_capacity == _size){size_t newcapacity = _capacity == 0 ? 4 : 2 * _capacity;reserve(newcapacity);}size_t end = _size + 1;// 挪动元素while (pos < end){_str[end] = _str[end - 1];end--;}_str[pos] = ch;		//将ch放置到pos这个位置++_size;			//更新_size
}// 插入字符串
void string::insert(size_t pos, const char* str)
{assert(pos <= _size);size_t len = strlen(str);	// 要插入的字符串长度if (_size + len > _capacity){reserve(_size + len);}// 从后向前移动字符串内容以腾出空间  for (size_t i = _size; i >= pos; --i){_str[i + len] = _str[i];}// 复制新字符串到指定位置  memcpy(_str + pos, str, len);// 添加新的字符串结束符  _str[_size + len] = '\0';// 更新字符串长度  _size += len;
}

来个简单的代码测试一下:

(4)pop_back()函数

pop_back() 函数用于移除字符串末尾的字符。

实现:将字符串末尾的字符设置为 '\0'。 更新字符串长度 _size。

string.h:

// pop_back()函数
void pop_back();

string.cpp:

// pop_back()函数
void string::pop_back()
{_str[_size - 1] = '\0';--_size;
}
(5)erase()函数

erase() 函数用于从字符串中移除指定位置的字符或子字符串。

string.h:

// erase()函数
void erase(size_t pos = 0, size_t len = npos);

string.cpp:

// erase()函数
void string::erase(size_t pos,size_t len)
{assert(pos < _size);				 if (len == npos || len >= _size - pos){_str[pos] = '\0';		// 位置pos置为'\0'_size = pos;			// 有效元素个数为pos个}else	// len小于后面的字符个数{// 将后面的字符拷贝到pos位置strcpy(_str + pos, _str + pos + len);_size -= len;			// 更新有效元素}
}

erase()需要额外定义一个类成员变量npos来实现,它为无符号数的-1,一般为整型的最大值。

string.h:

public:static const size_t npos;

string.cpp:

const size_t string::npos = -1;

简单的对上面两个函数进行测试:

(6)swap()函数

swap() 函数用于交换两个字符串的内容。

string.h:

// swap()函数
void swap(string& str);

string.cpp:

// swap()函数
void string::swap(string& str)
{// 这里我们调用std库中的swap()函数std::swap(_str, str._str);std::swap(_capacity, str._capacity);std::swap(_size, str._size);
}

测试:

3.5 查找操作
(1)find()函数

find() 函数用于在字符串中查找指定字符或子字符串的位置。

我们在这里实现从指定位置开始向后遍历查找指定字符或者子字符串。

如果找到,则返回子字符串在字符串中的起始位置;否则返回 npos。

string.h:

// find()函数
size_t find(char ch, size_t pos);
size_t find(const char* str, size_t pos);

string.cpp:

// find()函数
// 寻找字符
size_t string::find(char ch, size_t pos)
{for (int i = pos; i < _size; i++){if (_str[i] == ch){return i;}}return npos;
}// 从pos开始寻找字符串
size_t string::find(const char* str, size_t pos)
{char* p = strstr(_str + pos, str);if (p){return p - _str;}else{return npos;}
}
 (2)substr()函数

substr() 函数用于获取字符串的子字符串。

string.h:

// substr()函数
string substr(size_t pos = 0, size_t len = npos);

string.cpp:

// substr()函数
string string::substr(size_t pos, size_t len)
{string str;if (len == npos || len >= _size - pos){for (size_t i = pos; i < _size; i++){str += _str[i];}}else{for (size_t i = pos; i < pos + len; i++){str += _str[i];}}return str;
}
3.6 重载函数

在C++中,函数重载允许我们为同一函数名创建多个版本,这些版本可以有不同的参数列表(参数的数量或类型不同)。通过这种方式,我们可以使代码更加简洁和易于理解。

(1)operator=赋值函数

赋值运算符(operator=)用于将一个对象的内容复制到另一个对象中。对于自定义的字符串类,我们需要自己实现这个运算符,以确保正确地管理内存。

string.h:

// operator=赋值函数
string& operator=(const string& s);

string.cpp:

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

来测试一下:

这里我们可以对以上的代码进行优化:

// operator= 函数
string& string::operator=(const string& str)
{if (this != &str){string tmp(str._str);	// 调用构造函数swap(tmp);				// 将tmp与this交换}return *this;
}

原始实现中,我们首先检查自赋值(即对象试图将自己赋值给自己),然后分配足够的内存来存储新字符串,复制内容,并释放旧内存。这种方法虽然有效,但可能会导致性能问题,特别是当字符串很长时,因为涉及到多次内存分配和释放。

优化后的实现采用了“拷贝-交换”技术。这种方法通过创建一个临时对象来存储要赋值的字符串,然后使用swap函数交换临时对象和当前对象的内容。由于swap函数通常实现得非常高效(只需交换指针),这种方法可以显著提高性能,并减少内存分配的次数。

(2)其他比较函数

每个比较运算符都基于strcmp函数的返回值来实现。

这些函数之间是可以复用的。

string.h:

// 比较函数
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;

string.cpp:

// 比较函数
bool string::operator<(const string& str) const
{return strcmp(_str, str._str) < 0;
}
bool string::operator<=(const string& str) const
{return *this < str || *this == str;
}
bool string::operator>(const string& str) const
{return !(*this <= 0);
}
bool string::operator>=(const string& str) const
{return !(*this <= str);
}
bool string::operator==(const string& str)const
{return strcmp(_str, str._str) == 0;
}
bool string::operator!=(const string& str)const
{return !(*this == str);
}void string::clear()
{_str[0] = '\0';_size = 0;
}
(3)流插入和流提取

我们接下来试着实现流插入和流提取:

注意,我们不能将这两个函数定义放在string类里

在 C++ 中,当我们使用 std::cout << d1 或 std::cin >> d1 这样的表达式时,左侧的 std::cout 或 std::cin 是对象,而右侧的 d1 是我们想要输出或输入的数据。运算符函数需要能够接受这两个对象作为参数。 如果 << 或 >> 运算符被定义为 std::string 的成员函数,那么它们将需要额外的 this 指针来访问类的成员变量,这将导致需要三个参数(this 指针,左操作数,右操作数),这与标准的运算符用法不兼容。

string.h:

//流插入和流提取
istream& operator>>(istream& is, string& str);
ostream& operator<<(ostream& os, const string& str);

string.cpp:

// 流输入
istream& operator>>(istream& is, string& str)
{// 清除字符串 str 中的现有内容  str.clear();char ch;// 从输入流 is 中读取第一个字符到 ch 中  ch = is.get();// 定义一个字符数组(缓冲区)buffchar buff[128];size_t i = 0;// 循环读取字符,直到遇到空格或换行符为止  while (ch != ' ' && ch != '\n'){// 将字符 ch 存储到缓冲区 buff 的当前位置  buff[i++] = ch;  if (i == 127){// 在缓冲区末尾添加字符串结束符 '\0'buff[i++] = '\0';str += buff;// 重置索引 i,为下一轮存储字符做准备  i = 0;}// 从输入流中读取下一个字符到 ch 中  ch = is.get();}// 循环结束后,检查缓冲区中是否还有未处理的字符  if (i > 0){// 在缓冲区末尾添加字符串结束符 '\0'  buff[i] = '\0';// 将缓冲区的内容追加到字符串 str 中  str += buff;}return is;
}
// 流提取
ostream& operator<<(ostream& os, const string& str)
{for (size_t i = 0; i < str.size(); i++){os << str[i];}return os;
}

测试一下:

完整代码

string.h:

#include<iostream>
#include<assert.h>
#include<stdbool.h>
using namespace std;namespace My_string
{class string{public:typedef char* iterator;			//将char*重命名为iteratortypedef const char* const_iterator;//const版本的iteratorconst_iterator begin() const;	//提供const_iterator begin()函数const_iterator end() const;		//提供const_iterator end()函数//非const版本的iteratoriterator begin();				//提供iterator begin()函数iterator end();					//提供iterator end()函数string(const char* str = " ");	//构造函数~string();						//析构函数string(const string& str);		//拷贝构造函数const char* c_str() const;		// c_str()函数size_t size() const;			// size()函数size_t capacity() const;		// capacity()函数bool empty() const;				// empty()函数// resize()函数void resize(size_t n, char ch = '\0');// 非const版本char& operator[](size_t pos);	//operator[]函数// const版本const char& operator[](size_t pos)const;// 预留空间void reserve(size_t n);// 尾插一个字符void push_back(char ch);// 尾插一个字符串void append(const char* str);//operator+=函数可以构成重载,函数名相同,参数不同string& operator+=(char ch);			// 字符相加string& operator +=(const char* str);	// 字符串相加// insert()函数 void insert(size_t pos, char ch);	//插入字符void insert(size_t pos, const char* str);	//插入字符串// erase()函数void erase(size_t pos = 0, size_t len = npos);// pop_back()函数void pop_back();// find()函数size_t find(char ch, size_t pos);size_t find(const char* str, size_t pos);// substr()函数string substr(size_t pos = 0, size_t len = npos);// operator=赋值函数string& operator=(const string& s);// swap()函数void swap(string& str);// 比较函数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;// clear()函数void clear();public:static const size_t npos;private:char* _str;			// 指向字符串的指针size_t _size;		// 有效字符个数size_t _capacity;	// 有效空间个数};//流插入和流提取istream& operator>>(istream& is, string& str);ostream& operator<<(ostream& os, const string& str);
}

string.cpp:

#include"string.h"
namespace My_string 
{const size_t string::npos = -1;string::iterator string::begin(){return _str;}string::iterator string::end(){return _str + _size;}string::const_iterator string::begin() const{return _str;}string::const_iterator string::end() const{return _str + _size;}// 构造函数string::string(const char* str){_size = strlen(str);_capacity = _size;_str = new char[_capacity + 1];		// +1用于储存'\0'strcpy(_str, str);}// 析构函数string::~string(){delete[] _str;_str = nullptr;_size = _capacity = 0;}// 拷贝构造函数(1)string::string(const string& str){_str = new char[str._capacity + 1];	//额外多给一个空间,用于存放'/0'strcpy(_str, str._str);		//拷贝数据_capacity = str._capacity;	//设置容量_size = str._size;			//设置有效数据个数}// 拷贝构造函数(2)//string::string(const string& str)//{//	string tmp(str._str);//	std::swap(tmp._str, _str);//	std::swap(tmp._size, _size);//	std::swap(tmp._capacity, _capacity);//}// 拷贝构造函数(3)//string::string(const string& str)//{//	string tmp(str._str);//	swap(tmp);			// 这里的swap我们接下来会定义//}// c_str()函数const char* string::c_str() const{return _str;}// size()函数size_t string::size() const{return _size;}// capacity()函数size_t string::capacity() const{return _capacity;}// empty()函数bool string::empty() const{return _size == 0;}// operator[]函数char& string::operator[](size_t pos){assert(pos < _size);return _str[pos];}// const版本const char& string::operator[](size_t pos)const{assert(pos < _size);return _str[pos];}// 预留空间void string::reserve(size_t n){if (n > _capacity){char* tmp = new char[n + 1];strcpy(tmp, _str);delete[] _str;_str = tmp;_capacity = n;}}// resize()函数void string::resize(size_t n, char ch){if (n > _size){if (n > _capacity){reserve(n);}// 使用 memset 函数将字符 ch // 填充到新添加的空间中memset(_str + _size, ch, n - _size);}_size = n;_str[n] = '\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 string::append(const char* str){size_t len = strlen(str);if (_size + len > _capacity) {reserve(_size + len);}strcpy(_str+_size, str);_size += len;			}string& string::operator+=(char ch){// 调用push_back()函数push_back(ch);		return *this;}string& string::operator+=(const char* str){append(str);return *this;}// insert()函数// 插入字符void string::insert(size_t pos, char ch){assert(pos <= _size);if (_capacity == _size){size_t newcapacity = _capacity == 0 ? 4 : 2 * _capacity;reserve(newcapacity);}size_t end = _size + 1;// 挪动元素while (pos < end){_str[end] = _str[end - 1];end--;}_str[pos] = ch;		//将ch放置到pos这个位置++_size;			//更新_size}// 插入字符串void string::insert(size_t pos, const char* str){assert(pos <= _size);size_t len = strlen(str);	// 要插入的字符串长度if (_size + len > _capacity){reserve(_size + len);}// 从后向前移动字符串内容以腾出空间  for (size_t i = _size; i >= pos; --i){_str[i + len] = _str[i];}// 复制新字符串到指定位置  memcpy(_str + pos, str, len);// 添加新的字符串结束符  _str[_size + len] = '\0';// 更新字符串长度  _size += len;}// erase()函数void string::erase(size_t pos,size_t len){assert(pos < _size);				 if (len == npos || len >= _size - pos){_str[pos] = '\0';		// 位置pos置为'\0'_size = pos;			// 有效元素个数为pos个}else	// len小于后面的字符个数{// 将后面的字符拷贝到pos位置strcpy(_str + pos, _str + pos + len);_size -= len;			// 更新有效元素}}// pop_back()函数void string::pop_back(){_str[_size - 1] = '\0';--_size;}// find()函数// 寻找字符size_t string::find(char ch, size_t pos){for (int i = pos; i < _size; i++){if (_str[i] == ch){return i;}}return npos;}// 从pos开始寻找字符串size_t string::find(const char* str, size_t pos){char* p = strstr(_str + pos, str);if (p){return p - _str;}else{return npos;}}// substr()函数string string::substr(size_t pos, size_t len){string str;if (len == npos || len >= _size - pos){for (size_t i = pos; i < _size; i++){str += _str[i];}}else{for (size_t i = pos; i < pos + len; i++){str += _str[i];}}return str;}// operator= 函数//string& string::operator=(const string& str)//{//	if (this != &str)//	{//		char* tmp = new char[str._capacity + 1];//		strcpy(tmp, str._str);//		delete[] _str;//		_str = tmp;//		_size = str._size;//		_capacity = str._capacity;//	}//	return *this;//}// operator= 函数string& string::operator=(const string& str){if (this != &str){string tmp(str._str);	// 调用构造函数swap(tmp);				// 将tmp与this交换}return *this;}// swap()函数void string::swap(string& str){// 这里我们调用std库中的swap()函数std::swap(_str, str._str);std::swap(_capacity, str._capacity);std::swap(_size, str._size);}// 比较函数bool string::operator<(const string& str) const{return strcmp(_str, str._str) < 0;}bool string::operator<=(const string& str) const{return *this < str || *this == str;}bool string::operator>(const string& str) const{return !(*this <= 0);}bool string::operator>=(const string& str) const{return !(*this <= str);}bool string::operator==(const string& str)const{return strcmp(_str, str._str) == 0;}bool string::operator!=(const string& str)const{return !(*this == str);}void string::clear(){_str[0] = '\0';_size = 0;}// 流输入istream& operator>>(istream& is, string& str){// 清除字符串 str 中的现有内容  str.clear();char ch;// 从输入流 is 中读取第一个字符到 ch 中  ch = is.get();// 定义一个字符数组(缓冲区)buffchar buff[128];size_t i = 0;// 循环读取字符,直到遇到空格或换行符为止  while (ch != ' ' && ch != '\n'){// 将字符 ch 存储到缓冲区 buff 的当前位置  buff[i++] = ch;  if (i == 127){// 在缓冲区末尾添加字符串结束符 '\0'buff[i++] = '\0';str += buff;// 重置索引 i,为下一轮存储字符做准备  i = 0;}// 从输入流中读取下一个字符到 ch 中  ch = is.get();}// 循环结束后,检查缓冲区中是否还有未处理的字符  if (i > 0){// 在缓冲区末尾添加字符串结束符 '\0'  buff[i] = '\0';// 将缓冲区的内容追加到字符串 str 中  str += buff;}return is;}// 流提取ostream& operator<<(ostream& os, const string& str){for (size_t i = 0; i < str.size(); i++){os << str[i];}return os;}
}

test.cpp:

#include"string.h"
#include<iostream>using namespace std;void test1()
{// 测试构造函数  My_string::string s1("Hello");cout << "s1: " << s1.c_str() << endl;// 测试拷贝构造函数  /*My_string::string s2(s1);cout << "s2 (s1): " << s2.c_str() << endl;	*/
}void test2()
{My_string::string str("Hello");str[0] = 'h';cout << str.c_str() << endl;//使用operator[]函数打印for (size_t i = 0; i < str.size(); i++){cout << str[i] << " ";}cout << endl;
}void test3()
{My_string::string str("hello");for (auto i : str){cout << i << " ";}cout << endl;My_string::string::iterator it1 = str.begin();while (it1 != str.end()){cout << *it1 << " ";++it1;}cout << endl;My_string::string::iterator it2 = str.end();if (it2 != str.begin()) { // 检查避免直接解引用 end()  --it2; // 先移动到一个有效的位置  while (it2 != str.begin()){std::cout << *it2 << " ";--it2;}std::cout << *it2 << " "; // 输出最后一个字符(begin() 之前的字符)  }std::cout << std::endl;
}void test4()
{My_string::string str("hello");cout << str.c_str() << endl;str.push_back('A');cout << str.c_str() << endl;str.append("world");cout << str.c_str() << endl;str += 'C'; cout << str.c_str() << endl;
}void test5()
{My_string::string str("hello world");str.insert(6, 'x');cout << str.c_str() << endl;str.insert(6, "yyyyy");cout << str.c_str() << endl;str.insert(0, 'X');cout << str.c_str() << endl;
}void test6()
{My_string::string str("hello world");str.pop_back();cout << str.c_str() << endl;str.erase(2, 3);cout << str.c_str() << endl;
}void test7()
{My_string::string str("hello world");cout << str.find('l', 0) << endl;cout << str.find("ld", 0) << endl;
}void test8()
{My_string::string str1("hello");My_string::string str2("world");cout << str1.c_str() << endl;cout << str2.c_str() << endl;str1.swap(str2);cout << str1.c_str() << endl;cout << str2.c_str() << endl;
}void test9()
{My_string::string str("hello World");cout << str.substr(2, 2) << endl;cout << str.substr(7, 3) << endl;
}void test10()
{My_string::string str1("hello world");My_string::string str2("xxxxxxxxxxx");str1 = str2;cout << str1.c_str() << endl;cout << str2.c_str() << endl;My_string::string str3("yyyyy");str1 = str3;cout << str1.c_str() << endl;cout << str3.c_str() << endl;
}void test11()
{My_string::string str("hello");str.resize(10, '*');cout << str.c_str() << endl;str.reserve(15);cout << str.c_str() << endl;
}void test12()
{My_string::string str("XXX");cin >> str;cout << str << endl;
}int main()
{//test1();//test2();//test3();//test4();//test5();//test6();//test7();//test8();//test9();//test10();//test11();test12();return 0;
}

结束语

实验周太忙啦!!!一直没时间写。。。

感谢各位大佬的阅读!!!

求点赞收藏评论关注!!!

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

vue通过JSON文件生成KML文件源码

可以使用封装的json解析器进行JSON数据获取&#xff0c;读取点的经度、维度、高程等数据&#xff0c;再使用对应的KML文件生成函数使用该源码下载KML文件&#xff08;固定KML生成&#xff1a;js模板式生成大疆上云kml文件&#xff08;含详细注释&#xff0c;已封装成函数&#…

unity中GameObject介绍

在 Unity 中&#xff0c;Cube和Sphere等基本几何体是 Unity 引擎的内置预制体&#xff08;Prefabs&#xff09;&#xff0c;它们属于 Unity 中的GameObject 系统&#xff0c;可以在 Unity 的 Hierarchy 视图或 Scene 视图中右键点击&#xff0c;然后在弹出的菜单中选择 3D Obje…

MySQL 数据出海之数据同步方案

优质博文&#xff1a;IT-BLOG-CN 数据库出海流程 【1】业务出海&#xff1a;1&#xff09;数据库出海&#xff1b;2&#xff09;应用出海&#xff1b;3&#xff09;流量分发&#xff1b; 【2】数据库出海&#xff1a;涉及业务方、信安、DBA和框架组。 数据库出海&#xff0c…

Go 语言基础教程:7.Switch 语句

在这篇教程中&#xff0c;我们将学习 Go 语言中的 switch 语句&#xff0c;它是条件分支的重要结构。我们将通过一个示例程序逐步解析 switch 的不同用法。 package mainimport ("fmt""time" )func main() {i : 2fmt.Print("Write ", i, " …

Python画笔案例-094 绘制 神奇彩条动画

1、绘制 神奇彩条动画 通过 python 的turtle 库绘制 神奇彩条动画,如下图: 2、实现代码 绘制 神奇彩条动画,以下为实现代码: """神奇彩条动画.py一个用海龟画图的图章功能配合颜色渐变和动画原理做成的动画。 """ from random import randi…

重学SpringBoot3-Spring WebFlux之SSE服务器发送事件

更多SpringBoot3内容请关注我的专栏&#xff1a;《SpringBoot3》 期待您的点赞&#x1f44d;收藏⭐评论✍ Spring WebFlux之SSE服务器发送事件 1. 什么是 SSE&#xff1f;2. Spring Boot 3 响应式编程与 SSE为什么选择响应式编程实现 SSE&#xff1f; 3. 实现 SSE 的基本步骤3.…

MySQL-32.索引-操作语法

一.语法 二.代码实现 指定某个字段为主键&#xff0c;其实就是建立一个主键索引。而指定某个字段唯一&#xff0c;就是建立一个唯一索引。 -- 索引 -- 创建&#xff1a;为tb_emp表的name字段建立一个索引 create index idx_emp_name on tb_emp(name);-- 查询&#xff1a;查…

Handler、Looper、message进阶知识

Android Handler、Looper、Message的进阶知识 在Android开发中&#xff0c;Handler、Looper和Message机制是多线程通信的核心。为了深入理解并优化它们的使用&#xff0c;尤其是在高并发和UI性能优化中&#xff0c;可以利用一些高级特性。 1. Handler的高阶知识 Handler在基本…

Axure随机验证码高级交互

亲爱的小伙伴&#xff0c;在您浏览之前&#xff0c;烦请关注一下&#xff0c;在此深表感谢&#xff01; 课程主题&#xff1a;字母数字随机验证码高级交互 主要内容&#xff1a;4位字母数字随机验证码生成、错误提示与State状态同步 应用场景&#xff1a;登录验证码、其他类…

Linux - 文件描述符 | 文件系统 | 软硬链接

文章目录 一、文件描述符1、补充知识2、文件描述符是什么&#xff1f;3、处理文件的系统调用接口4、文件描述符的分配规则5、简单理解Linux下一切皆文件6、FILE7、重定向 二、文件系统三、软硬链接1、软连接2、硬链接3、软硬链接对比 一、文件描述符 1、补充知识 &#xff08;…

5G NR:BWP入门

简介 5G NR 系统带宽比4G LTE 大了很多&#xff0c;4G LTE 最大支持带宽为20MHz&#xff0c; 而5G NR 的FR1 最大支持带宽为100MHz&#xff0c; FR2 最大支持带宽为 400MHz。 带宽越大&#xff0c;意味了终端功耗越多。为了减少终端的功耗&#xff0c;5G NR 引入了BWP(Band Wid…

从零实现数据结构:一文搞定所有排序!(下集)

1.快速排序 思路框架&#xff1a; 在有了前面冒泡选择插入希尔排序之后&#xff0c;人们就在想能不能再快一点&#xff0c;我们知道排序算法说人话就是把大的往后放小的往前放&#xff0c;问题就在于如何更快的把大的挪到数组队尾小的挪到数组前面。这里我们先总结一下上集前…

NVR录像机汇聚管理EasyNVR多品牌NVR管理工具/设备视频报警功能详解

在科技日新月异的今天&#xff0c;视频监控系统作为现代社会的“第三只眼”&#xff0c;正以前所未有的方式深刻影响着我们的生活与社会结构。从公共场所的安全监控到个人生活的记录分享&#xff0c;视频监控系统以其独特的视角和功能&#xff0c;为社会带来了诸多好处&#xf…

【Linux】————磁盘与文件系统

作者主页&#xff1a; 作者主页 本篇博客专栏&#xff1a;Linux 创作时间 &#xff1a;2024年10月17日 一、磁盘的物理结构 磁盘的物理结构如图所示&#xff1a; 其中具体的物理存储结构如下&#xff1a; 磁盘中存储的基本单位为扇区&#xff0c;一个扇区的大小一般为512字…

Python 自动化运维:Python基础知识

Python 自动化运维&#xff1a;Python基础知识 目录 &#x1f4ca; Python 基础复习 数据类型、控制结构与常用函数面向对象编程&#xff08;OOP&#xff09;与类的使用函数式编程概念与 lambda 表达式异常处理与日志记录的基本实践 1. &#x1f4ca; Python 基础复习 数据…

二十二、Python基础语法(模块)

模块(module)&#xff1a;在python中&#xff0c;每个代码文件就是一个模块&#xff0c;在模块中定义的变量、函数、类别人都可以直接使用&#xff0c;如果想要使用别人写好的模块&#xff0c;就必须先导入别人的模块&#xff0c;模块名须满足标识符规则&#xff08;由字母、数…

SwiftUI(三)- 渐变、实心形状和视图背景

引言 在现代的应用的UI设计中&#xff0c;渐变和形状背景为界面带来了丰富的层次与视觉效果&#xff0c;而SwiftUI提供了一系列简单且强大的API&#xff0c;可以轻松实现这些效果。在这篇文章中&#xff0c;我们将介绍SwiftUI中的渐变、实心形状和视图背景的基础用法&#xff…

【论文阅读】Learning persistent homology of3D point clouds

摘要 motivation&#xff1a;PD计算过程非常耗时&#xff0c;严重限制了TDA的应用 本文提出了一种端到端的神经网络模型TopologyNet&#xff0c;用于直接从3D点云数据中拟合拓扑表示。TopologyNet显著减少了生成拓扑表示的计算时间&#xff0c;并在实际实例中保持了较小的近似…

Python4

4. 更多控制流工具 除了刚介绍的 while 语句&#xff0c;Python 还用了一些别的。我们将在本章中遇到它们。 4.1. if 语句 if elif else if x<0: x 0 print(Negative changed to zero) elif x0: print( zero) else: print(More) 4.2. for 语句 Pyth…

2024.7最新子比主题zibll7.9.2开心版源码+授权教程

授权教程&#xff1a; 1.进入宝塔搭建一个站点 绑定 api.zibll.com 域名 并上传 index.php 文件 2.设置伪静态 3.开启SSL证书&#xff0c;找一个能用的域名证书&#xff0c;将密钥(KEY)和证书(PEM格式)复制进去即可 4.在宝塔文件地址栏中输入 /etc 找到 hosts文件并打开&a…