C++中的string类模拟实现

news/2024/4/20 12:33:55/

目录

string类的模拟实现

string类的构造函数

string类拷贝构造函数

string类析构函数

string类c_str()函数

string类中的[]运算符重载函数

string类中的赋值运算符重载

string类中获取字符串有效字符个数

string类中获取字符串存储空间大小(不包括\0)

string类reserve()函数

string类push_back()函数

string类append()函数

string类+=运算符重载函数

string类关系运算符重载

string类基础迭代器实现

string类resize()函数

string类insert()函数

string类erase()函数

string类find()函数

string类swap()函数

string类流插入运算符重载

string类流提取运算符重载

项目文件


string类的模拟实现

string类的构造函数

在设计string类的构造函数时,需要考虑到下面三个问题:

  1. 是否需要传递参数,即是否需要提供有参及无参构造函数
  2. 如何为字符串数组开辟空间,空间开辟的大小为多少
  3. 字符串数组的容量和有效数据个数之间的关系如何

针对上面三个问题,提出以下的解决方案:

  1. 在提供类构造函数时,需要为类提供两种构造函数:1. 有参构造 2. 无参构造。对于有参构造来说可以实现以类对象来构造或者以常量字符串来构造,而对于无参构造函数来说,可以不需要额外提供,只需要在以常量字符串构造的函数给一个空字符串""(空字符串不是没有内容,默认包含\0)作为缺省值即可
  2. 对于第二个问题,字符串数组开辟多大空间,对于无参构造函数来说,如果确保有缺省值可以防止出现空指针解引用的问题;而对于有参的构造函数来说,因为已经确定参数是个字符串,所以可以参数字符串的长度(不包括\0)作为初始大小以确保在开始时有足够空间
  3. 因为标准库中的capacity不计入\0占用的大小,所以本次模拟实现时capacity也不计入\0的个数,那么capacity即为字符串长度+1,但是第二个问题:因为使用的是缺省参数,所以无参就使用缺省值,而因为缺省值为1,如果在尾插时涉及到使用capacity进行扩容,那么会导致只能插入一个字符,后续字符将无法插入,所以综上capacity也需要给缺省值
//构造函数
string(const char* str = "", size_t capacity = 4):_size(strlen(str))
{_capacity = (_size == 0 ? capacity : _size);//为字符串数组开辟空间_str = new char[_capacity + 1];strcpy(_str, str);
}

string类拷贝构造函数

虽然编译器默认会提供拷贝构造函数,但是该构造函数只能完成浅拷贝,如果对字符串数组进行浅拷贝,那么会出现两个问题:

  1. 两个指针指向同一个位置,当一个指针修改数组中的内容时,另一个指针指向的数组中的内容也会改变
  2. 当调用析构函数时,因为两个指针指向同一个位置,当一个指针被析构后,另一个指针并不会不析构,此时导致程序崩溃

所以为了避免上面的问题,string类的拷贝构造函数需要自行设计从而进行深拷贝

在设计string类的拷贝构造函数需要考虑到空间开辟的问题,因为是拷贝某一个对象中的内容,所以可以考虑使用该对象的capacity,但是注意需要+1,因为capacity不包括\0的占用空间

📌

注意拷贝构造函数的初始化列表为_size初始化

//拷贝构造函数
string(const string& s):_size(s._size)//注意拷贝构造也需要初始化列表,_capacity(s._capacity)
{_str = new char[s._capacity + 1];strcpy(_str, s._str);
}

string类析构函数

对于析构函数来说,只需要释放开辟的空间即可,因为使用的是new[]进行的空间开辟,所以需要使用delete[]进行空间释放

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

string类c_str()函数

因为C形式字符串是内置类型,所以使用cout打印时编译器会自动识别类型,相当于使用%s进行打印字符串,只需要返回字符串数组首字符地址即可

//返回C形式字符串
const char* c_str()
{return _str;//返回字符数组的首元素地址
}

string类中的[]运算符重载函数

对于下标引用操作符来说,只需要返回当前下标对应位置的字符即可,但是需要注意的是要判断传入的位置是否合法

📌

为了减少返回值拷贝到临时变量的消耗,推荐使用引用返回

const版本:

//重载[]
char& operator[](size_t pos)
{//确保pos不会越界assert(pos <= _size);return _str[pos];
}

