C++中string类的模拟实现

目录

1.string类的结构

2.默认成员函数

2.1.默认构造函数

2.2拷贝构造函数 

2.3赋值运算符重载 

2.4析构函数

 3.迭代器(Iterators)

4.string类的空间操作(Capacity)

4.1size()

4.2capacity()

4.3clear() 

4.4reserve()

5.元素访问(Element access)

6.string类的修改操作(Modifiers) 

6.1push_back()

6.2append() 

6.3operator+=()

6.4swap()

6.5insert() 

6.6 erase()

7.字符串操作(String operations) 

7.1c_str()

7.2find()

7.3substr()

8.非成员函数重载(Non-member function overloads) 

8.1关系运算符(relational operators)

8.2输入输出重载(operator>> and operator<<) 

 8.2.1输出运算符重载

8.2.2输入运算符重载

9.参考代码

9.1string.h 

9.2string.cpp

9.3Test.cpp 


1.string类的结构

		char* _str = nullptr;size_t _capacity = 0;size_t _size = 0;//这里可以直接给默认值,相当于定义,因为有const,只有整型可以//static const size_t npos = -1;static const size_t npos;

        string类结构里有一个_str指针,指向存储字符的数组,_capacity表示当前string的空间大小,_size表示当前string中的有效元素个数,静态常量npos默认等于-1,表示整形的最大值。 

2.默认成员函数

2.1.默认构造函数

		//默认构造函数string()//直接给空指针在使用.c_str()时进行打印对空指针进行了解引用:_str(new char[1] {'\0'})//:_str(nullptr),_size(0),_capacity(0){}//带参的构造string(const char* str){_size = strlen(str);//_capacity不包含\0_capacity = _size;//开空间的时候多开一个用于存储\0_str = new char[_capacity + 1];strcpy(_str, str);}

        这里的默认构造函数不能给_str空指针,如果是个空串进行打印的话,会对空指针进行解引用会导致程序崩溃。 因为要存一个'\0',所以空串也要开一个空间。

        这里可以把上述两个构造合成一个构造函数。

//将上述两个构造函数合并成一个全缺省的构造函数
string(const char* str = "")	//空的常量字符串默认包含一个\0
{_size = strlen(str);_capacity = _size;_str = new char[_capacity + 1];strcpy(_str, str);	//strcpy先拷贝再判断,会拷贝\0
}

2.2拷贝构造函数 

        1.传统写法

//传统写法
string(const string& s)
{_str = new char[s._capacity + 1];    //多开一个空间给'\0'strcpy(_str, s._str);                //拷贝数据_size = s._size;_capacity = s._capacity;
}

        2.现代写法

void swap(string& s) 
{std::swap(_str, s._str);std::swap(_size, s._size);std::swap(_capacity, s._capacity);
}//现代写法
string(const string& s)
{string tmp(s._str);swap(tmp);
}

        这里调用库里面的交换函数,交换string结构里面的数据。然后在拷贝函数中用被拷贝的string对象s中的_str构造一个临时对象,然后进行交换,函数结束之后这个临时对象自动销毁,构造出一个与s一样的新对象。

