C++入门--string类的实现

目录

  • 1.string类常用函数实现
    • (1)string类成员变量定义
    • (2) string类默认构造函数实现
    • (3) string类拷贝构造函数实现
    • (4)string类析构函数
    • (5)string类c_str()函数实现
    • (6)string类size()函数实现
    • (7)string类operator[]函数实现
    • (8)string类迭代器的实现
    • (9)string类reserve函数实现
    • (10)string类push_back函数实现
    • (11)string类append函数实现
    • (12)string类operator+=运算符重载函数实现
    • (13)string类insert函数实现
    • (14)string类erase函数实现
    • (15)string类find函数实现
    • (16)string类substr函数实现
    • (17)string类resize函数模拟实现
    • (18)string类流插入operator<<非成员函数模拟实现
    • (19)string类string类流提取operator>>非成员函数模拟实现
    • (20)string类operator=运算符重载函数
    • (21)string类关系运算符重载
      • operator<函数
      • operator==函数
      • 函数复用实现
  • 2.string常用接口类函数实现源码
    • string.h文件

1.string类常用函数实现

(1)string类成员变量定义

namespace llh
{class string{private:size_t size;//string类的大小size_t capacity;//string类的存储容量char* _str;};
}

我们定义string类的成员变量有sizecapacity_str,配合我们实现插入、删除、扩容等修改操作函数,并且把我们自己实现的string类成员函数封装在命名空间里面,避免和标准库里面的string类类型、函数冲突。

(2) string类默认构造函数实现

		//默认构造函数string(const char* str = ""){_size = strlen(str);_capacity = _size;_str = new char[_capacity + 1];strcpy(_str, str);}

我们在实现默认构造函数时,①使用""空字符串作为缺省参数,常量字符串末尾有'\0',是跟库里面string类默认构造函数无参调用以及string类其他变量初始化配合,_str = new char[_capacity + 1];capacity+1是不传参调用时存储'\0';②构造函数使用初始化列表进行初始化,初始化列表中成员的初始化顺序与成员变量声明顺序应保持一致。

(3) string类拷贝构造函数实现

		//拷贝构造函数//拷贝构造函数string(const string& s){if (this != &s){_str = new char[s._capacity + 1];_size = s._size;_capacity = s._capacity;//strcpy(_str, s._str);//拷贝构造的string类中包含'\0',且'\0'后还有字符//strcpy只能拷贝到'\0'位置,剩余字符不能拷贝memcpy(_str, s._str, _size + 1);}}

使用string类对象s进行拷贝构造时(1)如果进行浅拷贝(即值拷贝),编译器只是将对象中的值拷贝过来,这时候两个string类对象会使用同一块空间,此时发生的危害:①相互影响。一个string类对象修改空间的数据会影响另一个string类对象的数据内容②同一块空间会被析构两次,程序崩溃;(2)所以string类要进行深拷贝,_str = new char[s._capacity + 1];开辟相同的空间,memcpy(_str, s._str, _size + 1);把数据拷贝到新开辟得到空间中,内置类型变量_size_capacity直接进行赋值,这时候两个对象使用不同的空间,其他数据相同。

(4)string类析构函数

		//析构函数~string(){_size = _capacity = 0;delete[] _str;_str = nullptr;}

使用delete操作符释放string类对象指向的空间,_size = _capacity = 0使用string类大小和存储容量清除为0。

(5)string类c_str()函数实现

		//string类返回字符串指针的函数const char* c_str()const{return _str;}

_str为私有成员变量,需要通过函数调用才能进行访问。

(6)string类size()函数实现

