秒懂C++之string类(下)

fe594ea5bf754ddbb223a54d8fb1e7bc.gif

目录

一.接口说明

1.1 erase

1.2 replace(最好别用)

1.3 find

1.4 substr

1.5 rfind

1.6 find_first_of

1.7 find_last_of

二.string类的模拟实现

2.1 构造

2.2 无参构造

2.3 析构

2.4.【】运算符

2.5 迭代器

2.6 打印

2.7 reserve扩容

2.8 push_back尾插

2.9 append追加

2.10 insert插入

2.11 erase删除

2.12 swap交换

2.13 find寻找

2.14 运算符+=

2.15 substr

2.16 流插入<<

2.17 流提取>>

三.现代写法

拷贝构造

赋值拷贝

四.小拓展

五.全部代码


一.接口说明

1.1 erase

#include <iostream>
#include <string>
using namespace std;int main()
{string s1("hello world");s1.erase(0, 4);cout << s1 << endl;//o world 从下标为0开始,删除4个字符s1.erase();cout << s1 << endl;//return 0;}

1.2 replace(最好别用)

int main()
{string s1("hello world");string s2;s1.replace(0, 1, "aaaa");cout << s1 << endl;//aaaaello worldreturn 0;
}

替换指定位置内容,并挪动后面数据。

1.3 find

int main()
{string s1("hello world a b");// 所有的空格替换为100%size_t pos = s1.find(' ', 0);//从下标为0位置开始找while (pos != string::npos){s1.replace(pos, 1, "100%");// 效率很低,能不用就不要用了pos = s1.find(' ', pos + 4);}cout << s1 << endl;//hello100%world100%a100%breturn 0;
}

作用就是从头开始找到该字符并返回其下标位置。

之所以不用replace是因为有更好的替代~ 


int main()
{string s1("hello world a b");string s2;for (auto ch : s1){if (ch == ' '){s2 += "100%";}else{s2 += ch;}}s1.swap(s2);cout << s1 << endl;//hello100%world100%a100%breturn 0;
}

注意:

swap(s1,s2)与s1.swap(s2)可不一样,前者是通过有个中间变量进行交换,而后者是让双方指针进行交换,后者更为高效~

1.4 substr

int main()
{string s1("hello");string s2;s2 = s1.substr(1, 2);cout << s2 << endl;//elcout << s1.substr(1) << endl;//elloreturn 0;
}
str 中从 pos 位置开始,截取 n 个字符,然后将其返回

1.5 rfind

从末尾开始找字符并返回下标

int main()
{//获取后缀名string s1("Test.cpp");string s2("Test.tar.zar");size_t pos1 = s1.find('.');size_t pos2 = s2.rfind('.');if(pos1 != string::npos){string s3 = s1.substr(pos1);cout << s3 << endl;//.cppstring s4 = s2.substr(pos2);cout << s4 << endl;//.zar}return 0;
}

1.6 find_first_of

int main()
{string s1("hello world");size_t pos1 = s1.find_first_of("abcde");cout << pos1 << endl;//1pos1 = s1.find_first_of("ow");cout << pos1 << endl;//4return 0;
}

返回在选定字符串中任意一个字符第一次出现的下标。(从头开始遍历)

1.7 find_last_of

int main()
{string s1("hello world");size_t pos1 = s1.find_last_of("abcde");cout << pos1 << endl;//10pos1 = s1.find_last_of("h");cout << pos1 << endl;//0return 0;
}

返回在选定字符串中任意一个字符第一次出现的下标。(从尾开始遍历)

注意:find_first_not_of与find_last_not_of,就是返回指选定字符串之外的字符下标

int main()
{string s1("hello world");size_t pos1 = s1.find_first_not_of("abcde");cout << pos1 << endl;//0pos1 = s1.find_last_not_of("h");cout << pos1 << endl;//10return 0;
}

二.string类的模拟实现

2.1 构造

首先实现构造函数的时候在初始化列表不能直接这么写,涉及到了权限放大~

也不能在私有成员那写:const char* _str,这样_str以后只能指向一处,无法修改指向,也意味着无法增删改。

namespace lj
{class string{public://构造string(const char* str){_size = strlen(str);_capacity = _size;_str = new char[_capacity + 1];//加1是因为在_capacity那被认为‘\0’是有效字符strcpy(_str, str);}private:size_t _size;size_t _capacity;char* _str;};void test_string1(){string s1("aabbcc");//构造成功}
}

建议直接放弃在列表初始化,还得考虑声明顺序啥的挺麻烦~

2.2 无参构造

//无参构造string(){_str = nullptr;_size = 0;_capacity = 0;}//返回c格式字符串const char* c_str(){return _str;}

可惜这种无参写法不行,因为c_str是以指针的形式返回的(字符串形式),而cout接触到指针会认为是要打印字符串,直接解引用指针,一个空指针被解引用是错误的。

开辟个1字节的空间,里面放个字符(但不是有效字符)

不过与其写无参不如直接写缺省参数

//为什么字符串就能匹配上啊?,因为这样指针就能指向它,本身是地址。

2.3 析构

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

2.4.【】运算符

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

//为什么里面还能有【】,因为这个是给内置类型使用的,自定义类型要自己自定义

2.5 迭代器

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

其实迭代器实现也挺简单的~

for+auto的遍历底层逻辑其实还是迭代器。

2.6 打印

首先调用size()函数的时候出现了权限放大的问题,调用运算符【】也是如此所以在这里我们就要引入const成员函数进行修改~对需要改变自身的额外再写一个针对const权限函数,对不需要改变自身的加入const。

void print_str(const string& s){for (size_t i = 0; i < s.size(); i++){//s[i]++;cout << s[i] << endl;}string::const_iterator it = s.begin();while (it != s.end()){// *it = 'x';cout << *it << " ";++it;}cout << endl;}

2.7 reserve扩容

		void reserve(size_t n){if (n > _capacity){char* tmp = new char[n + 1];//还有一个开给\0strcpy(tmp, _str);//拷贝所指向的内容delete[] _str;//销毁旧空间_str = tmp;//指向新空间_capacity = n;}}

2.8 push_back尾插

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

由于\0不算有效字符,所以最后计算完后_size都会与其下标相等,而这时候可以刚好插入字符。最后需要注意人工补上\0。

2.9 append追加

		void append(const char* str){size_t len = strlen(str);//计算所追加的长度if (_size + len > _capacity){reserve(_size + len);}strcpy(_str+_size, str);_size += len;}

//strcpy(_str+_size, str);为什么不是_size就行了呢?因为_size是一个数据不是一个指针,而_str是指针+数可以达到偏移的效果

2.10 insert插入

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

需要注意的细节有点多:如果是这种写法

while (end >= (int)pos){_str[end + 1] = _str[end];--end;}

最后end会减为负数,而其类型又是无符号整型,会一直陷入死循环。就算把end改为整型,在循环条件里又会和pos触发类型提升(小的向大的转,有符号向无符号转),最后还得强制转化pos类型才可以解决问题。所以直接换个写法一劳永逸~

		void insert(size_t pos, const char* str){assert(pos <= _size);size_t len = strlen(str);if (_size+len > _capacity){reserve(_size+len);}size_t end = _size;while (pos < end){_str[end+len-1] = _str[end - 1];end--;}strncpy(_str + pos, str,len);_size+=len;_str[_size] = '\0';}

 

2.11 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{strcpy(_str + pos, _str + pos + len);//很精妙_size -= len;}}

2.12 swap交换

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

如果没有中间的swap(string),那么swap(s1,s2)就会走与swap(a,b)一样的结果,产生中间变量。如果有中间的swap(string),那当我们swap(s1,s2)时进入其中就会让我们去调用string类里面的swap,只需要交换指针即可,不产生中间变量。

2.13 find寻找

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

 

		size_t find(const char*str, size_t pos = 0){const char* ptr = strstr(_str + pos, str);if (ptr == nullptr){return npos;}else{return ptr - _str;//利用指针-指针}}

2.14 运算符+=

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

简单复用即可

2.15 substr
 

		string substr(size_t pos = 0, size_t len = npos){assert(pos < _size);size_t end = pos+len;if (len == npos || pos + len >= _size){end = _size;}string sub1;sub1.reserve(end - pos);//重新创个空间for (size_t i = pos; i < end; i++)//拷贝内容{sub1 += _str[i];}return sub1;}

最终会报错~

问题一:因为是临时对象,这意味着出作用会就会调用析构函数(清理资源),而临时对象又指向临时资源,被清理后那就变成野指针了。

解决方法:写出拷贝构造(深拷贝)

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

问题二:在赋值拷贝中(s2=s1)会出现以下情况,要么s2的空间过大,与s1大小差距过大造成资源浪费。要么s2的空间太小,不足够容纳s1,只有二者刚刚好才合适。

// s2 = s1
string& operator=(const string& s){if (this != &s)//不自己调用自己{char* tmp = new char[s._capacity + 1];strcpy(tmp, s._str);delete[] _str;_str = tmp;_size = s._size;_capacity = s._capacity;}return *this;}

既然这样就重新构造一个与s1相仿的空间,再拷贝原内容进去,删掉s2所指的原来空间,令s2指向新空间,再浅拷贝,使得赋值拷贝合理化。

成功实现效果~ 

2.16 流插入<<

	ostream& operator<<(ostream& out, const string& s){for (auto ch : s){out << ch;}return out;}

2.17 流提取>>

	void clear(){_size = 0;_str[0] = '\0';}istream& operator>>(istream& in,  string& s){s.clear();char ch = in.get();while (ch != ' ' && ch != '\n'){s += ch;ch = in.get();}return in;}

细节一:clear函数

如果没有_str[0] = '\0';由于cout<<s1是以_size为准的,在遍历的时候就停止流插入了。而cout<<s1.c_str()中由于其返回的是整段的字符串不看_size,是直接解引用的。那么在没有'\0'的情况下还是会输出,没有达成清理的效果。

另外如果没有clear函数清理原内容,那么cin最后做到的只是拼接。

细节二.get函数

如果ch以这样的方式提取那么最终会无法识别到‘ ’与‘\n’而死循环。因为in拿不到‘ ’与‘\n’,他们通常是作为分割符合使用的,所以会无视。只有get函数才能够识别并提取它们。

三.现代写法

备注:功能和传统写法一组,只是让代码行数更少

拷贝构造

		//传统写法			string s2(s1) string(const string& s){//深拷贝_str = new char[s._capacity + 1];strcpy(_str, s._str);_size = s._size;_capacity = s._capacity;}//现代写法			string s2(s1) string(const string& s){string tmp(s._str);//调用构造,构造出与s1一样的空间与内容swap(tmp);//s2与tmp交换指针}

细节:s2必须得指向空(全缺省),如果指向其他地方那么tmp可能会出现随机值,报错。

赋值拷贝

        //传统写法  s1 = s3string& operator=(const string& s){if (this != &s)//不自己调用自己{char* tmp = new char[s._capacity + 1];strcpy(tmp, s._str);delete[] _str;_str = tmp;_size = s._size;_capacity = s._capacity;}return *this;}//现代写法  s1 = s3string& operator=(string s){swap(s);return *this;}

四.小拓展

to_string可以识别各种类型然后转化为string。

stoi可以识别string类型然后转化为需要的类型。

五.全部代码

//string.cpp
#define _CRT_SECURE_NO_WARNINGS 1
#include "string.h"namespace lj
{//构造string::string(const char* str)//不能给'\0'类型不匹配,而字符串会默认带\0{_size = strlen(str);_capacity = _size;_str = new char[_capacity + 1];//加1是因为在_capacity那被认为‘\0’是有效字符strcpy(_str, str);}//现代写法			string s2(s1) string::string(const string& s){string tmp(s._str);//调用构造swap(tmp);}//现代写法  s1 = s3string& string::operator=(string s){swap(s);return *this;}string::~string(){delete[] _str;_str = nullptr;_size = 0;_capacity = 0;}char& string::operator[](size_t pos){return _str[pos];}const char& string:: operator[](size_t pos)const{return _str[pos];}void string::reserve(size_t n){if (n > _capacity){char* tmp = new char[n + 1];//还有一个开给\0strcpy(tmp, _str);//拷贝所指向的内容delete[] _str;//销毁旧空间_str = tmp;//指向新空间_capacity = n;}}void string::push_back(char ch){if (_size == _capacity){size_t newcapacity = _capacity == 0 ? 4 : _capacity * 2;reserve(newcapacity);}_str[_size] = ch;_size++;_str[_size] = '\0';}void string::append(const char* str){size_t len = strlen(str);//计算所追加的长度if (_size + len > _capacity){reserve(_size + len);}strcpy(_str + _size, str);_size += len;}void string::insert(size_t pos, char ch){assert(pos <= _size);if (_size == _capacity){size_t newCapacity = _capacity == 0 ? 4 : _capacity * 2;reserve(newCapacity);}size_t end = _size;while (pos < end){_str[end] = _str[end - 1];end--;}_str[pos] = ch;_size++;_str[_size] = '\0';}void string::insert(size_t pos, const char* str){assert(pos <= _size);size_t len = strlen(str);if (_size + len > _capacity){reserve(_size + len);}size_t end = _size;while (pos < end){_str[end + len - 1] = _str[end - 1];end--;}strncpy(_str + pos, str, len);_size += len;_str[_size] = '\0';}void string::erase(size_t pos, size_t len){assert(pos <= _size);if (len == npos || pos + len > _size){_str[pos] = '\0';_size = pos;}else{strcpy(_str + pos, _str + pos + len);//很精妙_size -= len;}}void string::swap(string& s){std::swap(_str, s._str);std::swap(_size, s._size);std::swap(_capacity, s._capacity);}size_t string::find(char ch, size_t pos){for (size_t i = pos; i <= _size; i++){if (_str[i] == ch){return i;}}return npos;}size_t string::find(const char* str, size_t pos){const char* ptr = strstr(_str + pos, str);if (ptr == nullptr){return npos;}else{return ptr - _str;//利用指针-指针}}string& string::operator+=(char ch){push_back(ch);return *this;}string& string::operator+=(const char* str){append(str);return *this;}string string::substr(size_t pos, size_t len){assert(pos < _size);size_t end = pos + len;if (len == npos || pos + len >= _size){end = _size;}string sub1;sub1.reserve(end - pos);//重新创个空间for (size_t i = pos; i < end; i++)//拷贝内容{sub1 += _str[i];}return sub1;}void string::clear(){_size = 0;_str[0] = '\0';}ostream& operator<<(ostream& out, const string& s){for (auto ch : s){out << ch;}return out;}istream& operator>>(istream& in, string& s){s.clear();char ch = in.get();while (ch != ' ' && ch != '\n'){s += ch;ch = in.get();}return in;}};

//string.h
#pragma once
#include <iostream>
#include <assert.h>
using namespace std;
namespace lj
{class string{public://无参构造/*	string():_str(new char[1]){_str[0] = '\0';_size = 0;_capacity = 0;}*///返回c格式字符串const char* c_str()const{return _str;}//构造string(const char* str = "");//不能给'\0'类型不匹配,而字符串会默认带\0//现代写法			string s2(s1) string(const string& s);//现代写法  s1 = s3string& operator=(string s);~string();size_t size()const{return _size;}typedef char* iterator;typedef const char* const_iterator;iterator begin(){return _str;}const_iterator begin()const{return _str;}iterator end(){return _str + _size;}const_iterator end()const{return _str + _size;}char& operator[](size_t pos);const char& operator[](size_t pos)const;void reserve(size_t n);void push_back(char ch);void append(const char* str);void insert(size_t pos, char ch);void insert(size_t pos, const char* str);void erase(size_t pos, size_t len = npos);void swap(string& s);size_t find(char ch, size_t pos = 0);size_t find(const char* str, size_t pos = 0);string& operator+=(char ch);string& operator+=(const char* str);string substr(size_t pos = 0, size_t len = npos);void clear();private:size_t _size = 0;size_t _capacity = 0;char* _str = nullptr;const static size_t npos = -1;};ostream& operator<<(ostream& out, const string& s);istream& operator>>(istream& in, string& s);}

//test.cpp
#define _CRT_SECURE_NO_WARNINGS 1
#include <iostream>
#include <string>
using namespace std;//int main()
//{
//	//string s1("hello world");
//	//s1.erase(0, 4);
//	//cout << s1 << endl;//o world 从下标为0开始,删除4个字符
//	//s1.erase();
//	//cout << s1 << endl;//
//
//	//string s1("hello world");
//	//string s2;
//	//s1.replace(0, 1, "aaaa");
//	//cout << s1 << endl;//aaaaello world
//	string s1("hello world a b");
//	// 所有的空格替换为100%
//	size_t pos = s1.find(' ', 0);
//	while (pos != string::npos)
//	{
//		s1.replace(pos, 1, "100%");
//		// 效率很低,能不用就不要用了
//
//		pos = s1.find(' ', pos + 4);
//	}
//	cout << s1 << endl;//hello100%world100%a100%b
//
//	return 0;
//}//int main()
//{
//	string s1("hello world a b");
//	string s2;
//	for (auto ch : s1)
//	{
//		if (ch == ' ')
//		{
//			s2 += "100%";
//		}
//		else
//		{
//			s2 += ch;
//		}
//	}
//	s1.swap(s2);
//	cout << s1 << endl;//hello100%world100%a100%b
//
//	return 0;
//}//int main()
//{
//	string s1("hello");
//	string s2;
//	s2 = s1.substr(1, 2);
//	cout << s2 << endl;//el
//	cout << s1.substr(1) << endl;//ello
//
//	return 0;
//}//int main()
//{
//	//获取后缀名
//	string s1("Test.cpp");
//	string s2("Test.tar.zar");
//
//	size_t pos1 = s1.find('.');
//	size_t pos2 = s2.rfind('.');
//
//	if(pos1 != string::npos)
//	{
//		string s3 = s1.substr(pos1);
//		cout << s3 << endl;//.cpp
//
//		string s4 = s2.substr(pos2);
//		cout << s4 << endl;//.zar
//
//	}
//
//	return 0;
//}//int main()
//{
//	string s1("hello world");
//
//	size_t pos1 = s1.find_first_not_of("abcde");
//	cout << pos1 << endl;//0
//
//	pos1 = s1.find_last_not_of("h");
//	cout << pos1 << endl;//10
//
//
//	return 0;
//}#include"string.h"void print_str(const string& s)
{for (size_t i = 0; i < s.size(); i++){//s[i]++;cout << s[i] << endl;}string::const_iterator it = s.begin();while (it != s.end()){// *it = 'x';cout << *it << " ";++it;}cout << endl;}
void test_string1()
{//string s1("aabbcc");//构造成功//cout << s1.c_str() << endl;//for (size_t i = 0; i < s1.size(); i++)//{//	s1[i]++;//}//cout << s1.c_str() << endl;//string s2;cout << s2.c_str << endl;//string s1("aabbcc");//string::iterator it = s1.begin();//while (it != s1.end())//{//	cout << *it << " ";//	++it;//}//cout << endl;//a a b b c c//string s1("aabbcc");//for (auto ch : s1)//{//	cout << ch << " ";//}//cout << endl;//a a b b c c//string s3("hello");//print_str(s3);//bb cc dd//string s1("aabbcc ddeeff");//s1.push_back('a');//cout << s1.c_str() << endl;//string s2;//s2.push_back('a');//cout << s2.c_str() << endl;/*string s1("aabbcc ddeeff");s1.append("aaa");cout << s1.c_str() << endl;*///string s1("aabbcc ddeeff");//s1.insert(0, 'w');//cout << s1.c_str() << endl;//string s1("aabbcc ddeeff");//s1.insert(0, "ddd");//cout << s1.c_str() << endl;//string s2("aabbcc ddeeff");//s2.insert(12, "ddd");//cout << s2.c_str() << endl;//string s1("aabbcc ddeeff");//s1.erase(1, 2);//cout << s1.c_str() << endl;//string s1("aabbcc ddeeff");//string s2("abc");//s2.swap(s1);//cout << s1.c_str() << endl;//cout << s2.c_str() << endl;/*string s1("aabbcc ddeeff");cout << s1.find('\0') << endl;cout << s1.find('a',1) << endl;*///string s1("aabbcc ddeeff");//cout << s1.find("cc",0) << endl;//cout << s1.find('a', 1) << endl;//string s1("aabbcc ddeeff");//s1 += 'a';//cout << s1.c_str() << endl;//string s2("aabbcc ddeeff");//s2 += "bbb";//cout << s2.c_str() << endl;//string s1("aabbcc ddeeff");//string s2;//s2 = s1.substr(3, 4);//cout << s2.c_str() << endl;//string str;//string& s = str;//string s1("abcd");cout << s1 << endl;//string s2("acacac");//cout << s2 << endl;//string s1("abcabc");//cin >> s1;//cout << s1 << endl;/*string s1("hello world");cout << s1.c_str() << endl;cout << s1 << endl;s1.clear();cout << s1.c_str() << endl;cout << s1 << endl;*///string s1("abc");//cout << s1 << endl;//string s2(s1);//cout << s2 << endl;//string s1("aaaaaa");//string s3("xxxxxx");//s1 = s3;//cout << s1 << endl;//cout << s3 << endl;std::string str = std::to_string(123);cout << str << endl;std::string str1 = std::to_string(123.222);cout << str1 << endl;std::string str2 = std::to_string(123);int i = stoi(str2);cout << i << endl;}
int main()
{test_string1();return 0;
}

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

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

相关文章

大模型算法面试题(十二)

本系列收纳各种大模型面试题及答案。 1、领域模型Continue PreTrain数据如何选取 在领域模型的Continue PreTrain&#xff08;持续预训练&#xff09;过程中&#xff0c;数据选取是一个至关重要的步骤&#xff0c;它直接影响模型在特定领域上的性能和泛化能力。以下是一些关于…

Transformer-Bert---散装知识点---mlm,nsp,较之经典tran的区别和实际应用方式

本文记录的是笔者在了解了transformer结构后嗑bert中记录的一些散装知识点&#xff0c;有时间就会整理收录&#xff0c;希望最后能把transformer一个系列都完整的更新进去。 1.自监督学习 bert与原始的transformer不同&#xff0c;bert是使用大量无标签的数据进行预训练&#…

batch norm记录

文章目录 概要整体架构流程训练阶段推理阶段模型中使用的注意事项 概要 面试百度时候被问到了BN 内部详细的训练阶段&#xff0c;推理阶段的计算过程。没回答好&#xff0c;来记录一下 推荐一下b站up: Enzo_Mi。视频做的确实不错 bn 讲解视频 整体架构流程 训练阶段 均值和标…

【C/C++】printf和cout的区别

创作不易&#xff0c;本篇文章如果帮助到了你&#xff0c;还请点赞 关注支持一下♡>&#x16966;<)!! 主页专栏有更多知识&#xff0c;如有疑问欢迎大家指正讨论&#xff0c;共同进步&#xff01; &#x1f525;c系列专栏&#xff1a;C/C零基础到精通 &#x1f525; 给大…

C++初阶学习——探索STL奥秘——标准库中的string类

1. 为什么学习string类&#xff1f; 在我们学习C语言的时候&#xff0c;有一个点是非常难处理的&#xff0c;那就是字符串&#xff0c;在我们对字符串访问&#xff0c;增删查改时都是非常不便的&#xff0c;所以我们封装了一个string类主要来处理字符串有关的问题 2. 标准库中…

多模态论文一:CLIP模型主要内容讲解【原理+代码】

一、CLIP模型主要内容讲解 CLIP&#xff08;Contrastive Language-Image Pre-training&#xff09;是OpenAI在2021年发布的一种用于图像和文本联合表示学习的模型。CLIP的核心思想是通过对比学习来预训练一个模型&#xff0c;使其能够理解图像和文本之间的关系。以下是CLIP的工…

数据传输安全--SSL VPN

目录 IPSEC在Client to LAN场景下比较吃力的表现 SSL VPV SSL VPN优势 SSL协议 SSL所在层次 SSL工作原理 SSL握手协议、SSL密码变化协议、SSL警告协议三个协议作用 工作过程 1、进行TCP三次握手、建立网络连接会话 2、客户端先发送Client HELLO包&#xff0c;下图是包…

Oracle对比两表数据的不一致

MINUS 基本语法如下 [SQL 语句 1] MINUS [SQL 语句 2];举个例子&#xff1a; select 1 from dual minus select 2 from dual--运行结果 1-------------------------------- select 2 from dual minus select 1 from dual--运行结果 2所以&#xff0c;如果想找所有不一致的&a…

【数据结构】二叉树链式结构——感受递归的暴力美学

前言&#xff1a; 在上篇文章【数据结构】二叉树——顺序结构——堆及其实现中&#xff0c;实现了二叉树的顺序结构&#xff0c;使用堆来实现了二叉树这样一个数据结构&#xff1b;现在就来实现而二叉树的链式结构。 一、链式结构 链式结构&#xff0c;使用链表来表示一颗二叉树…

FPGA:有限状态机

从以下6个实验理解状态机的概念 开发板频率为 50 M H z 50MHz 50MHz&#xff0c;一个时钟周期是 20 n s 20ns 20ns。 1、实验一:LED灯亮0.25秒、灭0.75秒的状态循环 通过之前的分析&#xff0c;我们实现频闪灯时&#xff0c;是让led灯在0.5秒实现一次翻转&#xff0c;而这里…

经典文献阅读之--World Models for Autonomous Driving(自动驾驶的世界模型:综述)

Tip: 如果你在进行深度学习、自动驾驶、模型推理、微调或AI绘画出图等任务&#xff0c;并且需要GPU资源&#xff0c;可以考虑使用UCloud云计算旗下的Compshare的GPU算力云平台。他们提供高性价比的4090 GPU&#xff0c;按时收费每卡2.6元&#xff0c;月卡只需要1.7元每小时&…

html+css 实现水波纹按钮

前言&#xff1a;哈喽&#xff0c;大家好&#xff0c;今天给大家分享htmlcss 绚丽效果&#xff01;并提供具体代码帮助大家深入理解&#xff0c;彻底掌握&#xff01;创作不易&#xff0c;如果能帮助到大家或者给大家一些灵感和启发&#xff0c;欢迎收藏关注哦 &#x1f495; 文…

vue3前端开发-小兔鲜项目-使用pinia插件完成token的本地存储

vue3前端开发-小兔鲜项目-使用pinia插件完成token的本地存储&#xff01;实际业务开发中&#xff0c;token是一个表示着用户登录状态的重要信息&#xff0c;它有自己的生命周期。因此&#xff0c;这个参数值必须实例化存储在本地中。不能跟着pinia。因为pinia是基于内存设计的模…

事务和函数索引

事务 事务的定义 事务&#xff08;Transaction&#xff09;&#xff0c;就是将一组SQL语句放在同一批次内去执行&#xff0c;如果一个SQL语句出错&#xff0c;则该批次内 的所有SQL都将被取消执行。 事务的特点 一个事务中如果有一个数据库操作失败&#xff0c;那么整个 事务…

若依框架 : 生成代码

6.生成代码 6.1.配置生成设置 ruoyi-generator -> src -> main -> resources -> generator.yml 由于 案例中 表都有 前缀 为 tta_ , 这里设置去掉 6.2.生成代码 6.2.1.导入数据库中的表 6.2.2.修改设置 6.2.2.1.设置生成信息 点击 编辑 -> 生成信息 特别…

m4a怎么转mp3?m4a转mp3的几种方法教程

m4a怎么转mp3&#xff1f;M4A音频格式的全称MPEG-4 Audio&#xff0c;是一种音频压缩格式。这种格式以其卓越的音质和相对较小的文件大小而广受欢迎&#xff0c;尤其是在音乐存储、在线流媒体以及音频编辑等领域。M4A格式被广泛应用于苹果公司的产品中&#xff0c;如iPhone、iP…

开放式耳机会成为未来的主流吗?开放式耳机推荐指南

开放式耳机是否会成为未来的主流&#xff0c;是一个值得探讨的问题。 从目前的市场趋势和技术发展来看&#xff0c;有一些因素支持开放式耳机可能成为主流。 一方面&#xff0c;人们对于健康和舒适的关注度不断提高。长时间佩戴传统耳机可能导致耳部不适&#xff0c;而开放式…

在Linux中,部署及优化Tomcat

tomcat概述 自 2017 年 11月编程语言排行榜 Java 占比 13%,高居榜首&#xff0c;Tomcat 也一度成为 Java开发人员的首选。其开源、占用系统资源少、跨平台等特性深受广大程序员喜爱。本章主要学习如何部署 Tomcat 服务&#xff0c;根据生产环境实现多个虚拟主机的配置&#xf…

【QGroundControl二次开发】五.python生成自定义MAVLink消息及使用

一 . 环境配置 参考&#xff1a; MAVLink代码生成-C# 二. 生成MAVLINK协议 在MAVlink源码下找到message_definitions/common.xml&#xff0c;修改其中的内容。 例如&#xff1a; <message id"12" name"DISTANCE_SENSOR"><description>Dedi…

【Code Complete2】Note-1 [启发式编程、管理复杂度、隐藏设计]

【Code Complete2】_Note-1 [启发式编程、管理复杂度、隐藏设计] 文章目录 【Code Complete2】_Note-1 [启发式编程、管理复杂度、隐藏设计]启发式编程管理复杂度隐藏设计--减少“改动所影响的代码量” 启发式编程 ​ **设计是一个启发的过程&#xff0c;充满了不确定性&#…