当前位置: 首页 > news >正文

C++学习之路,从0到精通的征途:string类的模拟实现

目录

一.string类的成员变量与成员函数

二.string类的接口实现

1.构造函数,析构函数,拷贝构造函数,赋值重载

(1)构造函数

(2)析构函数        

(3)拷贝构造函数

(4)赋值重载 

2.迭代器        

3.容量:size,capacity,empty,resize,reserve

(1)size

(2)capacity

(3)empty

(4)reserve

(5)resize

4.访问:operator[]

5.修改:push_back,operator+=,append,clear,swap,c_str

(1)push_back

(2)operator[]

(3)append

(4)operator+=

(5) clear

(6)swap

(7)c_str

6.逻辑运算符

7.查找:find 

8.在固定位置插入数据:insert

9.删除固定位置的数据:erase

10.流插入,流提取运算符重载:<<,>>

(1)流插入<<

(2)流提取>>

三.现代版写法的string类

1.构造函数

2.拷贝构造函数

3.赋值重载

四.代码总览 

string.h

string.cpp

test.cpp


一.string类的成员变量与成员函数

namespace my_string
{class string{friend std::ostream& operator<<(std::ostream& _cout, const my_string::string& s);friend std::istream& operator>>(std::istream& _cin, my_string::string& s);public:typedef char* iterator;typedef const char* const_iterator;string(const char* str = ""):_size(strlen(str)){                                                              _str = new char[_size + 1];_capacity = _size;memcpy(_str, str, _size + 1);}string(const string& s){_str = new char[s._capacity + 1];memcpy(_str, s._str, s._size + 1);_size = s._size;_capacity = s._capacity;}string& operator=(const string& s){char* tmp = new char[s._capacity + 1];memcpy(tmp, s._str, s._size + 1);delete[] _str;_str = tmp;_size = s._size;_capacity = s._capacity;return *this;}~string(){delete[] _str;_str = nullptr;_size = _capacity = 0;}// iteratoriterator begin();iterator end();const_iterator begin() const;const_iterator end() const;// capacitysize_t size() const;size_t capacity() const;bool empty() const;void resize(size_t n, char c = '\0');void reserve(size_t n);// accesschar& operator[](size_t index);const char& operator[](size_t index)const;// relational operatorsbool operator<(const string& s);bool operator<=(const string& s);bool operator>(const string& s);bool operator>=(const string& s);bool operator==(const string& s);bool operator!=(const string& s);// modifyvoid 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;// 返回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,并返回该字符的位置string& insert(size_t pos, char c);string& insert(size_t pos, const char* str);// 删除pos位置上的元素,并返回该元素的下一个位置string& erase(size_t pos, size_t len);private:char* _str;size_t _size;size_t _capacity;public:static const size_t npos;};
}

二.string类的接口实现

1.构造函数,析构函数,拷贝构造函数,赋值重载

(1)构造函数

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

        _size在初始化列表中用给定的字符串的长度初始化,若没有给定字符串,则采用缺省值的空字符串。

        在函数体内初始化_str,new一个size+1大小的连续空间,用来存放给定的字符串的‘\0’,申请完空间后,最后将给定字符串中的数据按字节拷贝给_str。

(2)析构函数        

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

(3)拷贝构造函数

string(const string& s)
{_str = new char[s._capacity + 1];memcpy(_str, s._str, s._size + 1);_size = s._size;_capacity = s._capacity;
}

        如果在这里不写拷贝构造,那么编译器则会调用默认的拷贝构造来进行浅拷贝,则会导致同一块空间被析构两次,所以这里需要手动写深拷贝,并将数据按字节转移到新对象中。

(4)赋值重载 

string& operator=(const string& s)
{char* tmp = new char[s._capacity + 1];memcpy(tmp, s._str, s._size + 1);delete[] _str;_str = tmp;_size = s._size;_capacity = s._capacity;return *this;
}

        赋值重载与拷贝构造的区别在于需要将被赋值对象所申请的资源释放,并返回被赋值的对象,实现连续赋值。

2.迭代器        

        string类的迭代器以指针为底层逻辑进行实现:

typedef char* iterator;
typedef const char* const_iterator;
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;
}

        有了迭代器之后就可以实现范围for:

3.容量:size,capacity,empty,resize,reserve

(1)size

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

(2)capacity

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

(3)empty

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

(4)reserve

        reserve所传的参数n一般分为两种情况:

        根据std中的reserve,我们在n<capacity的情况下,对容量不做处理。 

        在n>capacity的情况下,进行异地扩容,并将原数据复制到新空间中,释放旧空间,更新capacity大小:

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

(5)resize

        resize所传的参数n一般分为三种情况:

        当n<size,删除数据,不改变容量。

        当size<n<capacity,插入数据,不改变容量。

        当n>capacity,先进行扩容,再插入数据。

        由于resize如果不传用来插入的数据c,则直接用'\0'进行填充,所以缺省值采用'\0':

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

4.访问:operator[]

char& string::operator[](size_t index)
{return _str[index];
}const char& string::operator[](size_t index) const
{return _str[index];
}

5.修改:push_back,operator+=,append,clear,swap,c_str

(1)push_back

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

(2)operator[]

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

(3)append

