string类的常用接口及模拟实现

目录

基础知识

常用接口

1>常见构造

2>容量操作

3>访问及遍历操作

1.迭代器

2.反向迭代器

3.范围for

4.auto

4>修改操作

5>非成员函数

其它接口

模拟实现

string.h

string.cpp

         swap()


基础知识

string是一个管理字符的类,定义在std命名空间的。当我们使用using namespace std;后,就可以直接使用string类,而不用每次在string前加上std:: 前缀。这样能减少代码的冗余,提高代码的可读性,但是有可能会引起命名冲突

#include<string>
// 第一种
std::string mystr1;
// 第二种
using namespace std;
string mystr2;

所以我们在使用string类时,必须包含#include头文件以及using namespace std;(根据具体情况而定)

vs下的string结构:总共占28个字节,内部结构有

① 一个联合体,用来定义string中字符串的存储空间,当字符串长度小于16时,使用内部固定的字符数组来存放;当当字符串长度大于等于16时,从堆上开辟空间(大多数情况下字符串的长度都小于16,那么string对象创建好后,内部已经有了16个字符数组的固定空间,不需要通过堆创建,效率高)

② 一个size_t字段保存字符串长度

③ 一个size_t字段保存从堆上开辟空间总的容量

④ 一个指针

所以总共占16+4+4+4=28个字节

g++下string的结构:总共占4个字节,内部只包含一个指针,该指针将来指向一块堆空间,内部包含了三个字段:① 空间总大小 ② 字符串有效长度 ③ 引用计数

常用接口

string类的接口有很多,下面我就介绍一些常用的接口,大家可以配合C++官网的介绍来看下面的内容,因为C++官网只有英文,看起来会有点吃力

string - C++ Reference

1>常见构造

函数名称功能说明
string()构造空的string类对象,即空字符串
string(const char* s)用C-string来构造string类对象
string(const string& s)拷贝构造函数
string(size_t n, char c)ring类对象中包含n个字符c
int main()
{
    // 重点
	string s1;
	string s2("hello tianci");
    string s3(s2);
    
	string s4(10, 'c');
    string s5("aabb", 2);
	string s5(s3, 2, 3);
	string s6(s3, 4);
	return 0;
}

2>容量操作

函数名称功能说明
size返回字符串有效字符长度
length返回字符串有效字符长度
capacity返回空间总大小
empty检测字符串释放为空串,是返回true,否则返回false
clear清空有效字符
reserve为字符串预留空间
resize将有效字符的个数改成n个,多出的空间用字符c填充
int main()
{
	string s("hello tianci");
	cout << s.size() << endl;
	cout << s.length() << endl;
	cout << s.capacity() << endl;

	s.resize(10);
	cout << s << endl;
	s.resize(12, 'w');
	cout << s << endl;

	s.reserve(100);
	cout << s.capacity() << endl;

	s.clear();
	cout << s.empty() << endl;

	return 0;
}

这里有几点需要注意:

① size()与length()方法底层实现原理完全相同,引入size()的原因是为了与其他容器的接口保持一致,建议使用size()

② clear()只是将string中有效字符清空,不改变底层空间大小

③ resize(n)用0来填充多出的元素空间,resize(size_t n, char c)用字符c来填充多出的元素空间(resize在改变元素个数时,如果是将元素个数增多,可能会改变底层容量的大小,如果是将元素个数减少,底层空间总大小不变)

④ reserve(size_t res_arg=0):为string预留空间,不改变有效元素个数,当reserve的参数小于string的底层空间总大小时,reserve不会改变容量大小

3>访问及遍历操作

函数名称功能说明
operator[]返回pos位置的字符,const string类对象调用
begin+endbegin获取一个字符的迭代器 + end获取最后一个字符下一个位置的迭代器
rbegin+rendbegin获取一个字符的迭代器 + end获取最后一个字符下一个位置的迭代器
范围forC++11支持更简洁的范围for的新遍历方式
int main()
{
	string s("hello tianci");
	for (int i = 0; i < s.size(); i++)
	{
		cout << s[i];
	}
	cout << endl;
	string::iterator it1 = s.begin();
	while (it1 != s.end())
	{
		cout << *it1;
		++it1;
	}
	cout << endl;
	string::reverse_iterator it2 = s.rbegin();
	while (it2 != s.rend())
	{
		cout << *it2;
		++it2;
	}
	cout << endl;
	for (char ch : s)
	{
		cout << ch;
	}
	return 0;
}