const版本:

//重载[]——const
const char& operator[](size_t pos) const
{assert(pos <= _size);return _str[pos];
}

string类中的赋值运算符重载

虽然编译器默认会生成赋值运算符重载函数,但是对于内置类型和自定义类型都是浅拷贝,所以需要自行重载赋值运算符。

对于赋值运算符重载函数来说,需要考虑到空间是否足够的问题,有下面三种情况需要考虑:

  1. 源字符串和目标字符串所在空间大小基本一致

  1. 源字符串远大于目标字符串所在空间大小

  1. 源字符串远小于目标字符串所在空间大小

对于上面的三种情况来说,可以考虑的解决方式为:

  1. 对于第一种情况,使用大的一方的空间
  2. 对于第二种情况,扩容目标字符串空间,再将源字符串空间的内容赋值到目标字符串空间
  3. 对于第三种情况,释放目标字符串空间,再为目标字符串重新开辟空间,将源字符串空间的内容拷贝到目标字符串空间

但是,上面的解决方式过于复杂,所以考虑下面的思路:

不论是哪一种情况,先以目标字符串空间为基础开辟新的空间,再将源字符串中的内容直接拷贝到新的空间,这一步可以确保如果空间开辟失败不会影响源字符串中的内容,释放源字符串的空间,接着使源字符串的指针指向新的空间,

📌

注意处理自己给自己赋值的情况