2.3赋值运算符重载 

         1.写法1

		string& operator=(const string& s){if (this != &s)    //检测是否是自己给自己赋值{delete[] _str;    //释放原来对象的空间_str = new char[s._capacity + 1];    //new一个和s一样大的空间strcpy(_str, s._str);    //拷贝数据_size = s._size;_capacity = s._capacity;}return *this;}

        2.写法2 

		string& operator=(const string& s){if (this != &s){string tmp(s);swap(tmp);}return *this;}

        这个和拷贝函数的现代写法思路一样。

        3.写法3

		string& operator=(string tmp){swap(tmp);	//这里虽然tmp被交换了,但是形参的改变不影响实参return *this;}

        这里直接使用传值传参,然后进行交换,函数结束之后并不会影响实参。 

2.4析构函数

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

 3.迭代器(Iterators)

		// iteratortypedef char* iterator;typedef const char* const_iterator;iterator begin(){return _str;}iterator end(){return _str + _size;}const_iterator begin() const    //函数重载,重载迭代器用于const对象{return _str;}const_iterator end() const{return _str + _size;}

         因为string的底层是利用数组进行存储的,所以这里直接利用原始指针作为迭代器即可,用两个typedef关键字将char*类型和const char*类型改为迭代器的名字。

4.string类的空间操作(Capacity)

4.1size()

		size_t size() const{return _size;}

4.2capacity()

		size_t capacity() const{return _capacity;}

4.3clear() 

         清除string对象里面的数据,但是这里不缩容_capacity不改变。

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

4.4reserve()

         该函数在string.cpp里面实现的,所以加上了类域限定符.

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

5.元素访问(Element 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];}

6.string类的修改操作(Modifiers

6.1push_back()

        尾插一个字符.

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

6.2append() 

        尾插一个字符串.这里扩容保持一个对齐的原则,如果需要的空间大于原来空间的两倍,则需要多少开多少,如果小于原来的两倍,则开2倍.

	void string::append(const char* str){size_t len = strlen(str);    //计算尾插的str大小if (_size + len > _capacity){//需要的空间大于原空寂的2倍,需要多少开多少,小于2倍开2倍reserve((_size + len) > (2 * _capacity) ? (_size + len) : (2 * _capacity));}strcpy(_str + _size, str);_size += len;}

6.3operator+=()

        +=运算符的重载实现能加一个字符,也能加一个字符串,复用上述两个接口进行实现.

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

6.4swap()

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

6.5insert() 

	// 在pos位置上插入字符c/字符串strvoid string::insert(size_t pos, char c){assert(pos <= _size);if (_size == _capacity){reserve(_capacity == 0 ? 4 : _capacity * 2);}//这里用size_t类型头插会出错,因为end永远都小于不了0//size_t end = _size;	//从最后一个\0开始往后挪动数据//用int end在与pos比较时会提升为size_t类型,也会出错//int end = _size;//while (end >= (int)pos)//{//	_str[end + 1] = _str[end];//	end--;//}//挪动数据,_size处是\0,从后面的\0开始挪动size_t end = _size + 1;while (end > pos)    {_str[end] = _str[end - 1];end--;}_str[pos] = c;++_size;}
	//在pos位置插入字符串void string::insert(size_t pos, const char* str){assert(pos <= _size);size_t len = strlen(str);if (_size + len > _capacity){//大于2倍,需要多少扩多少,小于2倍,扩2倍reserve((_size + len) > (2 * _capacity) ? (_size + len) : (2 * _capacity));}//挪动数据size_t end = _size + len;while (end >= pos + len - 1){_str[end] = _str[end - len];--end;}//插入字符串for (size_t i = 0; i < len; i++){_str[pos + i] = str[i];}_size += len;}

6.6 erase()

        如果需要删除的len的长度大于从pos到最后位置的长度,则修正len之后进行删除.

	// 删除pos位置上之后的len个元素void string::erase(size_t pos, size_t len){assert(pos < _size);//如果删除的元素个数大于从pos到最后的个数,修正一下lenif (len >= _size - pos){_str[pos] = '\0';_size = pos;}else{for (size_t i = pos + len; i <= _size; i++){_str[i - len] = _str[i];}_size -= len;}}

7.字符串操作(String operations) 

7.1c_str()

        这里返回string对象中的数组指针,可以说是返回C语言类型的字符串对象.

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

7.2find()

	// 返回c在string中第一次出现的位置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;}// 返回子串s在string中第一次出现的位置size_t string::find(const char* s, size_t pos) const{assert(pos < _size);//调用库里面的strstr函数在自身中寻找子串const char* ptr = strstr(_str + pos, s);if (ptr == nullptr){return npos;}else{return ptr - _str;}}

7.3substr()

	//返回子串string string::substr(size_t pos, size_t len) const{assert(pos < _size);//len大于剩余字符的长度,更新一下lenif (len > _size - pos){len = _size - pos;}string sub;sub.reserve(len);for (size_t i = 0; i < len; i++){sub += _str[pos + i];}return sub;}

8.非成员函数重载(Non-member function overloads) 

8.1关系运算符(relational operators)

        字符串的关系运算符与C语言中的compare()类似,这里直接复用库里面的compare()函数.

需要注意的是compare()函数是使用C语言中的字符串格式进行比较的,这里比较的是string对象中_str指向的数组.

//relational operatorsbool operator<(const string& s1, const string& s2){return strcmp(s1.c_str(), s2.c_str()) < 0;}bool operator==(const string s1, const string& s2){return strcmp(s1.c_str(), s2.c_str()) == 0;}bool operator<=(const string s1, const string& s2){return s1 < s2 || s1 == s2;}bool operator>(const string s1, const string& s2){return !(s1 <= s2);}bool operator>=(const string s1, const string& s2){return !(s1 < s2);}bool operator!=(const string s1, const string& s2){return !(s1 == s2);}

8.2输入输出重载(operator>> and operator<<) 

         这里的输入和输出为什么重载成全局函数请参考C++类和对象(5)--日期类的实现中友元声明中的注释.

 8.2.1输出运算符重载

        遍历string对象一个一个输出即可.

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

8.2.2输入运算符重载

        这里首先先清除s中原有的数据,然后在栈里面开一个256大小的buff(为了减少扩容的次数).这里用get()函数一个一个读取输入的字符,如果用输入运算符的话会忽略输入的空格和换行符.当一个buff满了之后拷贝到s中,跳出循环后如果buff中还有遗留的数据,则全部拷贝到s中.

	istream& operator>>(istream& _cin, string& s){s.clear();const int N = 256;char buff[N];int i = 0;char ch;//in >> ch; 默认会忽略空格和换行符ch = _cin.get();while (ch != ' ' && ch != '\n'){buff[i++] = ch;if (i == N - 1){buff[i] = '\0';s += buff;i = 0;}//in >> ch;ch = _cin.get();}if (i > 0){buff[i] = '\0';s += buff;}return _cin;}

9.参考代码

        string实现在XiaoC这个命名空间中,上述的代码没有做测试,下面给出测试代码,有兴趣可以自行对接口进行测试.

9.1string.h 

#pragma once
#define _CRT_SECURE_NO_WARNINGS 1
#include <iostream>
#include <assert.h>
#include <string>
using namespace std;namespace XiaoC
{class string{//friend ostream& operator<<(ostream& _cout, const XiaoC::string& s);//friend istream& operator>>(istream& _cin, XiaoC::string& s);public:默认构造函数//string()//	//直接给空指针在使用.c_str()时进行打印对空指针进行了解引用//	:_str(new char[1] {'\0'})//	//:_str(nullptr)//	,_size(0)//	,_capacity(0)//{}//带参的构造//string(const char* str)//{//	_size = strlen(str);//	//_capacity不包含\0//	_capacity = _size;//	//开空间的时候多开一个用于存储\0//	_str = new char[_capacity + 1];//	strcpy(_str, str);//}//将上述两个构造函数合并成一个全缺省的构造函数string(const char* str = "")	//空的常量字符串默认包含一个\0{_size = strlen(str);_capacity = _size;_str = new char[_capacity + 1];strcpy(_str, str);	//strcpy先拷贝再判断,会拷贝\0}//拷贝构造//传统写法//string(const string& s)//{//	_str = new char[s._capacity + 1];//	strcpy(_str, s._str);//	_size = s._size;//	_capacity = s._capacity;//}//现代写法string(const string& s){string tmp(s._str);swap(tmp);}//赋值重载//写法1//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;//}//写法2//string& operator=(const string& s)//{//	if (this != &s)//	{//		string tmp(s);//		swap(tmp);//	}//	return *this;//}//写法3string& operator=(string tmp){swap(tmp);	//这里虽然tmp被交换了,但是形参的改变不影响实参return *this;}~string(){if (_str){delete[] _str;_str = nullptr;_size = 0;_capacity = 0;}}// iteratortypedef 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;}// modifyvoid push_back(char c);void append(const char* str);string& operator+=(char c);string& operator+=(const char* str);void swap(string& s) {std::swap(_str, s._str);std::swap(_size, s._size);std::swap(_capacity, s._capacity);}//返回string中的指向字符串的指针const char* c_str() const{return _str;}// capacitysize_t size() const{return _size;}size_t capacity() const{return _capacity;}void clear(){_str[0] = '\0';_size = 0;}void reserve(size_t n);// accesschar& operator[](size_t index){assert(index < _size);return _str[index];}const char& operator[](size_t index)const{assert(index < _size);return _str[index];}// 返回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/字符串strvoid insert(size_t pos, char c);void insert(size_t pos, const char* str);// 删除pos位置上的元素,并返回该元素的下一个位置void erase(size_t pos, size_t len = npos);//返回子串string substr(size_t pos = 0, size_t len = npos) const;private:char* _str = nullptr;size_t _capacity = 0;size_t _size = 0;//这里可以直接给默认值,相当于定义,因为有const,只有整型可以//static const size_t npos = -1;static const size_t npos;};//relational operatorsbool operator<(const string& s1, const string& s2);bool operator==(const string s1, const string& s2);bool operator<=(const string s1, const string& s2);bool operator>(const string s1, const string& s2);bool operator>=(const string s1, const string& s2);bool operator!=(const string s1, const string& s2);ostream& operator<<(ostream& _cout, const string& s);istream& operator>>(istream& _cin, string& s);}

9.2string.cpp

#include "string.h"
namespace XiaoC
{const size_t string::npos = -1;void string::push_back(char c){//扩容if (_size == _capacity){reserve(_capacity == 0 ? 4 : _capacity * 2);}_str[_size] = c;++_size;_str[_size] = '\0';}void string::append(const char* str){size_t len = strlen(str);if (_size + len > _capacity){//大于2倍,需要多少开多少,小于2倍开2倍reserve((_size + len) > (2 * _capacity) ? (_size + len) : (2 * _capacity));}strcpy(_str + _size, str);_size += len;}string& string::operator+=(char c){push_back(c);return *this;}string& string::operator+=(const char* str){append(str);return *this;}void string::reserve(size_t n){if (n > _capacity){char* tmp = new char[n + 1];strcpy(tmp, _str);delete[] _str;_str = tmp;_capacity = n;}}// 在pos位置上插入字符c/字符串strvoid string::insert(size_t pos, char c){assert(pos <= _size);if (_size == _capacity){reserve(_capacity == 0 ? 4 : _capacity * 2);}//这里用size_t类型头插会出错,因为end永远都小于不了0//size_t end = _size;	//从最后一个\0开始往后挪动数据//用int end在与pos比较时会提升为size_t类型,也会出错//int end = _size;//while (end >= (int)pos)//{//	_str[end + 1] = _str[end];//	end--;//}//挪动数据,_size处是\0,从后面的\0开始挪动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){//大于2倍,需要多少扩多少,小于2倍,扩2倍reserve((_size + len) > (2 * _capacity) ? (_size + len) : (2 * _capacity));}size_t end = _size + len;while (end >= pos + len - 1){_str[end] = _str[end - len];--end;}for (size_t i = 0; i < len; i++){_str[pos + i] = str[i];}_size += len;}// 删除pos位置上之后的len个元素void string::erase(size_t pos, size_t len){assert(pos < _size);if (len >= _size - pos){_str[pos] = '\0';_size = pos;}else{for (size_t i = pos + len; i <= _size; i++){_str[i - len] = _str[i];}_size -= len;}}// 返回c在string中第一次出现的位置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;}// 返回子串s在string中第一次出现的位置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;}}//返回子串string string::substr(size_t pos, size_t len) const{assert(pos < _size);//len大于剩余字符的长度,更新一下lenif (len > _size - pos){len = _size - pos;}string sub;sub.reserve(len);for (size_t i = 0; i < len; i++){sub += _str[pos + i];}return sub;}//relational operatorsbool operator<(const string& s1, const string& s2){return strcmp(s1.c_str(), s2.c_str()) < 0;}bool operator==(const string s1, const string& s2){return strcmp(s1.c_str(), s2.c_str()) == 0;}bool operator<=(const string s1, const string& s2){return s1 < s2 || s1 == s2;}bool operator>(const string s1, const string& s2){return !(s1 <= s2);}bool operator>=(const string s1, const string& s2){return !(s1 < s2);}bool operator!=(const string s1, const string& s2){return !(s1 == s2);}ostream& operator<<(ostream& _cout, const string& s){for (auto ch : s){_cout << ch;}return _cout;}istream& operator>>(istream& _cin, string& s){s.clear();const int N = 256;char buff[N];int i = 0;char ch;//in >> ch; 默认会忽略空格和换行符ch = _cin.get();while (ch != ' ' && ch != '\n'){buff[i++] = ch;if (i == N - 1){buff[i] = '\0';s += buff;i = 0;}//in >> ch;ch = _cin.get();}if (i > 0){buff[i] = '\0';s += buff;}return _cin;}
}

9.3Test.cpp

#include "string.h"namespace XiaoC
{//测试构造函数void test_string1(){string s1;string s2("hello world");cout << s1.c_str() << endl;cout << s2.c_str() << endl;}//测试遍历访问void test_string2(){string s1("hello world");cout << s1.c_str() << endl;//[] + 下标访问for (size_t i = 0; i < s1.size(); i++){s1[i] += 2;}cout << s1.c_str() << endl;//范围for底层就是替代为迭代器for (auto e : s1){cout << e << " ";}cout << endl;//通过迭代器遍历访问string::iterator it = s1.begin();while (it != s1.end()){cout << *it << " ";++it;}cout << endl;}//测试增删改查void test_string3(){string s1 = "hello world";s1 += 'x';s1 += 'c';cout << s1.c_str() << endl;s1 += " hello XiaoC";cout << s1.c_str() << endl;string s2 = "abcd";cout << s2.c_str() << endl;s2.insert(0, 'c');cout << s2.c_str() << endl;string s3 = "hello world";cout << s3.c_str() << endl;s3.insert(0, "xxx");cout << s3.c_str() << endl;string s4 = "hello world";cout << s4.c_str() << endl;s4.erase(0, 3);cout << s4.c_str() << endl;string s("test.cpp.zip");size_t pos = s.find('.');string suffix = s.substr(pos);cout << suffix.c_str() << endl;//拷贝构造string copy(s);cout << copy.c_str() << endl;//赋值运算符string s5 = "XiaoC";s5 = s;cout << s5.c_str() << endl;}//测试比较大小void test_string4(){string s1 = "helloworld";string s2 = s1;cout << (s1 == s2) << endl;cout << (s1 < s2) << endl;}//测试输入输出void test_string5(){string s1 = "hello world";cout << s1 << endl;string s2;cin >> s2;cout << s2 << endl;}//测试返回字串void test_string6(){string s1 = "hello world";string s2;s2 = s1.substr(6, 5);cout << s2 << endl;}
}
int main()
{//XiaoC::test_string1();//XiaoC::test_string2();//XiaoC::test_string3();//XiaoC::test_string4();XiaoC::test_string5();//XiaoC::test_string6();return 0;
}

 

 

 

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

如若内容造成侵权/违法违规/事实不符,请联系一条长河网进行投诉反馈,一经查实,立即删除!

相关文章

Qt 状态机编程,双层状态机,实现暂停恢复

流程设计状态图 #ifndef WORKMACHINE_H #define WORKMACHINE_H#include <QObject> #include <QStateMachine> #include <QHistoryState> #include <QFinalState>#include "WorkThread.h"class WorkMachine : public QObject {Q_OBJECT publ…

记录可编辑表格(未完整)

每一行都独立 <el-table-column label"操作" width"220" fixed"right"><template #default"{ row, $index }"><el-buttonv-if"!row.tableEditFlag"type"primary"size"small"click"…

螺栓与散装物体检测系统源码分享

螺栓与散装物体检测检测系统源码分享 [一条龙教学YOLOV8标注好的数据集一键训练_70全套改进创新点发刊_Web前端展示] 1.研究背景与意义 项目参考AAAI Association for the Advancement of Artificial Intelligence 项目来源AACV Association for the Advancement of Comput…

[云服务器12] 搭建eaglercraft网页MC

众所周知&#xff0c;MC是一个炒鸡好玩的游戏&#xff01; 但是&#xff0c;Mojang开发出来是经过Java JAR打包过的的.jar文件&#xff0c;这就不得不依赖HMCL PCL BakaXL等启动器来启动了…… 所以今天&#xff0c;我们将使用开源的eaglercraft来搭建一个在线版MC&#xff0…

誉龙视音频 Third/TimeSyn 远程命令执行复现

0x01 漏洞描述&#xff1a; 誉龙公司定位为系统级的移动视音频记录解决方案提供商&#xff0c;凭借其深厚的行业经验&#xff0c;坚持自主研发&#xff0c;匠心打造记录仪领域行业生态&#xff0c;提供开放式的记录仪APK、GB28181 SDK、国网B协议、管理平台软件OEM。誉龙视音频…

C/S架构与B/S架构的适用场景分析

C/S架构&#xff08;客户端/服务器架构&#xff09;与B/S架构&#xff08;浏览器/服务器架构&#xff09;在适用场景上各有特点&#xff0c;主要取决于应用的具体需求、用户群体、系统维护成本、跨平台需求等因素。 一、C/S架构的适用场景 1、高性能与交互性要求高的应用&…

9月26日云技术研讨会 | SOA整车EE架构开发流程及工具实施方案

面向服务的架构&#xff08;Service Oriented Architecture, SOA&#xff09;实施需要复杂的基础技术作为支撑&#xff0c;伴随着整车硬件资源的集中化、车载以太网等高速通信技术在车内的部署&#xff0c;将在未来一段时间内成为行业技术研究和市场布局的热点。 近年来&#x…

使用Webpack创建vue脚手架并搭建路由---详解

1.使用 vue 库 vue 是一个非常好用的 javascript 库&#xff0c;现在已经发行了 vue 3&#xff0c;我们可以直接导入使用库文件&#xff0c;也可以使用单文件&#xff08;SFC&#xff09;的形式&#xff0c;直接使用库文件会简单一点&#xff0c;我们先来试一下吧。 1.1安装 v…

JdbcTemplate常用方法一览AG网页参数绑定与数据寻址实操

JdbcTemplate是Spring框架中的一个重要组件&#xff0c;主要用于简化JDBC数据库操作。它提供了许多常用的方法&#xff0c;如查询、插入、更新、删除等。本文将介绍JdbcTemplate的常用方法及其使用方式&#xff0c;以及参数绑定和删除数据的方法。 一、JdbcTemplate常用方法 查…

24/9/19 算法笔记 kaggle BankChurn数据分类

题目是要预测银行里什么样的客户会流失&#xff0c;流失的概率是多少 我这边先展示一下我写的二分类的算法 import numpy as np import pandas as pd import matplotlib.pyplot as plt from sklearn.model_selection import train_test_split from sklearn.linear_model impo…

金砖软件测试赛项之Jmeter如何录制脚本!

一、简介 Apache JMeter 是一款开源的性能测试工具&#xff0c;用于测试各种服务的负载能力&#xff0c;包括Web应用、数据库、FTP服务器等。它可以模拟多种用户行为&#xff0c;生成负载以评估系统的性能和稳定性。 JMeter 的主要特点&#xff1a; 图形用户界面&#xff1a;…

Stable Diffusion绘画 | ControlNet应用-IP-Adapter:一致性角色就这么简单

IP-Adapter 更新了全新的模型—FaceID plus V2 版本&#xff0c;同时还支持 SDXL 模型。 FaceID plus V2 版本的优点&#xff1a; 解决任务一致性 一张图生成相似角色 下载地址&#xff1a;https://huggingface.co/h94/IP-Adapter-FaceID/tree/main 其中&#xff0c;两个 Lora文…

AIGC时代!AI的“iPhone时刻”与投资机遇

AIGC时代&#xff01;AI的“iPhone时刻”与投资机遇 前言AI的“iPhone时刻”与投资机遇 前言 AIGC&#xff0c;也就是人工智能生成内容&#xff0c;它就像是一股汹涌的浪潮&#xff0c;席卷了整个科技世界。它的出现&#xff0c;让我们看到了人工智能的无限潜力&#xff0c;也…

基于协同过滤算法+PHP的新闻推荐系统

作者&#xff1a;计算机学姐 开发技术&#xff1a;SpringBoot、SSM、Vue、MySQL、JSP、ElementUI、Python、小程序等&#xff0c;“文末源码”。 专栏推荐&#xff1a;前后端分离项目源码、SpringBoot项目源码、SSM项目源码 系统展示 【2025最新】基于协同过滤算法PHPMySQL的新…

【STM32 HAL库】OLED显示模块

【STM32 HAL库】OLED显示模块 前言理论OLED基本参数OLED基本驱动原理OLED坐标轴 应用CubeMx配置底层函数代码高层封装函数printf显示函数 前言 本文为笔者学习 OLED 的总结&#xff0c;基于keysking的视频内容&#xff0c;如有错误&#xff0c;欢迎指正 理论 OLED基本参数 …

react + antDesign封装图片预览组件(支持多张图片)

需求场景&#xff1a;最近在开发后台系统时经常遇到图片预览问题&#xff0c;如果一个一个的引用antDesign的图片预览组件就有点繁琐了&#xff0c;于是在antDesign图片预览组件的基础上二次封装了一下&#xff0c;避免重复无用代码的出现 效果 公共预览组件代码 import React…

【machine learning-十-grading descent梯度下降实现】

grading descent 梯度下降参数更新方法 --导数和学习率 从导数项直观理解梯度下降 grading descent 算法就是更新参数&#xff0c;今天来学习下如何更新w和b 梯度下降 还是以线性回归的均方差损失函数如下为例&#xff1a; 损失函数的可视化图如下 &#xff1a; 横轴和纵轴分…

影刀RPA实战:网页爬虫之苦瓜书籍数据

书籍常常被视为心灵的慰藉&#xff0c;因为它们能够在不同的层面上为人们提供支持和安慰。 1. 书籍对我们的重要性 书籍是人类知识的载体&#xff0c;也是智慧的结晶。它们不仅是学习的工具&#xff0c;更是人类心灵的慰藉。在忙碌的生活中&#xff0c;书籍能够提供知识、启发…

魅思CMS getOrderStatus SQL注入漏洞复现

0x01 漏洞描述&#xff1a; 魅思是一款集成了视频管理、用户管理、手机端应用封装等功能的综合性视频管理系统。该系统不仅以其强大的视频管理功能、灵活的用户管理机制、便捷的手机端应用封装功能以及高安全性和现代化的界面设计&#xff0c;成为了市场上备受关注的视频管理系…

技术美术百人计划 | 《4.5 DOF景深算法》笔记

1. 景深定义 景深&#xff08;Depth of Field&#xff0c;DOF&#xff09;&#xff0c;是指在摄影机镜头或其他成像器前沿能够取得清晰图像的成像所测定的被摄物体前后距离范围。镜头光圈、镜头焦距、及焦平面到拍摄物的距离是影响景深的重要因素。在聚焦完成后&#xff0c;焦点…