1.迭代器

迭代器是一种用于遍历容器(如数组、链表、向量等)中元素的对象,就像是一个指向容器中某个元素的“指针”,还可以通过迭代器来读取、修改元素,以及在容器中移动位置

这里我就简单介绍一下迭代器的使用,因为底层比较复杂,往后再详细介绍

int main()
{
	string s1("hello tianci");
	string::iterator it1 = s1.begin();
	while(it1 != s1.end())
	{
		cout << (*it1);
		++it1;
	}
    
    const string s2("hello tianci");
	string::const_iterator it2 = s2.begin();
    
	return 0;
}

这里有几点需要注意的是:

① string::iterator是C++中string类的一个成员类型,并不是一个类内部的类,而是一个类型别名,用于表示string对象的迭代器

② 这里用 < 替换 != 也可以,但是建议使用 != ,因为例如链表的存储结构不是连续的,所以就不能用 < ,而 != 可以通用

③可以这样理解, s.begin()指向字符串的首个有效字符,即指向'h',s.end()指向字符串最后一位有效字符的下一位,即指向'\0',("hello tianci"在内存中是以'\0'结尾的),所以s.begin()到s.end()是一个左闭右开的区间

2.反向迭代器

顾名思义,就是指从后往前迭代,所以s.rbegin()指向'i',s.rend()指向'h'的前一位,依然是一个左闭右开的区间,只不过是方向相反

int main()
{
	string s3("hello tianci");
	string::reverse_iterator it3 = s3.rbegin();
	while(it3 != s3.rend())
	{
		cout << (*it3);
		++it3;
	}
    
    const string s4("hello tianci");
	string::const_reverse_iterator it4 = s4.rbegin();
    
	return 0;
}

迭代器共有四种类型,在使用的时候需要判断对象的类型以及顺序

3.范围for

范围for是C++11的一个小语法

① 原理:范围for底层是迭代器

② 适用于容器遍历和数组遍历

③ 自动取容器的数据赋值给左边的对象

④ 自动++,自动判断结束

int main()
{
    int arr[] = {1,2,3,4};
	string s("hello tianci");
    for (int i : arr)
    {
        cout << i << ' ';
	}
    cout << endl;
	for (char ch: s)
	{
		cout << ch;
	}
	return 0;
}

需要注意的是,范围for是自动取容器的数据赋值给左边的对象,那就意味着依次取右边的值拷贝给左边的对象,所以在左边的代码中,ch的改变并不会影响原来的数据,而使用引用才会改变原来的数据

总结:对于遍历string类对象有三种方式:① 下标 + [] 的形式 ② 迭代器(通用) ③ 范围for

在这里我要介绍另一个C++11的小语法

4.auto

① C++11中,auto作为一个新的类型指示符来指示编译器,auto声明的变量必须由编译器在编译时期推导而得

② 用auto声明指针类型时,用auto和auto*没有任何区别,但用auto声明引用类型时则必须加&

③ 当在同一行声明多个变量时,这些变量必须是相同的类型,因为编译器实际只对第一个类型进行推导

④ auto可以作为函数的参数(C++20开始支持),也可以做返回值(谨慎使用,用不好很坑人)

⑤ auto不能直接用来声明数组

int main()
{
	std::map<std::string, std::string> dict;
	//std::map<std::string, std::string>::iterator dit = dict.begin();
	auto dit = dict.begin();
    return 0;
}

auto可以简化代码,替代写起来长的类型,所以auto使用起来会特别的香,但是使用时要注意一下细节

4>修改操作

函数名称功能说明
push_back在字符串后尾插字符c
append在字符串后追加一个字符串
operator+=在字符串后追加字符串str
c_str返回C格式字符串
find+npos从字符串pos位置开始往后找字符c,返回该字符在字符串中的位置
rfind从字符串pos位置开始往前找字符c,返回该字符在字符串中的位置
substr在str中从pos位置开始,截取n个字符,然后将其返回
int main()
{
	string s("hello");

	s.push_back(',');
	cout << s.c_str() << endl;
	s.append("tianci");
	cout << s.c_str() << endl;
	s += '!';
	cout << s.c_str() << endl;
	s += "!!";
	cout << s.c_str() << endl;

	int pos1 = s.find('l');
	int pos2 = s.find('l', 4);
	int pos3 = s.rfind('o');
	cout << pos1 << endl;
	cout << pos2 << endl;
	cout << pos3 << endl;

	string sub1 = s.substr(6, 4);
	cout << sub1.c_str() << endl;

	string sub2 = s.substr(6);
	cout << sub2.c_str() << endl;
	
	return 0;
}