//重载赋值运算符
string& operator=(const string& s)
{if (this != &s){char* tmp = new char[s._capacity + 1];//此处new失败将会抛异常strcpy(tmp, s._str);delete[] _str;_str = tmp;_size = s._size;_capacity = s._capacity;}//最后不要忘记修改目标字符串的_size和_capacityreturn *this;
}

string类中获取字符串有效字符个数

对于_size来说,在函数外是不可以被修改的,所以返回const类型的变量,为了非const对象和const对象都可以调用该函数,用const修饰this指针

//返回字符串的有效字符个数
const size_t size() const
{return _size;
}

string类中获取字符串存储空间大小(不包括\0

对于_capacity来说,在函数外是不可以被修改的,所以返回const类型的变量,为了非const对象和const对象都可以调用该函数,用const修饰this指针

//返回字符串的容量大小
const size_t capacity() const
{return _capacity;
}

string类reserve()函数

对于扩容函数来说,只需要处理好原始空间的释放以及原始空间的内容不丢失即可

📌

注意,当扩容的大小小于原始大小时不能进行缩容

//扩容reserve()函数
void reserve(size_t capacity)
{//当扩容的容量小于_capacity时不进行缩容if (capacity <= _capacity){return;}char* tmp = new char[capacity + 1];//new失败抛异常strcpy(tmp, _str);delete[] _str;_str = tmp;_capacity = capacity;
}

string类push_back()函数

对于push_back()函数来说,需要考虑的问题是插入数据时是否需要扩容

解决思路也很简单,因为直插入一个字符串,所以只需要将原来的容量+1即可

何时需要进行扩容

//字符串末尾追加字符
void push_back(char c)
{//判断是否需要扩容//因为_capacity是不包括\0的大小,如果_size+1>_capacity,那么意味着当前_size指向的位置即为\0的位置if (_size + 1 > _capacity){reserve(_size + 1);//扩容一个字符即可,如果不想频繁扩容可以扩容_capacity*2}_str[_size++] = c;//在_size的位置插入字符c_str[_size] = '\0';
}

string类append()函数

对于append()函数的分析与push_back()函数相同,此处不再分析

//字符串末尾追加字符串
void append(const char* s)
{size_t len = strlen(s);//需要判断是否需要扩容if (_size + len > _capacity){reserve(_size + len);}strcpy(_str + _size, s);_size += len;
}

string类+=运算符重载函数

//重载+=_字符串
string& operator+=(const char* s)
{//复用append()append(s);return *this;
}//重载+=_字符
string& operator+=(const char c)
{//复用push_back()push_back(c);return *this;
}

string类关系运算符重载

底层直接调用strcmp()函数

//模拟实现strcmp
const int strcmp(const char* s1, const char* s2) const
{int i = 0;while (*s1 == *s2){//如果两个都走到了\0的位置,那么说明二者相等if (!(*s1)){return 0;}s1++;s2++;}//走出循环后,说明当前两个指针指向的位置不同if (*s1 > *s2){return 1;}else{return -1;}
}//重载关系运算符
bool operator>(const string& s) const
{return strcmp(_str, s._str) > 0;
}bool operator==(const string& s) const
{return strcmp(_str, s._str) == 0;
}bool operator>=(const string& s) const
{//return *this > s || *this == s;return *this > s || s == *this;
}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);
}

string类基础迭代器实现

当前实现的迭代器是模拟指针的方式进行实现

//重载begin()_非const
iterator begin()
{return _str;
}//重载end()_非const
iterator end()
{return _str + _size;//_size总是指向有效数据的下一个位置,一般为\0的位置
}//重载begin()_const
const_iterator begin() const
{return _str;
}//重载end()_const
const_iterator end() const
{return _str + _size;
}

string类resize()函数

在模拟实现resize()函数时,需要注意resize()不同于reserve()函数

reserve()函数只是对原有的字符串空间进行扩容,并且如果扩容的大小小于原始大小,那么将不执行扩容,而resize()函数需要分为下面三种情况:

  1. 当扩容的大小小于size时,将对原始字符串进行删除,直到只剩下给定大小个数的字符构成的字符串

  1. 当扩容的大小介于sizecapacity之间时,在原始字符串末尾进行初始化

  1. 当扩容大小大于capacity时,则需要对原始空间先进行扩容,再对扩充的空间进行初始化

//resize()函数
void resize(size_t size, char c = '\0')
{//当size小于等于_size时对于第一种情况if (size <= _size){_str[size] = '\0';_size = size;}else{//需要扩容时对应第三种情况if (size > _capacity){reserve(size);}size_t end = _size;while (end < size){_str[end++] = c;}_str[size] = '\0';}
}

string类insert()函数

对于insert()函数来说有插入字符和插入字符串两种类型

首先对于插入字符来说,基本思路如下:

//insert()函数
void insert(size_t pos, const char c)
{assert(pos <= _size);//判断是否需要扩容if (_size + 1 > _capacity){reserve(_capacity * 2);}//挪动pos位置之后的数据size_t end = _size;while (end + 1 >= pos){_str[end + 1] = _str[end];end--;}_str[pos] = c;_size++;
}

但是需要注意一点,上面的方法在头部插入字符时会出现死循环以及越界访问(当前posend均为size_t类型),因为当end走到-1的位置时本应是最后一次循环,但是由于其为size_t类型,导致-1表示整型的最大值,从而造成end+1依旧大于pos,并且_str[end + 1] = _str[end]此时越界访问

可以考虑下面的修改方式

//insert()函数
void insert(size_t pos, const char c)
{assert(pos <= _size);//判断是否需要扩容if (_size + 1 > _capacity){reserve(_capacity * 2);}//挪动pos位置之后的数据size_t end = _size;//当end为npos时跳出循环while (end + 1 >= pos && end != npos){_str[end + 1] = _str[end];end--;}//单独在起始位置插入数据if (end == npos){_str[0] = c;}_str[pos] = c;_size++;
}

也可以将上方代码优化成下面的代码,思路如下

//insert()函数_插入一个字符
void insert(size_t pos, const char c)
{assert(pos <= _size);//判断是否需要扩容if (_size + 1 > _capacity){reserve(_capacity * 2);}//挪动pos位置之后的数据size_t end = _size + 1;while (end > pos){_str[end] = _str[end - 1];end--;}_str[pos] = c;_size++;
}

插入字符串的思路也是一样,只是挪动数据的个数以及插入字符串的方式改变

//insert()函数_插入一个字符串
void insert(size_t pos, const char* s)
{assert(pos <= _size);//判断是否需要扩容size_t len = strlen(s);if (_size + len > _capacity){reserve(_size + len);}//挪动pos位置之后的数据size_t end = _size + len;while (end > pos + len - 1){_str[end] = _str[end - len];end--;}strncpy(_str + pos, s, len);_size += len;
}

string类erase()函数

erase()函数只是删除字符或者字符串的功能,注意需要分情况讨论:

  1. 当需要删除字符的个数小于字符串字符个数时,直接向前覆盖,再最后一个位置加入\0改变_size即可
  2. 当需要删除的字符个数大于字符串字符个数时,全部删除,此时只需要在pos位置加入\0改变_size即可
//erase()函数
void erase(size_t pos, size_t len = npos)
{assert(pos <= _size);//对应第二种情况if (len == npos || len + pos >= _size){_str[pos] = '\0';_size = pos;return;}//对应第一种情况//size_t start = pos + len;//while (start <= _size)//{//    _str[pos++] = _str[start++];//}strcpy(_str + pos, _str + pos + len);_size -= len;
}

string类find()函数

find()函数也有两种类型,一种是找字符,另一种是找字符串

首先是找字符,直接挨个比较即可

//find()函数_找字符
size_t find(const 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;
}

接着是找字符串,调用strstr()函数即可

//find()函数_找字符串
size_t find(const char* s, size_t pos = 0)
{char* ptr = strstr(_str, s);if (ptr == NULL){return npos;}return ptr - _str;//两个指针相减返回差值
}

string类swap()函数

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

string类流插入运算符重载

//流插入运算符重载
ostream& operator<<(ostream& cout, string& s)
{for (auto ch : s){cout << ch;}return cout;
}

string类流提取运算符重载

//流提取运算符重载
istream& operator>>(istream& cin, string& s)
{s.clear();char ch = cin.get();char _buf[128] = {0};size_t i = 0;while (ch != ' ' && ch != '\n'){_buf[i++] = ch;if (i == 127){s += _buf;i = 0;}ch = cin.get();}if (i != 0){_buf[i] = '\0';s += _buf;}return cin;
}

项目文件

//头文件
#pragma once#include <iostream>
#include <cassert>
using namespace std;namespace sim_string
{class string{private:char* _str;//指向存储字符串的空间size_t _capacity;size_t _size;public:const static size_t npos = - 1;//构造函数string(const char* str = "", size_t capacity = 4):_size(strlen(str)){_capacity = (_size == 0 ? capacity : _size);//为字符串数组开辟空间_str = new char[_capacity + 1];strcpy(_str, str);}//拷贝构造函数string(const string& s):_size(s._size)//注意拷贝构造也需要初始化列表, _capacity(s._capacity){_str = new char[s._capacity + 1];strcpy(_str, s._str);}~string(){delete[] _str;_size = _capacity = 0;}//重载[]——非constchar& operator[](size_t pos){//确保pos不会越界assert(pos <= _size);return _str[pos];}//重载[]——constconst char& operator[](size_t pos) const{assert(pos <= _size);return _str[pos];}//重载赋值运算符string& operator=(const string& s){if (this != &s){char* tmp = new char[s._capacity + 1];//此处new失败将会抛异常strcpy(tmp, s._str);delete[] _str;_str = tmp;_size = s._size;_capacity = s._capacity;}//最后不要忘记修改目标字符串的_size和_capacityreturn *this;}//返回C形式字符串const char* c_str(){return _str;//返回字符数组的首元素地址}//返回字符串的有效字符个数const size_t size() const{return _size;}//返回字符串的容量大小const size_t capacity() const{return _capacity;}//扩容reserve()函数void reserve(size_t capacity){//当扩容的容量小于_capacity时不进行缩容if (capacity <= _capacity){return;}char* tmp = new char[capacity + 1];//new失败抛异常strcpy(tmp, _str);delete[] _str;_str = tmp;_capacity = capacity;}//字符串末尾追加字符void push_back(const char c){//判断是否需要扩容//因为_capacity是不包括\0的大小,如果_size+1>_capacity,那么意味着当前_size指向的位置即为\0的位置if (_size + 1 > _capacity){reserve(_size + 1);//扩容一个字符即可,如果不想频繁扩容可以扩容_capacity*2}_str[_size++] = c;//在_size的位置插入字符c_str[_size] = '\0';}//字符串末尾追加字符串void append(const char* s){size_t len = strlen(s);//需要判断是否需要扩容if (_size + len > _capacity){reserve(_size + len);}strcpy(_str + _size, s);_size += len;}//重载+=_字符串string& operator+=(const char* s){//复用append()append(s);return *this;}//重载+=_字符string& operator+=(const char c){//复用push_back()push_back(c);return *this;}//模拟实现strcmpconst int strcmp(const char* s1, const char* s2) const{int i = 0;while (*s1 == *s2){//如果两个都走到了\0的位置,那么说明二者相等if (!(*s1)){return 0;}s1++;s2++;}//走出循环后,说明当前两个指针指向的位置不同if (*s1 > *s2){return 1;}else{return -1;}}//重载关系运算符bool operator>(const string& s) const{return strcmp(_str, s._str) > 0;}bool operator==(const string& s) const{return strcmp(_str, s._str) == 0;}bool operator>=(const string& s) const{//return *this > s || *this == s;return *this > s || s == *this;}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);}typedef char* iterator;typedef const char* const_iterator;//重载begin()_非constiterator begin(){return _str;}//重载end()_非constiterator end(){return _str + _size;//_size总是指向有效数据的下一个位置,一般为\0的位置}//重载begin()_constconst_iterator begin() const{return _str;}//重载end()_constconst_iterator end() const{return _str + _size;}//insert()函数_插入一个字符void insert(size_t pos, const char c){assert(pos <= _size);//判断是否需要扩容if (_size + 1 > _capacity){reserve(_capacity * 2);}//挪动pos位置之后的数据size_t end = _size + 1;while (end > pos){_str[end] = _str[end - 1];end--;}_str[pos] = c;_size++;}//insert()函数_插入一个字符串void insert(size_t pos, const char* s){assert(pos <= _size);//判断是否需要扩容size_t len = strlen(s);if (_size + len > _capacity){reserve(_size + len);}//挪动pos位置之后的数据size_t end = _size + len;while (end > pos + len - 1){_str[end] = _str[end - len];end--;}strncpy(_str + pos, s, len);_size += len;}//void insert(size_t pos, const char c)//{//    assert(pos <= _size);//    //判断是否需要扩容//    if (_size + 1 > _capacity)//    {//        reserve(_capacity * 2);//    }//    //挪动pos位置之后的数据//    size_t end = _size;//    while (end + 1 >= pos && end != npos)//    {//        _str[end + 1] = _str[end];//        end--;//    }//    if (end == npos)//    {//        _str[0] = c;//    }//    _str[pos] = c;//    _size++;//}//resize()函数void resize(size_t size, char c = '\0'){//当size小于等于_size时对于第一种情况if (size <= _size){_str[size] = '\0';_size = size;}else{//需要扩容时对应第三种情况if (size > _capacity){reserve(size);}size_t end = _size;while (end < size){_str[end++] = c;}_str[size] = '\0';}}//erase()函数void erase(size_t pos, size_t len = npos){assert(pos <= _size);//对应第二种情况if (len == npos || len + pos >= _size){_str[pos] = '\0';_size = pos;return;}//对应第一种情况//size_t start = pos + len;//while (start <= _size)//{//    _str[pos++] = _str[start++];//}strcpy(_str + pos, _str + pos + len);_size -= len;}//find()函数_找字符size_t find(const 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;}//find()函数_找字符串size_t find(const char* s, size_t pos = 0){char* ptr = strstr(_str, s);if (ptr == NULL){return npos;}return ptr - _str;//两个指针相减返回差值}void swap(string& s){std::swap(_str, s._str);std::swap(_size, s._size);std::swap(_capacity, s._capacity);}//clear()函数void clear(){_str[0] = '\0';_size = 0;}};//流插入运算符重载ostream& operator<<(ostream& cout, string& s){for (auto ch : s){cout << ch;}return cout;}//流提取运算符重载istream& operator>>(istream& cin, string& s){s.clear();char ch = cin.get();char _buf[128] = {0};size_t i = 0;while (ch != ' ' && ch != '\n'){_buf[i++] = ch;if (i == 127){s += _buf;i = 0;}ch = cin.get();}if (i != 0){_buf[i] = '\0';s += _buf;}return cin;}
}//测试文件
#define _CRT_SECURE_NO_WARNINGS 1#include "simulat_string.h"void test_structure()
{sim_string::string s1;sim_string::string s2("hello world");cout << s1.c_str() << endl;cout << s2.c_str() << endl;
}void test_copyStructure()
{sim_string::string s1("hello world");sim_string::string s2(s1);cout << s2.c_str() << endl;//s1[6]++;//cout << s1.c_str() << endl;//cout << s2.c_str() << endl;
}void test_operator_pos()
{sim_string::string s1("hello world");for (size_t i = 0; i < s1.size(); i++){cout << s1[i] << " ";}cout << endl;//可以通过下标进行修改for (size_t i = 0; i < s1.size(); i++){s1[i]++;cout << s1[i] << " ";}cout << endl;const sim_string::string s2("hello Linux");for (size_t i = 0; i < s2.size(); i++){//s2[i]++;不可以修改cout << s2[i] << " ";}
}void test_operator_give()
{//当目标对象大于源对象sim_string::string s1("xxxx");sim_string::string s2 = "hello world";//注意此处不是赋值运算符重载,而是没有explicit修饰的构造函数s1 = s2;cout << s1.c_str() << endl;//当目标对象小于源对象sim_string::string s3("xxxxxxxxxxxxxxxxxxxxx");sim_string::string s4 = "hello";s3 = s4;cout << s3.c_str() << endl;//当目标对象等于源对象sim_string::string s5 = "xxxxxx";sim_string::string s6 = "------";s5 = s6;cout << s5.c_str() << endl;
}void test_push_back()
{sim_string::string s1 = "hello world";s1.push_back('d');cout << s1.c_str() << endl;//sim_string::string s2;//s2.push_back('a');//s2.push_back('b');//s2.push_back('c');//cout << s2.c_str() << endl;
}void test_append()
{sim_string::string s1 = "hello world";s1.append(" Linux");cout << s1.c_str() << endl;
}void test_operator_addEqual()
{sim_string::string s1 = "hello world";s1 += 'c';cout << s1.c_str() << endl;s1 += " Linux";cout << s1.c_str() << endl;
}void test_relational_operators()
{sim_string::string s1 = "abcde";sim_string::string s2 = "abcdef";cout << (s1 < s2) << endl;sim_string::string s3 = "abef";sim_string::string s4 = "abcd";cout << (s3 > s4) << endl;sim_string::string s5 = "abc";sim_string::string s6 = "abc";cout << (s5 != s6) << endl;
}void test(const sim_string::string& s1)
{//sim_string::string::const_iterator ptr1 = s1.begin();//while (ptr1 != s2.end())//{//    cout << *ptr1 << " ";//    ptr1++;//}//const版本可以使用范围for,但是不可以修改值内容for (auto ch : s1){cout << ch << " ";}
}void test_iterator()
{//sim_string::string s1 = "hello world";//sim_string::string::iterator ptr = s1.begin();//while (ptr != s1.end())//{//    cout << *ptr << " ";//    ptr++;//}//非const版本可以使用范围for并且修改字符串数组的内容//for (auto ch : s1)//{//    ch++;//    cout << ch << " ";//}const sim_string::string s2 = "hello world";test(s2);
}void test_insert()
{//sim_string::string s1 = "hello world";//s1.insert(0, 'd');//cout << s1.c_str() << endl;sim_string::string s2 = "hello";s2.insert(2, "world");cout << s2.c_str() << endl;s2.insert(0, "Linux");cout << s2.c_str() << endl;
}void test_resize()
{sim_string::string s2 = "hello world";s2.resize(20, 'd');cout << s2.c_str() << endl;s2.resize(15, 'c');cout << s2.c_str() << endl;s2.resize(5);cout << s2.c_str() << endl;
}void test_ostream_operator()
{sim_string::string s1 = "hello world";cout << s1;
}void test_istream_operator()
{sim_string::string s2;cin >> s2;cout << s2;sim_string::string s3("hello world");cin >> s3;cout << s3;
}void test_erase()
{sim_string::string s1 = "hello world";s1.erase(5, 5);cout << s1.c_str() << endl;s1.erase(1);cout << s1.c_str() << endl;
}void test_find()
{sim_string::string s1 = "hello world";size_t pos = s1.find('g');//cout << pos << endl;if (pos != sim_string::string::npos){cout << pos << endl;}else{cout << "无" << endl;}pos = s1.find("wol");//cout << pos << endl;if (pos != sim_string::string::npos){cout << pos << endl;}else{cout << "无" << endl;}
}void test_swap()
{sim_string::string s1 = "hello world";sim_string::string s2 = "hello Linux";cout << s1.c_str() << endl;cout << s2.c_str() << endl;s1.swap(s2);cout << s1.c_str() << endl;cout << s2.c_str() << endl;
}int main()
{//test_structure();//test_copyStructure();//test_operator_pos();//test_operator_give();//test_push_back();//test_append();//test_operator_addEqual();//test_relational_operators();//test_iterator();//test_insert();//test_resize();//test_erase();//test_find();//test_swap();//test_ostream_operator();test_istream_operator();return 0;
}


http://www.ppmy.cn/news/1405558.html

相关文章

人工智能(pytorch)搭建模型25-基于pytorch搭建FPN特征金字塔网络的应用场景,模型结构介绍

大家好&#xff0c;我是微学AI&#xff0c;今天给大家介绍一下人工智能(pytorch)搭建模型25-基于pytorch搭建FPN特征金字塔网络的应用场景&#xff0c;模型结构介绍。特征金字塔网络&#xff08;FPN&#xff09;是一种深度学习模型结构&#xff0c;主要应用于目标检测任务中&am…

scRNA+bulk+MR:动脉粥样硬化五个GEO数据集+GWAS,工作量十分到位

今天给大家分享一篇JCR一区&#xff0c;单细胞bulkMR的文章&#xff1a;An integrative analysis of single-cell and bulk transcriptome and bidirectional mendelian randomization analysis identified C1Q as a novel stimulated risk gene for Atherosclerosis 标题&…

Transformer的代码实现 day03(Positional Encoding)

Positional Encoding的理论部分 注意力机制是不含有位置信息&#xff0c;这也就表明&#xff1a;“我爱你”&#xff0c;“你爱我”这两者没有区别&#xff0c;而在现实世界中&#xff0c;这两者有区别。所以位置编码是在进行注意力计算之前&#xff0c;给输入加上一个位置信息…

FFMPEG对于处理rtp流出现马赛克问题处理

背景 本项目是基于FFMPEG 3.3版本进行的开发。 近期5G发展迅速&#xff0c;无线集群中的带宽不再是瓶颈&#xff0c;对于视频质量的要求也越来越高&#xff0c;现在使用720P、1080P、2K、4K进行视频通话成为了日常。 问题描述 本项目之前对于CIF和VGA格式的视频进行录…

论文笔记:基于多粒度信息融合的社交媒体多模态假新闻检测

整理了ICMR2023 Multi-modal Fake News Detection on Social Media via Multi-grained Information Fusion&#xff09;论文的阅读笔记 背景模型实验 背景 在假新闻检测领域&#xff0c;目前的方法主要集中在文本和视觉特征的集成上&#xff0c;但不能有效地利用细粒度和粗粒度…

2024年github之node排行榜top50

如果有帮助到您还请动动手帮忙点赞&#xff0c;关注&#xff0c;评论转发&#xff0c;感谢啦&#xff01;&#x1f495;&#x1f495;&#x1f495;&#x1f618;&#x1f618;&#x1f618; 本文由Butterfly一键发布工具发布 2024年github之node排行榜top50 语言star项目名称…

spring-boot集成websocket

引入Maven依赖包 <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-websocket</artifactId><version>跟随spingboot版本</version> </dependency>后端代码 /*** 开启WebSocket支持*…

Gtest 和VLD一起使用报内存泄漏

这个问题已知&#xff1a; Memory Leaks on Windows using VLD Issue #2255 google/googletest GitHub 那么解决办法是什么呢&#xff1f;一个折中的办法是这样的。 TEST() {VLDMarkAllLeaksAsReported();{/// 待测代码std::string s ...;}VLDReportLeaks(); }class MyT…

一、点击视频下载(通过视频url实现);二、点击下载视频按钮,视频以压缩包形式下载(但未实现压缩视频)

一、点击视频下载&#xff08;通过视频url实现&#xff09; <div class"video-list" v-for"(item,index) in videoList" :key"index"><span class"video-title" >{{item.title}}</span><span class"video-…

建造者模式:构建复杂对象的优雅之道

在软件开发中&#xff0c;我们经常需要构建复杂的对象&#xff0c;这些对象可能由多个组成部分构成&#xff0c;并且具有多种不同的表示形式。如果直接创建这些复杂对象&#xff0c;可能会导致代码难以维护和扩展。这时候&#xff0c;建造者模式&#xff08;Builder Pattern&am…

鸿蒙开发(七)-UIAbility启动模式

鸿蒙开发(七)-启动模式 根据代码中定义,UIAbility的启动模式有以下几种&#xff1a; "launchType": {"description": "Indicates the boot mode of ability.","type": "string","enum": ["standard",…

部署项目遇到的各种问题总结

文章目录 前言一、后端问题 jar包运行出现错误宝塔面板使用jdk17二、数据库问题 版本问题三、前端问题 连不上后端总结 前言 在做完项目之后&#xff0c;为了让别人访问到自己的网站&#xff0c;就需要部署前端后端以及数据库&#xff0c;但是在部署的过程中出现了各种问题和困…

C 练习实例97 - 读磁盘 写磁盘

题目&#xff1a;从键盘输入一些字符&#xff0c;逐个把它们送到磁盘上去&#xff0c;直到输入一个‘#’为止 在桌面新建一个hello.txt文件&#xff0c;内容示例&#xff1a; 代码&#xff1a; #include <stdio.h> #include <stdlib.h>int main() {FILE *fp; //文…

会声会影2024破解版,对电脑配置有哪些要求呢?

2024破解旗舰版是一款广受欢迎的视频编辑软件&#xff0c;它的最新版本&#xff0c;会声会影2024&#xff0c;已经发布。在这篇文章中&#xff0c;我们将探讨会声会影2024的新功能以及它对视频制作人员的影响。 会声会影2024破解旗舰版带来了许多新功能&#xff0c;其中最值得…

【御控物联】JavaScript JSON结构转换(14):对象To数组——规则属性重组

文章目录 一、JSON结构转换是什么&#xff1f;二、术语解释三、案例之《JSON对象 To JSON数组》四、代码实现五、在线转换工具六、技术资料 一、JSON结构转换是什么&#xff1f; JSON结构转换指的是将一个JSON对象或JSON数组按照一定规则进行重组、筛选、映射或转换&#xff0…

Ubuntu18.04安装wireshark

安装wireshark 环境Ubuntu18.04 1.使用root用户进行安装 2.将 wireshark-dev/stable PPA 添加到系统的软件源列表中。系统就可以从该PPA获取Wireshark软件包及其更新了。 apt-add-repository ppa:wireshark-dev/stable3.确保你系统上的软件包信息是最新的&#xff0c;这样在…

外贸建站:WordPress搭建外贸独立站零基础自建站完整教程(2024)

对于做外贸来说&#xff0c;拥有自己的外贸独立网站真的非常重要。在外贸领域&#xff0c;如今各平台竞争激烈&#xff0c;规则多&#xff0c;成本高&#xff0c;价格战、政策变化快&#xff0c;还存在封店风险等等因素。在这种情况下&#xff0c;拥有外贸独立站就能很好规避上…

Leaflet使用多面(MultiPolygon)进行遥感影像掩膜报错解决之道

目录 前言 一、问题初诊断 1、山重水复 2、柳暗花明 3、庖丁解牛 4、问题定位 二、解决多面掩膜问题 1、尝试数据修复 2、实际修复 3、最终效果 三、总结 前言 之前一篇讲解遥感影像掩膜实现&#xff1a;基于SpringBoot和Leaflet的行政区划地图掩膜效果实战&#xff0…

大数据面试专题 -- kafka

1、什么是消息队列&#xff1f; 是一个用于存放数据的组件&#xff0c;用于系统之间或者是模块之间的消息传递。 2、消息队列的应用场景&#xff1f; 主要是用于模块之间的解耦合、异步处理、日志处理、流量削峰 3、什么是kafka&#xff1f; kafka是一种基于订阅发布模式的…

P8597 [蓝桥杯 2013 省 B] 翻硬币

# [蓝桥杯 2013 省 B] 翻硬币 ## 题目背景 小明正在玩一个“翻硬币”的游戏。 ## 题目描述 桌上放着排成一排的若干硬币。我们用 * 表示正面&#xff0c;用 o 表示反面&#xff08;是小写字母&#xff0c;不是零&#xff09;&#xff0c;比如可能情形是 **oo***oooo&#x…