		size_t size()const{return _size;}

_size为私有成员变量,需要通过函数调用才能进行访问。

(7)string类operator[]函数实现

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

普通string类对象使用operator[]运算符重载,获取string类pos位置的字符,可以进行string类修改;const string类对象调用const修饰operator[]运算符,只能读取string类pos`位置的字符,不可以进行string类修改。

(8)string类迭代器的实现

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

①string类begin()迭代器为指针,指向string类第一个字符,end()迭代器为指针,指向string类最后一个有效字符的下一个位置②const类型的string类对象对数据内容进行访问,需要使用const类型的迭代器③迭代器封装在类域里面,使用迭代器需要在前面hhl::string::const对string类的数据才能进行访问。

(9)string类reserve函数实现

		void reserve(size_t n = 0){//只允许进行扩容if (n > _capacity){cout << "reserve->" << _capacity << endl;char* tmp = new char[n + 1];_capacity = n;//strcpy(tmp, _str);//拷贝构造的string类中包含'\0',且'\0'后还有字符//strcpy只能拷贝到'\0'位置,剩余字符不能拷贝memcpy(tmp, _str, n + 1);delete[] _str;_str = tmp;}}

实现reserve函数的过程:①对n进行判断,只进行扩容不进行缩容②开辟n+1大小的空间,多出的一个位置存储'\0'③把原有的数据拷贝到新的空间,释放掉原来的空间,让_str指向新的空间。

(10)string类push_back函数实现

		void push_back(char c){if (_size == _capacity){//二倍扩容reserve(_capacity == 0 ? 4 : _capacity * 2);}_str[_size] = c;++_size;_str[_size] = '\0';}

①在插入字符之前,判断string类字符个数_size是否和存储容量相等,如果相等需要扩容②在扩容的时候,无参构造对象有效字符数为0,需要指定开辟的空间大小,其余情况进行原有容量的二倍扩容③插入字符后,++_size;并且_str[_size] = '\0';在最后添加'\0'进行限制。

(11)string类append函数实现

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

①判断插入str字符串是否需要扩容,这次我们进行按需扩容②使用strcpy函数将字符串str拷贝string类对象末尾,strcpy函数会将str字符串中的'\0'一起拷贝过去,不需要单独进行限制,最后更新string类数据个数。

(12)string类operator+=运算符重载函数实现

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

实现operator+=函数的原理push_back函数、append函数的原理是一样的,我们在这里直接进行对push_back 函数、append函数进行复用。

(13)string类insert函数实现

		void insert(size_t pos, size_t n, char c){assert(pos <= _size);if (_size + n > _capacity){//扩容到_size+nreserve(_size + n);}size_t end = _size;//挪动数据//当插入0位置时,end在结束时为-1,//由于end为无符号整型,会整型提升为整数的最大值,会发生越界访问的错误,程序崩溃while (end >= pos && end != npos){_str[end + n] = _str[end];end--;}//插入n个字符cfor (int i = 0; i < n; i++){_str[i + pos] = c;}_size += n;}void insert(size_t pos, const char* str){assert(pos <= _size);size_t len = strlen(str);if (_size + len > _capacity){//扩容到_size+lenreserve(_size + len);}size_t end = _size;//挪动数据//当插入0位置时,end在结束时为-1,//由于end为无符号整型,会整型提升为整数的最大值,会发生越界访问的错误,程序崩溃while (end >= pos&&end!=npos){_str[end + len] = _str[end];end--;}//插入字符串strfor (int i = 0; i < len; i++){_str[pos + i] = str[i];}_size += len;}

增加的成员变量:

//在string里面定义静态共有成员变量public:static size_t npos;
//在类外面进行初始化size_t string::npos = -1;

①判断插入字符数据是否需要扩容,以及pos位置是否合法②在挪动数据的时候,pos位置为0时,end在结束的时候为-1,因为endsize_t类型会整型提升为整型最大值,程序因越界访问直接崩溃。解决办法:①设置end变量类型为int类型,while循环判断框里的pos强转为0(库里面pos变量为无符号整型,同标准库里面的保持一致)③设置一个公有静态成员无符号整型变量npos,并且初始化为-1,附加一个判断条件end!=npos③进行字符数据的插入,_size += len;更新string对象数据个数。

(14)string类erase函数实现

		void erase(size_t pos, size_t len = npos){assert(pos < _size);//要删除的长度很长,或字符串很短if (len == npos || pos + len >= _size){_str[pos] = '\0';_size = pos;}else{size_t end = pos + len;while (end<=_size){_str[pos++] = _str[end++];}_size -= len;}}

判断pos位置是否合法,如果pos位置之后要删除的数据个数很大,直接在pos位置加'\0'进行限制;或者正常进行挪动数据覆盖进行删除(把‘'\0'一起挪动)。

(15)string类find函数实现

		size_t find(char c, size_t pos = 0){assert(pos < _size);for (size_t i = 0; i < _size; i++){if (_str[i] == c){return i;}}return npos;}size_t find( const char* s,size_t pos = 0){assert(pos < _size);//pos位置开始const char* ptr = strstr(_str+pos, s);if (ptr){//指针减指针为之间的字符个数,也为子串的起始位置return ptr-_str;}else{return npos;}}

查找字符串使用strstr子串查找函数进行查找,若查找成功,返回字符串第一次出现的位置,ptr-_str得到string类的下标位置,若查找失败返回空指针。

(16)string类substr函数实现

		string substr(size_t pos = 0, size_t len=npos){assert(pos < _size);string tmp;//判断拷贝长度size_t n = len;//if (pos + len > _capacity)if (len==npos||pos + len > _size){n = _size - pos;}//把子串的数据拷贝string类tmpfor (size_t i = 0; i < n; i++){tmp += _str[pos + i];}return tmp;}

①判断需要拷贝的string类字符串长度,如果过长,从pos位置拷贝到string类末尾②把子串的数据拷贝string类tmp,并且返回tmp

(17)string类resize函数模拟实现

		void resize(size_t n, char ch = '\0'){			if (n < _capacity){_str[n] = '\0';_size = n;}else{reserve(n);for (size_t i = _size; i <n; i++){_str[i] = ch;}_size = n;_str[_size] = '\0';}}

判断是减少string类数据个数,还是增加数据个数。①若是减少string类数据个数,直接在对应的位置添加'\0'(不用进行缩容,考虑到后面还需要插入数据,反复进行扩容缩容,代价太大),_size = n;更新数据个数②若是增加string类数据个数,先进行扩容(若是存储容量足够,不需要扩容,否则就进行扩容,已在reserve函数中实现),插入字符c_size = n;_str_size] = '\0'; 进行字符串限制和数据更新。

(18)string类流插入operator<<非成员函数模拟实现

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

①把operator<<函数实现成非成员函数,是为了保证第一个参数ostream& out②在实现该函数时,没有直接out << s.c_str() << endl;输出,string类不看'\0'而终止,而是以_size数据个数为终止输出。

(19)string类string类流提取operator>>非成员函数模拟实现

//定义为类成员函数void clear(){_str[0] = '\0';_size = 0;}
//定义为非成员函数istream& operator>>(istream& in, string& s){s.clear();char ch = in.get();while (ch == ' ' ||ch == '\n'){ch = in.get();}char buff[128];size_t i = 0;while (ch != ' ' && ch != '\n'){//使用一个128字节大小的数组,把读取到的字符存放数组//如果buff数组到127字符,一次性加载到string类对象//此时还有字符没有读取完,继续存放在buff数组,直到读取完毕//最后再把字符串加载到string类末尾if (i == 127){buff[i] = '\0';s += buff;i = 0;}buff[i++] = ch;ch = in.get();}if (i != 0){//当读取到buff数组的字符时,大于0小于127时//进行最后一次追加拷贝buff[i] = '\0';//进行字符串拷贝限制s += buff;}}

①对string类对象输入的情况,需要使用clear函数清除原有string类数据,重新对string类对象输入②istream流不能读取空格和'\n',需要使用istream流中的get函数③考虑到拷贝数据个数太多,一个一个进行对string类拷贝需要,需要进行多次扩容;而不使用reserve函数进行开辟足够的空间,若空间开小了,达不到减少扩容的效果,若空间开大了,提取数据个数较小时,会造成空间浪费,所以选择128字节大小buff字符数组存储,进行适时拷贝。

(20)string类operator=运算符重载函数

传统写法实现:

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

开辟和s对象的空间大小,进行数据拷贝
在这里插入图片描述

现代写法版本一:

		//现代写法void swap(string& s){std::swap(_str, s._str);std::swap(_size, s._size);std::swap(_capacity, s._capacity);}string& operator=(const string& s){if (this != &s){string tmp(s);swap(tmp);}return *this;}

现代写法版本二:

		string& operator=(string tmp){swap(tmp);return *this;}

使用拷贝构造函数构造一个临时对象tmp,然后*thistmp进行私有变量值交换,因为tmp是临时对象,在函数销毁时会自动调用析构函数,对tmp现在(this原来指向)的空间进行销毁!

(21)string类关系运算符重载

operator<函数

		//版本一bool operator<(const string& s){size_t i1 = 0;size_t i2 = 0;while (i1 < _size && i2 < s._size){if (_str[i1++] < s._str[i2++]){return true;}else{return false;}}return _size < s._size;}//版本二简洁写法bool operator<(const string& s)const{int ret = memcmp(_str, s._str, _size < s._size ? _size : s._size);return ret == 0 ? _size < s._size : ret < 0;}

版本一和版本二原理一样,先比较数据个数较小的string类的部分字符串,若是比较的数据内容相同,则判断数据个数大小,返回真值。

operator==函数

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

函数复用实现

		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 !(*this < s);}bool operator!=(const string& s)const{return !(*this == s);}

2.string常用接口类函数实现源码

string.h文件

#pragma once
#include<iostream>
#include<string>
#include<string.h>
#include<assert.h>
using namespace std;
namespace hhl
{class string{//friend ostream& operator<<(ostream& out, string& s);public:typedef char* iterator;typedef const char* const_iterator;iterator begin(){return _str;}iterator end(){return _str + _size;}const_iterator begin()const{return _str;}const_iterator end()const{return _str + _size;}//默认构造函数string(const char* str = ""){_size = strlen(str);_capacity = _size;_str = new char[_capacity + 1];strcpy(_str, str);//memcpy(_str, str, _size + 1);}//拷贝构造函数string(const string& s){if (this != &s){_str = new char[s._capacity + 1];_size = s._size;_capacity = s._capacity;//strcpy(_str, s._str);//拷贝构造的string类中包含'\0',且'\0'后还有字符//strcpy只能拷贝到'\0'位置,剩余字符不能拷贝memcpy(_str, s._str, _size + 1);}}//传统写法string& operator=(const string& s){if (this != &s){_str = new char[s._capacity + 1];_size = s._size;_capacity = s._capacity;memcpy(_str, s._str, _size + 1);}return *this;}//现代写法void swap(string& s){std::swap(_str, s._str);std::swap(_size, s._size);std::swap(_capacity, s._capacity);}string& operator=(const string& s){if (this != &s){string tmp(s);swap(tmp);}return *this;}string& operator=(string tmp){swap(tmp);return *this;}//string类返回字符串指针的函数const char* c_str()const{return _str;}size_t size()const{return _size;}//析构函数~string(){_size = _capacity = 0;delete[] _str;_str = nullptr;}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 reserve(size_t n = 0){//只允许进行扩容if (n > _capacity){cout << "reserve->" << _capacity << endl;char* tmp = new char[n + 1];_capacity = n;//strcpy(tmp, _str);//拷贝构造的string类中包含'\0',且'\0'后还有字符//strcpy只能拷贝到'\0'位置,剩余字符不能拷贝memcpy(tmp, _str, n + 1);delete[] _str;_str = tmp;}}void push_back(char c){if (_size == _capacity){//二倍扩容reserve(_capacity == 0 ? 4 : _capacity * 2);}_str[_size] = c;++_size;_str[_size] = '\0';}void append(const char* str){size_t len = strlen(str);if (_size + len > _capacity){reserve(_size + len);}//for (size_t i = 0; i < len; i++)//{//	_str[_size + i] = str[i];//}//strcpy(_str + _size, str);memcpy(_str + _size, str, len + 1);_size += len;}string& operator+=(char c){push_back(c);return *this;}string& operator+=(const char* str){append(str);return *this;}void insert(size_t pos, size_t n, char c){assert(pos <= _size);if (_size + n > _capacity){//扩容到_size+nreserve(_size + n);}size_t end = _size;//挪动数据//当插入0位置时,end在结束时为-1,//由于end为无符号整型,会整型提升为整数的最大值,会发生越界访问的错误,程序崩溃while (end >= pos && end != npos){_str[end + n] = _str[end];end--;}//插入n个字符cfor (int i = 0; i < n; i++){_str[i + pos] = c;}_size += n;}void insert(size_t pos, const char* str){assert(pos <= _size);size_t len = strlen(str);if (_size + len > _capacity){//扩容到_size+lenreserve(_size + len);}size_t end = _size;//挪动数据//当插入0位置时,end在结束时为-1,//由于end为无符号整型,会整型提升为整数的最大值,会发生越界访问的错误,程序崩溃while (end >= pos && end != npos){_str[end + len] = _str[end];end--;}//插入字符串strfor (int i = 0; i < len; i++){_str[pos + i] = str[i];}_size += len;}//void erase(size_t pos, size_t len = npos)//{//	assert(pos < _size);//	//要删除的长度很长,或字符串很短//	if (len == npos || pos + len >= _size)//	{//		_str[pos] = '\0';//		//_str[_size] = '\0';//		_size=pos;//	}//	else//	{//		//进行覆盖移动//		for (size_t i = 0; i < _size-(pos+len); i++)//		{//			_str[pos+i] = _str[pos + i+len];//		}//		_str[_size - len] = '\0';//		_size -= len;//	}//}void erase(size_t pos, size_t len = npos){assert(pos < _size);//要删除的长度很长,或字符串很短if (len == npos || pos + len >= _size){_str[pos] = '\0';//_str[_size] = '\0';_size = pos;}else{size_t end = pos + len;while (end <= _size){_str[pos++] = _str[end++];}_size -= len;}}size_t find(char c, size_t pos = 0){assert(pos < _size);for (size_t i = 0; i < _size; i++){if (_str[i] == c){return i;}}return npos;}size_t find(const char* s, size_t pos = 0){assert(pos < _size);//pos位置开始const char* ptr = strstr(_str + pos, s);if (ptr){//指针减指针为之间的字符个数,也为子串的起始位置return ptr - _str;}else{return npos;}}string substr(size_t pos = 0, size_t len = npos){assert(pos < _size);string tmp;//判断长度size_t n = len;//if (pos + len > _capacity)if (len == npos || pos + len > _size){n = _size - pos;}//把需要的数据拷贝string类tmpfor (size_t i = 0; i < n; i++){tmp += _str[pos + i];}return tmp;}void resize(size_t n, char ch = '\0'){			if (n < _capacity){_str[n] = '\0';_size = n;}else{reserve(n);for (size_t i = _size; i <n; i++){_str[i] = ch;}_size = n;_str[_size] = '\0';}}void clear(){_str[0] = '\0';_size = 0;}//	// "hello" "hello"   false//	// "helloxx" "hello" false//	// "hello" "helloxx" true//版本一//bool operator<(const string& s)//{//	size_t i1 = 0;//	size_t i2 = 0;//	while (i1 < _size && i2 < s._size)//	{//		if (_str[i1++] < s._str[i2++])//		{//			return true;//		}//		else//		{//			return false;//		}//	}//	return _size < s._size;//}//版本二简洁写法bool operator<(const string& s)const{int ret = memcmp(_str, s._str, _size < s._size ? _size : s._size);return ret == 0 ? _size < s._size : ret < 0;}bool operator==(const string& s)const{return _size == s._size &&memcpy(_str, s._str, _size);}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 !(*this < s);}bool operator!=(const string& s)const{return !(*this == s);}private:char* _str;size_t _capacity;//string类的存储容量size_t _size;//string类的大小public:static size_t npos;//const static size_t npos = -1;//整型可以这样使用,其他类型不可以,不建议这样使用
};size_t string::npos = -1;ostream& operator<<(ostream& out, string& s){//out << s.c_str() << endl;for (int i = 0; i < s.size(); i++){out << s[i];}return out;}istream& operator>>(istream& in, string& s){s.clear();char ch = in.get();while (ch == ' ' ||ch == '\n'){ch = in.get();}char buff[128];size_t i = 0;while (ch != ' ' && ch != '\n'){//使用一个128字节大小的数组,把读取到的字符存放数组//如果buff数组到127字符,一次性加载到string类对象//此时还有字符没有读取完,继续存放在buff数组,直到读取完毕//最后再把字符串加载到string类末尾if (i == 127){buff[i] = '\0';s += buff;i = 0;}buff[i++] = ch;ch = in.get();}if (i != 0){buff[i] = '\0';s += buff;}}
}

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

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

相关文章

品牌宣传与媒体传播是声誉管理的主要方式之一

企业声誉是现如今影响品牌信任度、客户忠诚度的重要因素&#xff0c;也被视为企业的一种无形资&#xff0c;更影响着企业未来的发展。因此&#xff0c;企业声誉管理也日渐成为企业管理的重要课题之一&#xff0c;尤其在品牌营销管理领域。 什么是声誉管理&#xff1f;声誉管理有…

logstash 采集 docker 日志

1、nginx容器部署 参考&#xff1a;nginx容器部署 将容器内的nginx日志文件映射到宿主机/home/logs/nginx目录下 注意&#xff1a;并且需要需要将日志的输出格式修改为json 2、编辑vim /opt/logstash-7.4.2/config/nginx-log-es.conf 文件&#xff0c;收集docker nginx容器日…

Java训练五

一、跳动的心脏 心脏是动物的重要器官&#xff0c;不断跳动的心脏意味着鲜活的生命力。现在创建一个人类&#xff0c;把心脏类设计为人类里面的一个成员内部类。心脏类有一个跳动的方法&#xff0c;在一个人被创建时&#xff0c;心脏就开始不断地跳动。 package haha; publi…

Spring集成Seata

Seata的集成方式有&#xff1a; 1. Seata-All 2. Seata-Spring-Boot-Starter 3. Spring-Cloud-Starter-Seata 本案例使用Seata-All演示&#xff1a; 第一步&#xff1a;下载Seata 第二步&#xff1a;为了更好看到效果&#xff0c;我们将Seata的数据存储改为db 将seata\sc…

Vue3 —— to 全家桶及源码学习

该文章是在学习 小满vue3 课程的随堂记录示例均采用 <script setup>&#xff0c;且包含 typescript 的基础用法 前言 本篇主要学习几个 api 及相关源码&#xff1a; toReftoRefstoRaw 一、toRef toRef(reactiveObj, key) 接收两个参数&#xff0c;第一个是 响应式对象…

嘉楠勘智k230开发板上手记录(四)--HHB神经网络模型部署工具

按照K230_AI实战_HHB神经网络模型部署工具.md&#xff0c;HHB文档&#xff0c;RISC-V 编译器和模拟器安装来 一、环境 1. 拉取docker 镜像然后创建docker容器并进入容器 docker pull hhb4tools/hhb:2.4.5 docker run -itd --namehhb2_4 -p 22 "hhb4tools/hhb:2.4.5"…

【CSS】背景图定位问题适配不同机型

需求 如图, 实现一个带有飘带的渐变背景 其中头像必须显示飘带凹下去那里 , 需要适配不同的机型, 一不下心容易错位 实现 因为飘带背景是版本迭代中更新的, 所以飘带和渐变背景实则两个div 飘带切图如下 , 圆形部分需要契合头像 <view class"box-bg"><…

Linux ——实操篇

Linux ——实操篇 前言vi 和 vim 的基本介绍vi和vim常用的三种模式正常模式插入模式命令行模式 vi和vim基本使用各种模式的相互切换vi和vim快捷键关机&重启命令基本介绍注意细节 用户登录和注销基本介绍使用细节 用户管理基本介绍添加用户基本语法应用案例细节说明 指定/修…

获取接口的所有实现

一、获取接口所有实现类 方法1&#xff1a;JDK自带的ServiceLoader实现 ServiceLoader是JDK自带的一个类加载器&#xff0c;位于java.util包当中&#xff0c;作为 A simple service-provider loading facility。 &#xff08;1&#xff09;创建接口 package com.example.dem…

Springboot中拦截GET请求获取请求参数验证合法性

目录 目的 核心方法 完整代码 创建拦截器 注册拦截器 测试效果 目的 在Springboot中创建拦截器拦截所有GET类型请求&#xff0c;获取请求参数验证内容合法性防止SQL注入&#xff08;该方法仅适用拦截GET类型请求&#xff0c;POST类型请求参数是在body中&#xff0c;所以下面…

3.1 计算机网络和网络设备

数据参考&#xff1a;CISP官方 目录 计算机网络基础网络互联设备网络传输介质 一、计算机网络基础 1、ENIAC&#xff1a;世界上第一台计算机的诞生 1946年2月14日&#xff0c;宾夕法尼亚大学诞生了世界上第一台计算机&#xff0c;名为电子数字积分计算机&#xff08;ENIAC…

【Autolayout案例02-距离四周边距 Objective-C语言】

一、好,来看第二个案例 1.第二个案例,是什么意思呢,第二个案例,要求屏幕中间,有一个UIView UIView,是个红色的UIView UIView的大小,我不限定 但是无论你是什么屏幕下 这个UIView距离上边,始终是50 距离右边,始终是50, 距离下边,始终是50, 距离左边,始终是5…

Nginx跳转模块——location与rewrite

一、location 1、location作用 用于匹配uri&#xff08;文件、图片、视频&#xff09; uri&#xff1a;统一资源标识符。是一种字符串标识&#xff0c;用于标识抽象的或物理资源文件、图片、视频 2、locatin分类 1、精准匹配&#xff1a;location / {...} 2、一般匹配&a…

PROFINET转DeviceNet网关普通网线能代替profinet吗

捷米JM-DNT-PN这款神器&#xff0c;连接PROFINET和DeviceNet网络&#xff0c;让两边数据轻松传输。 这个网关不仅从ETHERNET/IP和DEVICENET一侧读写数据&#xff0c;还可以将缓冲区数据交换&#xff0c;这样就可以在两个网络之间愉快地传递数据了&#xff01;而且&#xff0c;…

虚幻引擎游戏开发过程中,游戏鼠标如何双击判定?

UE虚幻引擎对于游戏开发者来说都不陌生&#xff0c;市面上有47%主机游戏使用虚幻引擎开发游戏。作为是一款游戏的核心动力&#xff0c;它的功能十分完善&#xff0c;囊括了场景制作、灯光渲染、动作镜头、粒子特效、材质蓝图等。本文介绍了虚幻引擎游戏开发过程中游戏鼠标双击判…

springboot+mybatis实现简单的增、删、查、改

这篇文章主要针对java初学者&#xff0c;详细介绍怎么创建一个基本的springboot项目来对数据库进行crud操作。 目录 第一步&#xff1a;准备数据库 第二步&#xff1a;创建springboot项目 方法1&#xff1a;通过spring官网的spring initilizer创建springboot项目 方法2&am…

SQL SERVER ip地址改别名

SQL server在使用链接服务器时必须使用别名&#xff0c;使用ip地址就会把192.188.0.2这种点也解析出来 解决方案&#xff1a; 1、物理机ip 192.168.0.66 虚拟机ip 192.168.0.115 2、在虚拟机上找到 C:\Windows\System32\drivers\etc 下的 &#xff08;我选中的文件&a…

C#与C/C++交互(1)——需要了解的基础知识

【前言】 C#中用于实现调用C/C的方案是P/Invoke&#xff08;Platform Invoke&#xff09;&#xff0c;让托管代码可以调用库中的函数。类似的功能&#xff0c;JAVA中叫JNI&#xff0c;Python中叫Ctypes。 常见的代码用法如下&#xff1a; [DllImport("Test.dll", E…

修改IDEA的idea.vmoptions参数导致IDEA无法打开(ReservedCodeCacheSize)

事发原因 Maven导依赖的时候OOM&#xff0c;因此怀疑是内存太小&#xff0c;尝试修改idea.vmoptions的参数&#xff0c;然后发现IDEA重启后打不开了&#xff0c;卸载重装后也无法打开。。。 实际上如果导包爆出OOM的话应该调整下图参数&#xff0c;不过这都是后话了 解决思路…

【从零开始学习JAVA | 第四十四篇】TCP协议中的握手与挥手

前言&#xff1a; TCP&#xff08;传输控制协议&#xff09;作为计算机网络中的重要协议&#xff0c;扮演着确保数据可靠传输的角色。在TCP的通信过程中&#xff0c;握手与挥手问题是不可忽视的关键环节。握手是指在建立连接时&#xff0c;客户端与服务器相互确认彼此的身份并…