在这里我想提一点,在string类中,npos是一个静态常量成员,定义为static const size_t npos = -1; 通常用于表示一个“无效”或“最大可能”的位置值

① 在find()中,如果没有找到匹配的字符,通常会返回npos,此时npos作为一个无效的值,就可以让我们快速知道是否找到

② 在substr()中,npos赋值给缺省参数,此时npos作为一个最大可能的值,相当于截取到字符串的末尾

另外对string操作时,如果可以预估要放多少字符,可以先通过reserve预留空间,这样做可以提高效率。在string尾部追加字符时,建议用+=,它可以连接单个字符或字符串,看起来也比较简洁

5>非成员函数

函数名称功能说明
operator+尽量少用,因为传值返回,导致深拷贝效率低
operator>>输入运算符重载
operator<<输出运算符重载
getline获取一行字符串
relational operators大小比较
int main()
{
	string s1;
	string s2("tianci");
	s1 = "hello " + s2;
	cout << s1.c_str() << endl;

	cout << (s1 == s2) << endl;
	cout << (s1 != s2) << endl;
	cout << (s1 <= s2) << endl;
	cout << (s1 >= s2) << endl;
	cout << (s1 < s2) << endl;
	cout << (s1 > s2) << endl;

	string s3;
	cin >> s3;
	cout << s3 << endl;

	string s4;
	getline(cin, s4);
	cout << s4 << endl;

	string s5;
	getline(cin, s4, '#');
	cout << s5 << endl;

	return 0;
}

这里有一个非常重要的点,那就是cin读取时,遇到空格或'\n'就会停止,因为它认为空格是分隔符的意思;而getline读取时,遇到空格并不会停止,而是遇到字符(不给的时候就用缺省值'\n')才会停止,如果给了字符,则遇到该字符才会停止读取

getline() 可能在一些OJ题要用到,比如要读取一个字符串,但不想遇到空格就停止,这时候getline() 就可以完美解决这个问题

另外我想介绍一个关于cout的点

int main()
{
	const char* p1 = "aabb";
	int* p2 = nullptr;
	
	cout << p1 << endl;			// aabb
	cout << p2 << endl;			// 00000000
	
	return 0;
}

如果用cout打印p1,因为p1是常量,它会自动解引用从而打印字符串的内容,而不是打印它的地址;p2就不会这样

下面有两个方法可以解决

cout << (void*)p1 << endl;	// 00FA9B30
printf("%p", p1);			// 00FA9B30

其它接口

下面介绍一些可能会遇到的接口

int main()
{
	string s1;
	// 最多能放多少字符,不同平台结果可能不一样
	cout << s1.max_size() << endl;

	string s2("hello tianci");
	// 正向查找在原字符串中第一个与指定字符串(或字符)中
	// 的某个字符匹配的字符,返回它的位置
	cout << s2.find_first_of("aoc") << endl;		// 第一个出现的o,所以返回下标4
	// 从第六个位置开始往后找
	cout << s2.find_first_of("aoc", 6) << endl;		// 第一个出现的是a,所以返回下标8

	string s3("hello tianci");
	// 反向查找
	cout << s3.find_last_of("aoc") << endl;			// 第一个出现的是c,所以返回下标10
	// 从第六个位置开始往前找
	cout << s3.find_last_of("aoc", 6) << endl;		// 第一个出现的是o,所以返回下标4

	// 在下标为0位置上插入字符c
	string s4("hello");
	s4.insert(0, "h");								// hhello
	// 在下标为3位置上插入字符串str
	string s5("hello");
	s5.insert(3, "!!!");							// hel!!!lo

	// 交换
	s4.swap(s5);

	// 删除下标为1位置开始的1个字符
	string s6("hello");
	s6.erase(1, 1);									// hllo

	// 翻转字符串
	string s7("hello");
	reverse(s7.begin(), s7.end());					// olleh

	// 从下标为3位置开始的1个字符替换成%%
	string s8("tianci");
	s8.replace(3, 1, "%%");							// tia%%ci
	
    // to_string的功能是将数据转换为字符串
    
	return 0;
}