void string::append(const char* str)
{size_t len = strlen(str);if (_size + len > _capacity){// 扩容size_t newcapacity = _size + len > 2 * _capacity ? _size + len : 2 * _capacity;reserve(newcapacity);}memcpy(_str + _size, str, len + 1);_size += len;
}

(4)operator+=

// += 单个字符
string& string::operator+=(char c)
{push_back(c);return *this;
}// += 字符串
string& string::operator+=(const char* str)
{append(str);return *this;
}

(5) clear

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

(6)swap

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

(7)c_str

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

6.逻辑运算符

        对于比较运算符,字符的ASCII码相等,则继续比较,不相等,则根据实际情况返回比较结果,若到比较结束,并未返回,则根据字符串的长度来决定返回结果:

        以operator<和operator==为例:

bool string::operator<(const string& s)
{size_t end1 = _size - 1;size_t end2 = s._size - 1;size_t begin1 = 0;size_t begin2 = 0;while (begin1 != end1 && begin2 != end2){if (_str[begin1] < s._str[begin2]){return true;}else if (_str[begin1] > s._str[begin2]){return false;}begin1++;begin2++;}//if (begin1 == end1 && begin2 != end2)//{//	return true;//}//else//{//	return false;//}return begin1 == end1 && begin2 != end2;
}bool string::operator==(const string& s)
{size_t end1 = _size - 1;size_t end2 = s._size - 1;size_t begin1 = 0;size_t begin2 = 0;while (begin1 != end1 && begin2 != end2){if (_str[begin1] != s._str[begin2]){return false;}begin1++;begin2++;}return begin1 == end1 && begin2 == end2;
}

        这里的比较没有使用strcmp的原因是strcmp在遇到'\0'后则不再比较,无法解决字符串中包含'\0'的情况。 

        在实现了任一比较大小和相等后,剩余的比较运算符则可以直接进行复用:

bool string::operator<=(const string& s)
{return *this < s || *this == s;
}bool string::operator>(const string& s)
{return !(*this <= s);
}bool string::operator>=(const string& s)
{return !(*this < s);
}bool string::operator!=(const string& s)
{return !(*this == s);
}

7.查找:find 

(1)从pos位置开始向后查找字符c,返回c在string中第一次出现的位置,没找到返回npos

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

(2)从pos位置开始向后查找字符串s,返回s在string中第一次出现的位置,没找到返回npos

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

8.在固定位置插入数据:insert

(1)在pos位置上插入字符c

string& string::insert(size_t pos, char c)
{assert(pos <= _size);if (_size == _capacity){// 扩容size_t newcapacity = _capacity == 0 ? 4 : 2 * _capacity;reserve(newcapacity);}//size_t end = _size + 1;//while (end > pos)//{//	_str[end] = _str[end - 1];//	end--;//}memmove(_str + pos + 1, _str + pos, _size - pos + 1);_str[pos] = c;_size++;return *this;
}

(2)在pos位置上插入字符串str

string& string::insert(size_t pos, const char* str)
{size_t len = strlen(str);if (_size + len > _capacity){size_t newcapacity = _size + len > 2 * _capacity ? _size + len : 2 * _capacity;reserve(newcapacity);}memmove(_str + pos + len, _str + pos, _size + 1 - pos);memcpy(_str + pos, str, len);_size += len;return *this;
}

9.删除固定位置的数据:erase

        当从pos位置开始要删除元素的个数大于剩余元素个数或等于npos时,将pos为之后的字符全部删除。

        当从pos位置开始要删除元素的个数小于剩余元素个数时,将要被删除的元素用后面的元素覆盖:

string& string::erase(size_t pos, size_t len)
{assert(pos < _size);if (len == npos || len >= _size - pos){_str[pos] = '\0';_size = pos;return *this;}memmove(_str + pos, _str + pos + len, _size + 1 - (pos + len));_size -= len;return *this;
}

10.流插入,流提取运算符重载:<<,>>

(1)流插入<<

std::ostream& operator<<(std::ostream& _cout, const my_string::string& s)
{for (int i = 0; i < s.size(); ++i){_cout << s[i];}return _cout;
}

(2)流提取>>

std::istream& operator>>(std::istream& _cin, my_string::string& s)
{// 每次提取前需要将s中的数据清除s.clear();// 模拟string中的buff缓冲区,当所输入的字符串有效数据个数小于256时,存放到buff中char buff[256];// 使用get函数,能够输入空白字符和换行符char ch = _cin.get();size_t i = 0;while (ch != ' ' && ch != '\n'){buff[i++] = ch;ch = _cin.get();if (i == 255){s += buff;// 重置buffi = 0;}}// buff中有剩余字符if (i > 0){buff[i] = '\0';s += buff;}return _cin;
}

三.现代版写法的string类

1.构造函数

        用迭代器初始化string类:

template <class InputIterator>
string(InputIterator first, InputIterator last)
{while (first != last){push_back(*first);++first;}
}

2.拷贝构造函数

// s2(s1)
string::string(const string& s)
{//string tmp(s._str);string tmp(s.begin(), s.end());swap(tmp);
}

        这里用s1的成员变量_str构造tmp,再将tmp和s2交换swap,这样tmp中的成员变量就与s2中的成员变量交换,出函数体后tmp同时也会被析构。

3.赋值重载

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

四.代码总览 

string.h

#pragma once
#include<string.h>
#include<assert.h>
#include<iostream>namespace my_string
{class string{friend std::ostream& operator<<(std::ostream& _cout, const my_string::string& s);friend std::istream& operator>>(std::istream& _cin, my_string::string& s);public:typedef char* iterator;typedef const char* const_iterator;string(const char* str = ""):_size(strlen(str)){                                                              _str = new char[_size + 1];_capacity = _size;memcpy(_str, str, _size + 1);}string(const string& s){_str = new char[s._capacity + 1];memcpy(_str, s._str, s._size + 1);_size = s._size;_capacity = s._capacity;}string& operator=(const string& s){char* tmp = new char[s._capacity + 1];memcpy(tmp, s._str, s._size + 1);delete[] _str;_str = tmp;_size = s._size;_capacity = s._capacity;return *this;}~string(){delete[] _str;_str = nullptr;_size = _capacity = 0;}// iteratoriterator begin();iterator end();const_iterator begin() const;const_iterator end() const;// capacitysize_t size() const;size_t capacity() const;bool empty() const;void resize(size_t n, char c = '\0');void reserve(size_t n);// accesschar& operator[](size_t index);const char& operator[](size_t index) const;// relational operatorsbool operator<(const string& s);bool operator<=(const string& s);bool operator>(const string& s);bool operator>=(const string& s);bool operator==(const string& s);bool operator!=(const string& s);// modifyvoid 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;// 返回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,并返回该字符的位置string& insert(size_t pos, char c);string& insert(size_t pos, const char* str);// 删除pos位置上的元素,并返回该元素的下一个位置string& erase(size_t pos, size_t len);private:char* _str;size_t _size;size_t _capacity;public:static const size_t npos;};
}

string.cpp

#include"string.h"namespace my_string
{const size_t string::npos = -1;std::ostream& operator<<(std::ostream& _cout, const my_string::string& s){for (int i = 0; i < s.size(); ++i){_cout << s[i];}return _cout;}std::istream& operator>>(std::istream& _cin, my_string::string& s){// 每次提取前需要将s中的数据清除s.clear();// 模拟string中的buff缓冲区,当所输入的字符串有效数据个数小于256时,存放到buff中char buff[256];// 使用get函数,能够输入空白字符和换行符char ch = _cin.get();size_t i = 0;while (ch != ' ' && ch != '\n'){buff[i++] = ch;ch = _cin.get();if (i == 255){s += buff;// 重置buffi = 0;}}// buff中有剩余字符if (i > 0){buff[i] = '\0';s += buff;}return _cin;}const char* string::c_str() const{return _str;}size_t string::size() const{return _size;}size_t string::capacity() const{return _capacity;}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;}bool string::empty() const{return _size == 0;}void string::resize(size_t n, char c){//if (n < _size)//{//	_str[n] = '\0';//	_size = n;//}//if (n > _size && n < _capacity)//{//	while (_size != n)//	{//		_str[_size++] = c;//	}//	_str[n] = '\0';//}//if (n > _capacity)//{//	(*this).reserve(n);//}if (n > _size){if (n > _capacity){reserve(n);}memset(_str + _size, c, n - _size);}_str[n] = '\0';_size = n;}void string::reserve(size_t n){if (n > _capacity){char* tmp = new char[n + 1];memcpy(tmp, _str, _size + 1);delete[] _str;_str = tmp;_capacity = n;}}char& string::operator[](size_t index){return _str[index];}const char& string::operator[](size_t index) const{return _str[index];}bool string::operator<(const string& s){size_t end1 = _size - 1;size_t end2 = s._size - 1;size_t begin1 = 0;size_t begin2 = 0;while (begin1 != end1 && begin2 != end2){if (_str[begin1] < s._str[begin2]){return true;}else if (_str[begin1] > s._str[begin2]){return false;}begin1++;begin2++;}//if (begin1 == end1 && begin2 != end2)//{//	return true;//}//else//{//	return false;//}return begin1 == end1 && begin2 != end2;}bool string::operator==(const string& s){size_t end1 = _size - 1;size_t end2 = s._size - 1;size_t begin1 = 0;size_t begin2 = 0;while (begin1 != end1 && begin2 != end2){if (_str[begin1] != s._str[begin2]){return false;}begin1++;begin2++;}return begin1 == end1 && begin2 == end2;}bool string::operator<=(const string& s){return *this < s || *this == s;}bool string::operator>(const string& s){return !(*this <= s);}bool string::operator>=(const string& s){return !(*this < s);}bool string::operator!=(const string& s){return !(*this == s);}void string::push_back(char c){if (_size == _capacity){size_t newcapacity = _capacity == 0 ? 4 : 2 * _capacity;reserve(newcapacity);}_str[_size++] = c;_str[_size] = '\0';}void string::append(const char* str){size_t len = strlen(str);if (_size + len > _capacity){size_t newcapacity = _size + len > 2 * _capacity ? _size + len : 2 * _capacity;reserve(newcapacity);}memcpy(_str + _size, str, len + 1);_size += len;}string& string::operator+=(char c){push_back(c);return *this;}string& string::operator+=(const char* str){append(str);return *this;}void string::clear(){_str[0] = '\0';_size = 0;}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 c, size_t pos) const{assert(pos < _size);for (int 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* tmp = strstr(_str + pos, s);if (tmp == nullptr){return npos;}return tmp - _str;}string& string::insert(size_t pos, char c){assert(pos <= _size);if (_size == _capacity){size_t newcapacity = _capacity == 0 ? 4 : 2 * _capacity;reserve(newcapacity);}//size_t end = _size + 1;//while (end > pos)//{//	_str[end] = _str[end - 1];//	end--;//}memmove(_str + pos + 1, _str + pos, _size - pos + 1);_str[pos] = c;_size++;return *this;}string& string::erase(size_t pos, size_t len){assert(pos < _size);if (len == npos || len >= _size - pos){_str[pos] = '\0';_size = pos;return *this;}memmove(_str + pos, _str + pos + len, _size + 1 - (pos + len));_size -= len;return *this;}string& string::insert(size_t pos, const char* str){size_t len = strlen(str);if (_size + len > _capacity){size_t newcapacity = _size + len > 2 * _capacity ? _size + len : 2 * _capacity;reserve(newcapacity);}memmove(_str + pos + len, _str + pos, _size + 1 - pos);memcpy(_str + pos, str, len);_size += len;return *this;}
}

test.cpp

#include"string.h"
using namespace std;namespace my_string
{void Test_String1(){string s1("Hello world");cout << s1.c_str() << endl;                              cout << s1.size() << endl;           cout << s1.capacity() << endl;string s2 = "Hello";cout << s2.c_str() << endl;cout << s2.size() << endl;cout << s2.capacity() << endl;string s3(s2);cout << s3.c_str() << endl;cout << s3.size() << endl;cout << s3.capacity() << endl;string s4 = s1;cout << s4.c_str() << endl;cout << s4.size() << endl;cout << s4.capacity() << endl;s4 = s2;cout << s4.c_str() << endl;cout << s4.size() << endl;cout << s4.capacity() << endl;}void Test_String2(){string s1 = "Hello World";string::iterator it = s1.begin();while (it != s1.end()){(*it)++;cout << *it << ' ';++it;}cout << endl;const string s2(s1);string::const_iterator cit = s2.begin();while (cit != s2.end()){cout << *cit << ' ';++cit;}cout << endl;for (char e : s2){cout << e << ' ';}}void Test_String3(){string s1 = "Hello World";s1.reserve(100);cout << s1.c_str() << endl;cout << s1.size() << endl;cout << s1.capacity() << endl;cout << s1[0] << endl;++s1[0];cout << s1.c_str() << endl;string s2 = "Hello";s2.resize(2);cout << s2.c_str() << endl;cout << s2.size() << endl;cout << s2.capacity() << endl;s2.resize(4);cout << s2.c_str() << endl;cout << s2.size() << endl;cout << s2.capacity() << endl;s2.resize(10);cout << s2.c_str() << endl;cout << s2.size() << endl;cout << s2.capacity() << endl;//std::string s2 = "Hello";//s2.resize(2);//cout << s2.c_str() << endl;//cout << s2.size() << endl;//cout << s2.capacity() << endl;//s2.resize(4);//cout << s2.c_str() << endl;//cout << s2.size() << endl;//cout << s2.capacity() << endl;//s2.resize(10);//cout << s2.c_str() << endl;//cout << s2.size() << endl;//cout << s2.capacity() << endl;}void Test_String4(){string s1 = "Hello World";string s2 = "Hello";cout << (s1 < s2) << endl;cout << (s2 < s1) << endl;string s3(s1);cout << (s1 == s3) << endl;cout << (s2 == s3) << endl;cout << (s1 >= s2) << endl;cout << (s1 != s2) << endl;cout << (s2 <= s1) << endl;}void Test_String5(){string s1("Hello");s1.push_back('x');s1 += 'x';s1 += "xxxxx";cout << s1.c_str() << endl;cout << s1.size() << endl;cout << s1.capacity() << endl;const char* p = "Worldxxxxxxx";string s3 = "Hello ";cout << s3.c_str() << endl;cout << s3.size() << endl;cout << s3.capacity() << endl;s3.append(p);cout << s3.c_str() << endl;cout << s3.size() << endl;cout << s3.capacity() << endl;string s4 = "Hello";string s5 = "Worldxxxxxxxx";cout << s4.c_str() << endl;cout << s4.size() << endl;cout << s4.capacity() << endl;cout << s5.c_str() << endl;cout << s5.size() << endl;cout << s5.capacity() << endl;s4.swap(s5);cout << s4.c_str() << endl;cout << s4.size() << endl;cout << s4.capacity() << endl;cout << s5.c_str() << endl;cout << s5.size() << endl;cout << s5.capacity() << endl;}void Test_String6(){string s1 = "Hello ";cout << s1.find('e') << endl;s1 += "World";cout << s1.find("Wor") << endl;cout << s1.c_str() << endl;cout << s1.size() << endl;cout << s1.capacity() << endl;// "Hello World"s1.insert(0, 'x');cout << s1.c_str() << endl;cout << s1.size() << endl;cout << s1.capacity() << endl;string s2 = "Hello World";//s2.erase(1, 2);s2.insert(11, "xxx");cout << s2.c_str() << endl;cout << s2.size() << endl;cout << s2.capacity() << endl;}void Test_String7(){string s1 = "Hello World";cout << s1 << endl;cin >> s1;cout << s1 << endl;}}int main()
{my_string::Test_String2();return 0;
}

相关文章:

C++学习之路,从0到精通的征途:string类的模拟实现

目录 一.string类的成员变量与成员函数 二.string类的接口实现 1.构造函数&#xff0c;析构函数&#xff0c;拷贝构造函数&#xff0c;赋值重载 &#xff08;1&#xff09;构造函数 &#xff08;2&#xff09;析构函数 &#xff08;3&#xff09;拷贝构造函数 &…...

网页制作中的MVC和MVT

MVC&#xff08;模型-视图-控制器&#xff09;和MVT&#xff08;模型-模板-视图&#xff09;是两种常见的软件架构模式&#xff0c;通常用于Web应用程序的设计。它们之间的主要区别在于各自的组件职责和工作方式。 MVC&#xff08;模型-视图-控制器&#xff09;&#xff1a; 模…...

02 - spring security基于配置文件及内存的账号密码

spring security基于配置的账号密码 文档 00 - spring security框架使用01 - spring security自定义登录页面 yml文件中配置账号密码 spring:security:user:name: adminpassword: 123456yml文件中配置账号密码后&#xff0c;控制台将不再输出临时密码 基于内存的账号密码 …...

Firebase Studio:开启 AI 驱动的开发新纪元

Firebase Studio&#xff08;前身为 Project IDX&#xff09;的推出&#xff0c;标志着软件开发范式正经历深刻变革。它不仅是一个传统的 IDE&#xff0c;更是一个以 AI 为主导的、代理式 (agentic) 的云端开发环境&#xff0c;专注于全栈 AI 应用&#xff08;包括 API、后端、…...

网络基础2

目录 跨网络传输流程 网络中的地址管理 - 认识 IP 地址 跨网络传输 报文信息的跨网络发送 IP地址的转化 认识端口号 端口号范围划分 源端口号和目的端口号 认识 TCP / UDP协议 理解 socket 网络字节序 socket 编程接口 sockaddr 结构 我们继续来学习网络基础 跨网…...

Maven工具学习使用(十一)——部署项目到仓库

1、使用Maven默认方式 Maven 部署项目时默认使用的上传文件方式是通过 HTTP/HTTPS 协议。要在 Maven 项目中配置部署&#xff0c;您需要在项目的 pom.xml 文件中添加 部分。这个部分定义了如何部署项目的构件&#xff08;如 JAR 文件&#xff09;到仓库。。这个部分定义了如何…...

FPGA 37 ,FPGA千兆以太网设计实战:RGMII接口时序实现全解析( RGMII接口时序设计,RGMII~GMII,GMII~RGMII 接口转换 )

目录 前言 一、设计流程 1.1 需求理解 1.2 模块划分 1.3 测试验证 二、模块分工 2.1 RGMII→GMII&#xff08;接收方向&#xff0c;rgmii_rx 模块&#xff09; 2.2 GMII→RGMII&#xff08;发送方向&#xff0c;rgmii_tx 模块&#xff09; 三、代码实现 3.1 顶层模块 …...

torch.cat和torch.stack的区别

torch.cat 和 torch.stack 是 PyTorch 中用于组合张量的两个常用函数&#xff0c;它们的核心区别在于输入张量的维度和输出张量的维度变化。以下是详细对比&#xff1a; 1. torch.cat (Concatenate) 作用&#xff1a;沿现有维度拼接多个张量&#xff0c;不创建新维度 输入要求…...

索引下推(Index Condition Pushdown, ICP)

概念 索引下推是一种数据库查询优化技术&#xff0c;通过在存储引擎层面应用部分WHERE条件来减少不必要的数据读取。它特别适用于复合索引的情况&#xff0c;因为它可以在索引扫描阶段就排除不符合全部条件的数据行&#xff0c;而不是将所有可能匹配的记录加载到服务器层再进行…...

C++基础精讲-06

文章目录 1. this指针1.1 this指针的概念1.2 this指针的使用 2. 特殊的数据成员2.1 常量数据成员2.2 引用数据成员2.3 静态数据成员2.4 对象成员 3. 特殊的成员函数3.1 静态成员函数3.2 const成员函数3.3 mutable关键字 1. this指针 1.1 this指针的概念 1.c规定&#xff0c;t…...

Django3 - 建站基础

学习开发网站必须了解网站的组成部分、网站类型、运行原理和开发流程。使用Django开发网站必须掌握Django的基本操作&#xff0c;比如创建项目、使用Django的操作指令以及开发过程中的调试方法。 一、网站的定义及组成 网站(Website)是指在因特网上根据一定的规则&#xff0c;…...

UE5蓝图设置界面尺寸大小

UE5蓝图设置界面尺寸大小 Create widget 创建UIadd to Viewport 添加视图get Game User Settings获取游戏用户设置set Screen Resolutions 设置屏幕尺寸大小1920*1080set Fullscreen Mode 设置全屏模式为&#xff1a;窗口化或者全屏Apply Settings 应用设置...

无数字字母RCE

无数字字母RCE&#xff0c;这是一个老生常谈的问题&#xff0c;就是不利用数字和字母构造出webshell&#xff0c;从而能够执行我们的命令。 <?php highlight_file(__FILE__); $code $_GET[code]; if(preg_match("/[A-Za-z0-9]/",$code)){die("hacker!&quo…...

AutoGen参数说明

UserProxyAgent用户 user_proxy = UserProxyAgent配置说明: # 构造参数 def __init__(self,name: str,is_termination_msg: Optional[Callable[[Dict], bool]] = None,max_consecutive_auto_reply: Optional[int] = None,human_input_mode: Literal["ALWAYS", &qu…...

6.2 GitHub API接口设计实战:突破限流+智能缓存实现10K+仓库同步

GitHub Sentinel 定期更新 API 接口设计 关键词:GitHub API 集成、异步爬虫开发、RESTful 接口设计、请求限流策略、数据增量更新 1. 接口架构设计原则 采用 分层隔离架构 实现数据采集与业务逻辑解耦: #mermaid-svg-WihvC78J0F5oGDbs {font-family:"trebuchet ms&quo…...

用java代码如何存取数据库的blob字段

一.业务 在业务中我们被要求将文件或图片等转成 byte[] 或 InputStream存到数据库的Blob类型的字段中. 二.Blob类型介绍 在 MySQL 中&#xff0c;Blob 数据类型用于存储二进制数据。MySQL 提供了四种不同的 Blob 类型&#xff1a; TINYBLOB: 最大存储长度为 255 个字节。BL…...

2025蓝桥杯C++研究生组真题-上海市省赛

2025蓝桥杯C研究生组真题 A&#xff1a;数位倍数&#xff08;5分&#xff09; 问题描述&#xff1a;请问在 1 至 202504&#xff08;含&#xff09;中&#xff0c;有多少个数的各个数位之和是 5 的整数倍。例如&#xff1a;5、19、8025 都是这样的数。 A是填空题&#xff0c…...

原子操作CAS(Compare-And-Swap)和锁

目录 原子操作 优缺点 锁 互斥锁&#xff08;Mutex&#xff09; 自旋锁&#xff08;Spin Lock&#xff09; 原子性 单核单CPU 多核多CPU 存储体系结构 缓存一致性 写传播&#xff08;Write Propagation&#xff09; 事务串行化&#xff08;Transaction Serialization&#…...

Aspose.Words导出word,服务器用内存流处理,不生成磁盘文件

框架集&#xff1a;.NET8 public async Task<IActionResult> ExportPDF(long? id) {var infoawait form_Dahui_ReportDao.GetAsync(id);if (info null){return Content("没找到数据");}//读取word模板string fileTemp Path.Combine(AppContext.BaseDirect…...

攻防世界——Web题ez_curl

目录 Express PHP和Node.js的解析差异 Python代码 这道题最终得不到flag&#xff0c;用了很多师傅的代码也不成功。但还是需要学习 下载的附件&#xff1a; const express require(express);const app express();const port 3000; const flag process.env.flag;app.ge…...

力扣面试150题--螺旋矩阵

Day 20 题目描述 思路 根据题目描述&#xff0c;我们需要顺时针输出矩阵元素&#xff0c;顺时针说明有四种输出状态&#xff0c;横向从左到右和从右到左&#xff0c;纵向从上到下和从下到上&#xff0c;唯一的难点在于&#xff0c;输出完成一层后&#xff0c;如何进入内层&am…...

智能指针之设计模式2

前面介绍了工厂模式控制了智能指针和资源对象的创建过程&#xff0c;现在介绍一下智能指针是如何利用代理模式来实现“类指针&#xff08;like-pointer&#xff09;”的功能&#xff0c;并控制资源对象的销毁过程的。 2、代理模式 代理模式是为其它对象提供一种代理以控制对这…...

【Redis】redis持久化

Redis 持久化 Redis&#xff1a;非关系型的内存数据库 持久化&#xff1a;将数据永久写入磁盘&#xff08;内存→磁盘&#xff09; Redis 默认开启了持久化&#xff0c;默认模式为 RDB 为什么需要持久化&#xff1f; Redis 是内存数据库&#xff0c;宕机或关机后数据会丢失。…...

AtCoder Beginner Contest 401 E题 题解

E - Reachable Sethttp://E - Reachable Set 题意概述 &#xff1a; 给定一个无向图&#xff0c; 对于每个 &#xff0c;解决以下问题&#xff1a; -选择最少的一些顶点&#xff0c;使得删除这些顶点及其关联的所有边后 点1只能到达以内的所有点 牵制芝士 &#xff1a;头文…...

Kubernetes控制平面组件:API Server Webhook 授权机制 详解

云原生学习路线导航页&#xff08;持续更新中&#xff09; kubernetes学习系列快捷链接 Kubernetes架构原则和对象设计&#xff08;一&#xff09;Kubernetes架构原则和对象设计&#xff08;二&#xff09;Kubernetes架构原则和对象设计&#xff08;三&#xff09;Kubernetes控…...

【CodeMirror】系列(二)官网示例(六)自动补全、边栏

一、自动补全 codemirror/autocomplete 包提供了在编辑器中显示输入建议的功能。这个示例展示了如何启用该功能以及如何编写自己的补全来源。 自动补全是通过在编辑器的配置项中加入 autocompletion 扩展实现的。有些语言包支持内置的自动补全功能&#xff0c;比如HTML包。 默…...

CSS 表格样式学习笔记

CSS 提供了强大的工具来美化和定制 HTML 表格的外观。通过合理使用 CSS 属性&#xff0c;可以使表格更加美观、易读且功能强大。以下是对 CSS 表格样式的详细学习笔记。 一、表格边框 1. 单独边框 默认情况下&#xff0c;表格的 <table>、<th> 和 <td> 元…...

简单记录一下Android四大组件

1、Android Layout 1.1、LinearLayout 线性布局&#xff0c;子控件按照水平或垂直的方向依次排列&#xff0c;排列方向通过属性android:orientation控制&#xff0c;horizontal为水平排列&#xff0c;vertical为垂直排列。对于同一水平线上的控件&#xff0c;可以调整它的lay…...

在线地图支持天地图和腾讯地图,仪表板和数据大屏支持发布功能,DataEase开源BI工具v2.10.7 LTS版本发布

2025年4月11日&#xff0c;人人可用的开源BI工具DataEase正式发布v2.10.7 LTS版本。 这一版本的功能变动包括&#xff1a;数据源方面&#xff0c;Oracle数据源支持获取和查询物化视图&#xff1b;图表方面&#xff0c;在线地图支持天地图、腾讯地图&#xff1b;新增子弹图&…...

【图像处理基石】什么是通透感?

一、画面的通透感定义 画面的通透感指图像在色彩鲜明度、空间层次感、物体轮廓清晰度三方面的综合表现&#xff0c;具体表现为&#xff1a; 色彩鲜明&#xff1a;颜色纯净且饱和度适中&#xff0c;无灰暗或浑浊感&#xff1b;层次分明&#xff1a;明暗过渡自然&#xff0c;光…...

猫咪如厕检测与分类识别系统系列【六】分类模型训练+混合检测分类+未知目标自动更新

前情提要 家里养了三只猫咪&#xff0c;其中一只布偶猫经常出入厕所。但因为平时忙于学业&#xff0c;没法时刻关注牠的行为。我知道猫咪的如厕频率和时长与健康状况密切相关&#xff0c;频繁如厕可能是泌尿问题&#xff0c;停留过久也可能是便秘或不适。为了更科学地了解牠的如…...

NoSQL入门指南:Redis与MongoDB的Java实战

一、为什么需要NoSQL&#xff1f; 在传统SQL数据库中&#xff0c;数据必须严格遵循预定义的表结构&#xff0c;就像把所有物品整齐摆放在固定尺寸的货架上。而NoSQL&#xff08;Not Only SQL&#xff09;数据库则像一个灵活的储物间&#xff0c;允许存储各种类型的数据&#x…...

游戏引擎学习第223天

回顾 今天我们正在进行过场动画序列的制作&#xff0c;因此我想深入探讨这个部分。昨天&#xff0c;我们暂时停止了过场动画的制作&#xff0c;距离最终结局还有一些内容没有完成。今天的目标是继续完成这些内容。 我们已经制作了一个过场动画的系列&#xff0c;并把它们集中…...

【redis进阶二】分布式系统之主从复制结构(1)

目录 一 为什么要有分布式系统&#xff1f; 二 分布式系统涉及到的非常关键的问题&#xff1a;单点问题 三 学习部署主从结构的redis (1)创建一个目录 (2)进入目录拷贝两份原有redis (3)使用vim修改几个选项 (4)启动两个从节点服务器 (5)建立复制&#xff0c;要想配…...

(自用)若依生成左树右表

第一步&#xff1a; 在数据库创建树表和单表&#xff1a; SQL命令&#xff1a; 商品表 CREATE TABLE products (product_id INT AUTO_INCREMENT PRIMARY KEY,product_name VARCHAR(255) , price DECIMAL(10, 2) , stock INT NOT NULL, category_id INT NOT NULL); 商品分类…...

VectorBT量化入门系列:第六章 VectorBT实战案例:机器学习预测策略

VectorBT量化入门系列&#xff1a;第六章 VectorBT实战案例&#xff1a;机器学习预测策略 本教程专为中高级开发者设计&#xff0c;系统讲解VectorBT技术在量化交易中的应用。通过结合Tushare数据源和TA-Lib技术指标&#xff0c;深度探索策略开发、回测优化与风险评估的核心方法…...

5G网络下客户端数据业务掉线频繁

MCPTT&#xff08;Mission Critical Push-to-Talk&#xff09;客户端的日志&#xff0c;和界面在待机状态下&#xff08;即没有做通话等业务操作&#xff09;&#xff0c;会频繁提示“离线”。 主要先看有没有丢网&#xff0c;UL BLER有没有问题。确认没有问题。看到业务信道释…...

CPU(中央处理器)

一、CPU的定义与核心作用 CPU 是计算机的核心部件&#xff0c;负责 解释并执行指令、协调各硬件资源 以及 完成数据处理&#xff0c;其性能直接影响计算机的整体效率。 核心功能&#xff1a; 从内存中读取指令并译码。执行算术逻辑运算。控制数据在寄存器、内存和I/O设备间的…...

Java从入门到“放弃”(精通)之旅——程序逻辑控制④

Java从入门到“放弃”&#xff08;精通&#xff09;之旅&#x1f680;&#xff1a;程序逻辑的完美理解 一、开篇&#xff1a;程序员的"人生选择" 曾经的我&#xff0c;生活就像一段顺序执行的代码&#xff1a; System.out.println("早上8:00起床"); Syste…...

[Dify] 基于明道云实现金融业务中的Confirmation生成功能

在金融业务的日常流程中,交易记录的处理不仅涉及数据录入、流程审批,更重要的是其最终输出形式——交易确认函(Confirmation)。本文将介绍如何通过明道云的打印模板功能,快速、准确地生成符合业务需求的交易Confirmation,提升工作效率与合规性。 为什么需要Confirmation?…...

Qt安卓设备上怎么安装两个不同的Qt应用?

在安卓设备上安装两个不同的Qt应用时&#xff0c;需要确保这两个应用在安卓系统中被视为独立的应用程序。以下是详细的步骤和注意事项&#xff0c;帮助你实现这一目标&#xff1a; 一、修改应用的包名 安卓系统通过应用的包名&#xff08;package属性&#xff09;来区分不同的…...

Prompt工程提示词(1-6章)

White graces&#xff1a;个人主页 &#x1f439;今日诗词:怅望千秋一洒泪&#xff0c;萧条异代不同时&#x1f439; ⛳️点赞 ☀️收藏⭐️关注&#x1f4ac;卑微小博主&#x1f64f; ⛳️点赞 ☀️收藏⭐️关注&#x1f4ac;卑微小博主&#x1f64f; 目录 &#x1f680; 第…...

0基础 | 硬件滤波 C、RC、LC、π型

一、滤波概念 &#xff08;一&#xff09;滤波定义 滤波是将信号中特定波段频率滤除的操作&#xff0c;是抑制和防止干扰的重要措施。通过滤波器实现对特定频率成分的筛选&#xff0c;确保目标信号的纯净度&#xff0c;提升系统稳定性。 &#xff08;二&#xff09;滤波器分…...

C++ 编程指南34 - C++ 中 ABI 不兼容的典型情形

一:概述 ABI(Application Binary Interface)是二进制层面的接口规范。如果一个库的 ABI 发生了变化,那么基于旧 ABI 编译的代码可能在运行时与新库不兼容(即使接口名字都一样也不行)。那么在C++中编程中,哪些情形会导致ABI不兼容呢?下面逐一列举一下。 二:C++ 中 ABI…...

【动态规划】深入动态规划:背包问题

文章目录 前言01背包例题一、01背包二、分割等和子集三、目标和四、最后一块石头的重量|| 完全背包例题一、完全背包二、 零钱兑换三、零钱兑换||四、完全平方数 前言 什么是背包问题&#xff0c;怎么解决算法中的背包问题呢&#xff1f; 背包问题 (Knapsack problem) 是⼀种组…...

NVIDIA AI Aerial

NVIDIA AI Aerial 适用于无线研发的 NVIDIA AI Aerial 基础模组Aerial CUDA 加速 RANAerial Omniverse 数字孪生Aerial AI 无线电框架 用例构建商业 5G 网络加速 5G生成式 AI 和 5G 数据中心 加速 6G 研究基于云的工具 优势100% 软件定义通过部署在数字孪生中进行测试6G 标准化…...

计算机视觉6——相机基础

一、数字相机基本工作原理 &#xff08;一&#xff09;像素概念 数字相机生成二维图像&#xff0c;图像最小单元是像素。 每个像素对应三维世界中某个特定方向&#xff0c;像素值衡量某一时刻来自该方向的光照强度/颜色 &#xff0c;即相机度量每个像素的光照情况并保存到对…...

入门到精通,C语言十大经典程序

以下是十个经典的C语言程序示例&#xff0c;这些程序涵盖了从基础到稍复杂的应用场景&#xff0c;适合初学者和有一定基础的开发者学习和参考。 1. Hello, World! 这是每个初学者学习编程时的第一个程序&#xff0c;用于验证开发环境是否正确配置。 #include <stdio.h>…...

【毕设】Python构建基于TMDB电影推荐系统

个性化电影推荐系统 这是一个基于FastAPI开发的现代化电影推荐系统&#xff0c;结合了协同过滤和深度学习技术&#xff0c;为用户提供个性化的电影推荐服务。 主要功能 &#x1f3af; 个性化电影推荐&#x1f50d; 电影搜索与浏览⭐ 电影评分系统&#x1f49d; 收藏夹功能&a…...

嵌入式常见概念的介绍

目录 一、MCU、MPU、ARM &#xff08;一&#xff09;MCU&#xff08;微控制器&#xff09; &#xff08;二&#xff09;MPU&#xff08;微处理器&#xff09; &#xff08;三&#xff09;ARM&#xff08;架构&#xff09; 二、DSP &#xff08;一&#xff09;数字信号处理…...