这里有几点需要注意的是:

① find_first_of() 是从pos位置往后找,find_last_of() 是从pos位置往前找,返回的是特定字符串第一个出现在原字符串的字符的位置

② insert() 和 erase() 一般不使用,因为插入后还要移动数据,效率很低

③ reverse就是实现顺序逆置(比如1234,reverse后4321),它是一个函数模版,所以vector和list等都可以用

④ replace() 建议平替的时候使用,如果一换多个字符意味着又要移动数据,效率比较低

关于接口的介绍到这里就结束啦,如果大家遇到其它接口,可以尝试在C++官网读懂这个接口的作用,可以先猜再配合翻译软件理解这个接口实现的功能

模拟实现

string.h

#define _CRT_SECURE_NO_WARNINGS 1

#pragma once
#include<iostream>
#include<assert.h>
using namespace std;

namespace tianci
{
	class string
	{
	public:
		string(const char* str = "");
		string(const string& s);
		//string& operator=(const string& s);
		string& operator=(string s);


		~string();

		// iterator
		using iterator = char*;
		using const_iterator = const char*;
		iterator begin()
		{
			return _str;
		}
		iterator end()
		{
			return _str + _size;
		}
		const_iterator begin()const
		{
			return _str;
		}
		const_iterator end()const
		{
			return _str + _size;
		}

		// access
		char& operator[](size_t index)
		{
			assert(index < _size);
			return _str[index];
		}
		const char& operator[](size_t index)const
		{
			assert(index < _size);
			return _str[index];
		}

		// modify
		void push_back(char c);
		string& operator+=(char c);
		void append(const char* str);
		string& operator+=(const char* str);
		void clear();
		void swap(string& s);
		const char* c_str()const;

		// capacity
		size_t size()const;
		size_t capacity()const;
		bool empty()const;
		void resize(size_t n, char c = '\0');
		void reserve(size_t n);

		// 返回c在string中第一次出现的位置
		size_t find(char c, size_t pos = 0) const;
		// 返回子串s在string中第一次出现的位置
		size_t find(const char* s, size_t pos = 0) const;

		// 在pos位置上插入字符c/字符串str,并返回该字符的位置
		void insert(size_t pos, char c);
		void insert(size_t pos, const char* str);
		
		// 删除pos位置上的元素,并返回该元素的下一个位置
		void erase(size_t pos, size_t len);

		string substr(size_t pos, size_t len = npos);

		void swap(string& s);

	private:
		char* _str = nullptr;
		size_t _size = 0;
		size_t _capacity = 0;
	public:
		static const size_t npos;
	};

	void swap(string& s1, string& s2);

	bool operator== (const string& lhs, const string& rhs);
	bool operator!= (const string& lhs, const string& rhs);
	bool operator> (const string& lhs, const string& rhs);
	bool operator< (const string& lhs, const string& rhs);
	bool operator>= (const string& lhs, const string& rhs);
	bool operator<= (const string& lhs, const string& rhs);

	ostream& operator<<(ostream& os, const string& str);
	istream& operator>>(istream& is, string& str);
	istream& getline(istream& is, string& str, char delim = '\n');
}

关于string.h有一点就是static修饰的成员变量要在类外面定义,但如果是加了const 的成员变量可以在声明处给缺省值,就不用再到类外面定义(建议还是类内声明,类外定义,这个只是例外)

string.cpp

#include"string.h"

namespace tianci
{
	const size_t string::npos = -1;

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

	// 传统写法
	//string::string(const string& s)
	//{
	//	_str = new char[s._capacity + 1];
	//	strcpy(_str, s._str);
	//	_size = s._size;
	//	_capacity = s._capacity;
	//}
	//
	// 现代写法
	string::string(const string& s)
	{
		string tmp(s._str);
		swap(tmp);
	}

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


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

	void string::push_back(char c)
	{
		if (_size == _capacity)
		{
			reserve(_capacity == 0 ? 4 : 2 * _capacity);
		}
		_str[_size++] = c;
	}

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

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

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

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

	void string::swap(string& s)
	{
		tianci::string tmp = s;
		s = *this;
		*this = tmp;
	}

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

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

	size_t string::capacity()const
	{
		return _capacity;
	}

	bool string::empty()const
	{
		return _size == 0 ? true : false;
	}

	void string::resize(size_t n, char c)
	{
		if (n > _size)
		{
			if (n > _capacity)
			{
				reserve(n);
			}
			for (size_t i = 0; i < n - _size; i++)
			{
				_str[_size + i] = c;
			}
			_size = n;
		}
		else
		{
			_str[n] = c;
			_size = n;
		}
	}

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

	size_t string::find(char c, size_t pos) const
	{
		assert(pos < _size);
		for (size_t i = pos; i < _size; i++)
		{
			if (_str[i] == c)
				return i;
		}
		return npos;
	}

	size_t string::find(const char* s, size_t pos) const
	{
		assert(pos < _size);
		const char* ptr = strstr(_str + pos, s);
		if (ptr == nullptr)
		{
			return npos;
		}
		else
		{
			return ptr - _str; 
		}
	}

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

	void string::insert(size_t pos, const char* str)
	{
		assert(pos <= _size);
		size_t len = strlen(str);
		if (_size + len > _capacity)
		{
			size_t newcapacity = 2 * _capacity;
			if (_size + len > newcapacity)
			{
				newcapacity = _size + len;
				reserve(newcapacity);
			}
			_capacity = newcapacity;
		}

		size_t end = _size;
		while (end > pos - 1)
		{
			_str[end + len] = _str[end];
			--end;
		}
		for (size_t i = 0; i < len; i++)
		{
			_str[pos + i] = str[i];
		}
		_size += len;
	}

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

	string string::substr(size_t pos, size_t len)
	{
		assert(pos < _size);

		// 大于后面剩余串的长度,则直接取到结尾
		if (len > (_size - pos))
		{
			len = _size - pos;
		}

		tianci::string sub;
		sub.reserve(len);
		for (size_t i = 0; i < len; i++)
		{
			sub += _str[pos + i];
		}
		return sub;
	}

	void string::swap(string& s)
	{
		std::swap(_str, s._str);
		std::swap(_size, s._size);
		std::swap(_capacity, s._capacity);
	}
	
    // 成员函数
	
	// 全局函数
    
	void swap(string& s1, string& s2)
	{
		s1.swap(s2);
	}

	bool operator== (const string& lhs, const string& rhs)
	{
		return strcmp(lhs.c_str(), rhs.c_str()) == 0;
	}

	bool operator!= (const string& lhs, const string& rhs)
	{
		return !(lhs == rhs);
	}

	bool operator> (const string& lhs, const string& rhs)
	{
		return !(lhs <= rhs);
	}

	bool operator< (const string& lhs, const string& rhs)
	{
		return strcmp(lhs.c_str(), rhs.c_str()) < 0;
	}

	bool operator>= (const string& lhs, const string& rhs)
	{
		return !(lhs < rhs);
	}

	bool operator<= (const string& lhs, const string& rhs)
	{
		return lhs < rhs || lhs == rhs;
	}

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

	istream& operator>>(istream& is, string& str)
	{
		str.clear();
		int i = 0;
		char buff[256];
		char ch;
		ch = is.get();
		while (ch != ' ' && ch != '\n')
		{
			buff[i++] = ch;
			if (i == 255)
			{
				buff[i] = '\0';
				str += buff;
				i = 0;
			}
			ch = is.get();
		}
		if (i > 0)
		{
			buff[i] = '\0';
			str += buff;
		}
		return is;
	}

	istream& getline(istream& is, string& str, char delim)
	{
		str.clear();
		int i = 0;
		char buff[256];
		char ch;
		ch = is.get();
		while (ch != delim)
		{
			buff[i++] = ch;
			if (i == 255)
			{
				buff[i] = '\0';
				str += buff;
				i = 0;
			}
			ch = is.get();
		}
		if (i > 0)
		{
			buff[i] = '\0';
			str += buff;
		}
		return is;
	}
}

关于string.cpp的点就是,拷贝构造和赋值操作的现代写法巧妙地运用函数的this指针参数,还有就是出了作用域就会自动调用析构函数,不会担心内存泄漏,这些都是类和对象的知识点,其中一些函数实现成全局函数也是因为this指针,导致函数的参数固定,所以实现成全局函数会比较理想

swap()

最后再介绍一下swap函数,之所以我把它单拎出来,也是因为它比较重要

① 算法库swap:可以看到算法库中的swap是先拷贝构造一个对象,再去赋值,而赋值又要调用拷贝构造,后面还要调用析构函数,这样操作的代价有点大,效率特别低

② string类swap:string类的swap本质就是单纯调换指针的指向,并不需要再构造一个对象出来,代价相对小很多,效率也提高不少

之所以string类实现一个成员函数和一个全局函数,那是因为即使swap(a, b)这样写,也可以转为a.swap(b),所以全局函数的底层还是调用成员函数swap实现的,不仅string类,vector和list都实现了两个swap函数去交换,因为调用算法库中的swap函数代价实在是太大了

本篇文章到这里就结束啦,希望这些内容对大家有所帮助!

下篇文章见,希望大家多多来支持一下!

感谢大家的三连支持!

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

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

相关文章

Linux——多线程的控制

Linux——线程的慨念及控制-CSDN博客 文章目录 目录 文章目录 前言 一、线程函数的认识 1、基本函数的回顾 1、线程的创建pthread_create 2、线程阻塞pthread_join 3、线程退出pthread_exit 2、线程的分离pthread_detach 3、互斥锁初始化函数&#xff1a;pthread_mutex_init 4、…

计算机网络 (49)网络安全问题概述

前言 计算机网络安全问题是一个复杂且多维的领域&#xff0c;它涉及到网络系统的硬件、软件以及数据的安全保护&#xff0c;确保这些元素不因偶然的或恶意的原因而遭到破坏、更改或泄露。 一、计算机网络安全的定义 计算机网络安全是指利用网络管理控制和技术措施&#xff0c;保…

TCP状态转移图详解

状态 描述 LISTEN represents waiting for a connection request from any remote TCP and port. SYN-SENT represents waiting for a matching connection request after having sent a connection request. SYN-RECEIVED represents waiting for a confirming connect…

VUE学习笔记(入门)5__vue指令v-html

v-html是用来解析字符串标签 示例 <!doctype html> <html lang"en"> <head><meta charset"UTF-8" /><meta name"viewport" content"widthdevice-width, initial-scale1.0" /><title>Document<…

【论文投稿】探秘计算机视觉算法:开启智能视觉新时代

目录 引言 一、计算机视觉算法基石&#xff1a;图像基础与预处理 二、特征提取&#xff1a;视觉信息的精华萃取 三、目标检测&#xff1a;从图像中精准定位目标 四、图像分类&#xff1a;识别图像所属类别 五、语义分割&#xff1a;理解图像的像素级语义 六、计算机视觉…

【Java数据结构】Java对象的比较

元素的比较 基本类型比较 在Java中基本类型比较可以直接比较大小 &#xff0c;返回一个布尔类型&#xff08;true或者false&#xff09;。 int a 10; int b 20; System.out.println(a>b); System.out.println(ab); System.out.println(a<b);对象比较的问题 对象的比…

《自动驾驶与机器人中的SLAM技术》ch8:基于预积分和图优化的紧耦合 LIO 系统

和组合导航一样&#xff0c;也可以通过预积分 IMU 因子加上雷达残差来实现基于预积分和图优化的紧耦合 LIO 系统。一些现代的 Lidar SLAM 系统也采用了这种方式。相比滤波器方法来说&#xff0c;预积分因子可以更方便地整合到现有的优化框架中&#xff0c;从开发到实现都更为便…

Ubuntu 24.04 LTS 更改软件源

Ubuntu 24.04 LTS 修改软件源

【2024年度技术总结】Unity 游戏开发的深度探索与实践

文章目录 前言一、Unity 游戏开发的技术深度总结1、C# 编程基础2、Unity 基础入门3、Unity 实战技巧4、Unity 小技巧分享 二、技术工具与平台的年度使用心得1、学习资源的选择2、开发环境配置3、测试与调试工具 三、技术项目实战经验与成果展示1、【制作100个Unity游戏】专栏2、…

ingress-nginx代理tcp使其能外部访问mysql

一、helm部署mysql主从复制 helm repo add bitnami https://charts.bitnami.com/bitnami helm repo updatehelm pull bitnami/mysql 解压后编辑values.yaml文件&#xff0c;修改如下&#xff08;storageclass已设置默认类&#xff09; 117 ## param architecture MySQL archit…

Top期刊算法!RIME-CNN-BiLSTM-Attention系列四模型多变量时序预测

Top期刊算法&#xff01;RIME-CNN-BiLSTM-Attention系列四模型多变量时序预测 目录 Top期刊算法&#xff01;RIME-CNN-BiLSTM-Attention系列四模型多变量时序预测预测效果基本介绍程序设计参考资料 预测效果 基本介绍 基于RIME-CNN-BiLSTM-Attention、CNN-BiLSTM-Attention、R…

游戏引擎学习第84天

仓库:https://gitee.com/mrxiao_com/2d_game_2 我们正在试图弄清楚如何完成我们的世界构建 上周做了一些偏离计划的工作&#xff0c;开发了一个小型的背景位图合成工具&#xff0c;这个工具做得还不错&#xff0c;虽然是临时拼凑的&#xff0c;但验证了背景构建的思路。这个过…

搭建一个基于Spring Boot的数码分享网站

搭建一个基于Spring Boot的数码分享网站可以涵盖多个功能模块&#xff0c;例如用户管理、数码产品分享、评论、点赞、收藏、搜索等。以下是一个简化的步骤指南&#xff0c;帮助你快速搭建一个基础的数码分享平台。 — 1. 项目初始化 使用 Spring Initializr 生成一个Spring …

31、【OS】【Nuttx】OSTest分析(1):stdio测试(一)

背景 接上篇wiki 30、【OS】【Nuttx】构建脚本优化&#xff0c;引入待构建项目参数 最小系统分析完后&#xff0c;下一个能够更全面了解Nuttx的Demo&#xff0c;当然选择OSTest&#xff0c;里面有大量关于OS的测试用例&#xff0c;方便对Nuttx的整体功能有个把握。 stdio_tes…

Spring WebFlux

文章目录 一、概述1、Spring体系定位2、Spring MVC和WebFlux差异 二、入门1、依赖2、ReactorHttpHandlerAdapter&#xff08;main启动&#xff09;3、DispatcherHandler&#xff08;SpringWebFlux启动&#xff09;4、WebFilter 三、DispatcherHandler理解1、handle 前置知识&am…

基于SSM的自助购药小程序设计与实现(LW+源码+讲解)

专注于大学生项目实战开发,讲解,毕业答疑辅导&#xff0c;欢迎高校老师/同行前辈交流合作✌。 技术范围&#xff1a;SpringBoot、Vue、SSM、HLMT、小程序、Jsp、PHP、Nodejs、Python、爬虫、数据可视化、安卓app、大数据、物联网、机器学习等设计与开发。 主要内容&#xff1a;…

Oracle graph 图数据库体验-安装篇

服务端安装 环境准备 安装数据库 DOCKER 安装23AI FREE &#xff0c;参考&#xff1a; https://container-registry.oracle.com/ords/f?p113:4:111381387896144:::4:P4_REPOSITORY,AI_REPOSITORY,AI_REPOSITORY_NAME,P4_REPOSITORY_NAME,P4_EULA_ID,P4_BUSINESS_AREA_ID:1…

CSS 的基础知识及应用

前言 CSS&#xff08;层叠样式表&#xff09;是网页设计和开发中不可或缺的一部分。它用于描述网页的视觉表现&#xff0c;使页面不仅实现功能&#xff0c;还能提供吸引人的用户体验。本文将介绍 CSS 的基本概念、语法、选择器及其在提升网页美观性方面的重要性。 什么是 CSS&…

C语言之装甲车库车辆动态监控辅助记录系统

&#x1f31f; 嗨&#xff0c;我是LucianaiB&#xff01; &#x1f30d; 总有人间一两风&#xff0c;填我十万八千梦。 &#x1f680; 路漫漫其修远兮&#xff0c;吾将上下而求索。 C语言之装甲车库车辆动态监控辅助记录系统 目录 一、前言 1.1 &#xff08;一&#xff09;…

python+django+Nacos实现配置动态更新-集中管理配置(实现mysql配置动态读取及动态更新)

一、docker-compose.yml 部署nacos服务 version: "3" services:mysql:container_name: mysql# 5.7image: mysql:5.7environment:# mysql root用户密码MYSQL_ROOT_PASSWORD: rootTZ: Asia/Shanghai# 初始化数据库(后续的初始化sql会在这个库执行)MYSQL_DATABASE: nac…