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

string类的学习及模拟

C语言中,字符串是以'\0'结尾的一些字符的集合,为了操作方便,C标准库中提供了一些str系列的库函数, 但是这些库函数与字符串是分离开的,不太符合面向对象编程(OOP)的思想,而且底层空间需要用户自己管理,稍不留神可 能还会越界访问。手册链接:

cplusplus.com/reference/string/string/?kw=string

string类:

1. 字符串是表示字符序列的类   

2. 标准的字符串类提供了对此类对象的支持,其接口类似于标准字符容器的接口,但添加了专门用于操作 单字节字符字符串的设计特性。

3. string类是使用char(即作为它的字符类型,使用它的默认char_traits和分配器类型(关于模板的更多信 息,请参阅basic_string)。

4. string类是basic_string模板类的一个实例,它使用char来实例化basic_string模板类,并用char_traits 和allocator作为basic_string的默认参数(根于更多的模板信息请参考basic_string)。

5. 注意,这个类独立于所使用的编码来处理字节:如果用来处理多字节或变长字符(如UTF-8)的序列,这个 类的所有成员(如长度或大小)以及它的迭代器,将仍然按照字节(而不是实际编码的字符)来操作。

总结:

1. string是表示字符串的字符串类

2. 该类的接口与常规容器的接口基本相同,再添加了一些专门用来操作string的常规操作。

3. string在底层实际是:basic_string模板类的别名,typedef basic_string string;

4. 不能操作多字节或者变长字符的序列。 在使用string类时,必须包含#include头文件以及using namespace std;

下面我只简单描述我所学到的一些接口。

首先构造函数:

#define _CRT_SECURE_NO_WARNINGS 
#include<iostream>
#include<string>
using namespace std;void test_string1()
{string s1;                  // 空字符串string s2("hello world");   // hello worldstring s3(s2);              // hello worldstring s4(10, 'a');         // aaaaaaaaaastring s5(s2, 1, 3);        // ellcout << s1 << endl;cout << s2 << endl;cout << s3 << endl;cout << s4 << endl;cout << s5 << endl;
}

容器操作:

// 容器操作
void test_string4()
{string s1("hello world");string s2("hello");cout << s1.size() << endl;cout << s2.size() << endl;cout << s1.length() << endl;cout << s2.length() << endl;cout << s1.capacity() << endl;   // 容量一般比读出来的多一个用来存放“\0”cout << s2.capacity() << endl;cout << s1.empty() << endl;      // 空 打印1,非空打印0cout << s2.empty() << endl;//s1.clear(); // 清空有效字符,但是容量不变//cout << s1 << endl;//cout << s1.capacity() << endl;//s1.reserve(100);   // 开100个同类型的空间,不改变size,如果事先知道字符串大小适合使用它,避免增容带来的消耗// 当reserve的参数小于string的底层空间总大小时,reserver不会改变容量大小。//cout << s1 << endl;//cout << s1.capacity() << endl;s1.resize(16, 'k');    // 将原本size=11改为16,hello world为11个字符,剩下5个字符使用k填充cout << s1.size() << endl; // 11-》16cout << s1 << endl;        // hello worldkkkkkcout << s1.capacity() << endl;  // 15->31
}

访问string类的方法:operator[];迭代器;范围for。

// 访问string类对象
void test_string2()
{string s1("hello");// operator[]重载运算符打印字符串--访问string类对象cout << "chu:";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] += 1; // 阿斯克码值+1}// 读cout << "[ ]:";for (size_t i = 0;i < s1.size();++i){cout << s1[i] << ' ';}cout << endl;// 迭代器--访问string类对象//string::iterator it = s1.begin();auto it = s1.begin();while (it != s1.end())	// 写{*it -= 1;++it;}it = s1.begin();cout << "die:";while (it != s1.end())	// 读{cout << *it << ' ';++it;}cout << endl;// 范围for--访问string类对象--实际是迭代器,编译器会处理为迭代器cout << "for:";for (auto ch : s1){cout << ch << ' ';}cout << endl;
}
// 再看其他迭代器
int string2int(const string& str)
{int val = 0;string::const_iterator it = str.begin();  // const修饰的迭代器while (it != str.end()){val *= 10;val += (*it - '0');it++;}return val;
}
void test_string3()
{string s1("hello world");// 倒着遍历string::reverse_iterator rit = s1.rbegin();while (rit != s1.rend()){cout << *rit << ' ';++rit;}cout << endl;// char转为intstring s2("12345");cout << string2int(s2) << endl;// 迭代器分为:可修改—正向;不可修改正向;可修改—倒置;不可修改—倒置;
}

插入字符,追加字符,查找字符等操作:

// 追加字符
void test_string5()
{string s1("1");s1.push_back('3');  // 追加1个字符s1.append("4567");   // 追加一个字符串s1 += '8';           // 追加1个字符s1 += "91011";        // 追加一个字符串cout << s1 << endl;s1.insert(s1.begin(), '0'); // 在开始处插入字符0cout << s1 << endl;s1.insert(2, "2");   // 再第二个位置插入字符2cout << s1 << endl;
}void test_string6()
{string s1("hello world");cout << s1 << endl;cout << s1.c_str() << endl;s1 += '\0';s1 += " world";cout << s1 << endl;           // 打印s1对象的全部(sizecout << s1.c_str() << endl;  // 遇到换行符号就会停下来size_t f1 = s1.find(' ');    // 第二个参数默认是0;就是从前往后找的第一个空格,返回空格的下标if (f1 != string::npos)  // 没找到会返回string::npos,即整型的最大值{cout << s1.substr(f1, string::npos) << endl; // 从空格下标开始打印直到最后,第二个参数为打印的字符个数cout << s1.substr(f1 + 1, string::npos) << endl; // 从空格下标下一个位置开始打印直到最后}size_t f2 = s1.rfind(' ');  // 找到从后往前的第一个空格,返回空格的下标if (f2 != string::npos){cout << s1.substr(f2) << endl; // 从空格下标开始打印直到最后}// 网址(url)的构成:协议  域名 资源名称 三部分构成string url("https://cplusplus.com/reference/string/string/insert/");// 分离这三部分size_t l1 = url.find(':');if (l1 != string::npos){cout << url.substr(0, l1) << endl; // 协议}size_t l2 = url.find('/',l1+3);if (l2 != string::npos){cout << url.substr(l1+3, l2-(l1+3)) << endl;  // 域名}cout << url.substr(l2+1) << endl;  // 资源名称
}

从键盘上读取字符到string类中:

void test_string7()
{string s1;string s2;cin >> s1;        // 键盘输入字符到s1;遇到 空格和换行符 就结束getline(cin, s2);  // 键盘输入字符到s1;遇到 换行符 就结束,不会将换行符\n,写入string中cout << s1 << endl;cout << s2 << endl;
}
int main()
{//test_string1();//test_string2();//test_string3();//test_string4();//test_string5();//test_string6();test_string7();return 0;
}

string类的简单模拟实现:

深浅拷贝问题:

// 传统写法
namespace jess1  // 定义一个自己的命名空间,这样直接使用string字眼不会和库的冲突
{   class string{private:char* _str;  // 类成员--字符指针public://string()        // 问题2:打印的时候会报错,对空指针解引用,里面应该只有一个\0才对//	:_str(nullptr)//{}//string(char* str)  // 问题2:浅拷贝,调用析构函数的时候同一块空间被释放两次//	:_str(str)       // 问题3:str指向的常量字符串存放在代码段区域,不能被operator[]修改//{ }string(const char* str = "")         // 给一个空间--空字符--解决问题1:_str(new char[strlen(str)+1])  // 开空间多一个空间存放\0--解决问题2{strcpy(_str, str);  // 将字符拷贝到新空间--解决问题3}// 传统版拷贝构造// string s2(s1)==> s2.string(&s2,s1)string(const string& str):_str(new char[strlen(str._str)+1]){strcpy(_str, str._str);}// 现代版拷贝构造string s2(s1)==> s2.string(&s2,s1)string(const string& str):_str(nullptr)  // 初始化s2的this指针,交换给temp后调用析构函数不会崩溃,不初始化是随机值会崩溃{string temp(str._str); // 调用构造函数初始化tempswap(_str, temp._str); // s2和temp交换,swap是c++中的模板函数-直接调用}//s2=s1 ==>s2.operator(&s2,s1)// 赋值--现代写法string& operator=(string& str){if (this != &str){string temp(str);      // 调用拷贝构造,创建临时对象temp,用于后面交换swap(_str, temp._str);}return *this;}// 赋值--传统写法string& operator=(const string& str){if (this != &str) // 防止自己给自己赋值{char* temp = new char[strlen(str._str) + 1];strcpy(temp, str._str);delete[] _str;_str = temp;}return *this;}~string(){if (_str){delete[] _str;_str = nullptr;}}size_t size(){return strlen(_str);}char& operator[](size_t i){return _str[i];}// 测试static void Teststring1(){string s1("hello world!!!");string s2 = s1;cout << s1._str << endl;cout << s2._str << endl;}};
}

模拟其他功能:

#define _CRT_SECURE_NO_WARNINGS 
#include <string>
#include <iostream>
#include <assert.h>
using namespace std;
namespace jess2
{// 实现一个支持增删查改的string(此时需要有size和capacityclass string{private:char* _str;size_t _size;     // 已经有多少个有效字符size_t _capacity; // 能存多少个有效字符static size_t npos;public:friend ostream& operator<<(ostream& out, const string& s);friend istream& operator>>(istream& in, string& s);string(const char* str = ""){_size = strlen(str);_capacity = _size;_str = new char[_capacity + 1];strcpy(_str, str);}string(const string& str)  // 现代写法:_str(nullptr), _size(0), _capacity(0){string temp(str._str);swap(_str, temp._str);swap(_size, temp._size);swap(_capacity, temp._capacity);}//string(const string& str)   // 传统写法//{//	_size = strlen(str._str);//	_capacity = _size;//	_str = new char[_capacity + 1];//	strcpy(_str, str._str);//}// s2=s1string& operator=(string str)  // 现代写法--str是s1的拷贝构造的形参,把他和s2交换即可,出了函数作用域会自动调用析构函数销毁str{swap(_str, str._str);swap(_size, str._size);swap(_capacity, str._capacity);return *this;}string& operator=(const string& str)  // 传统写法{if (this != &str){_size = strlen(str._str);_capacity = _size;char* temp = new char[_capacity + 1];strcpy(temp, str._str);delete[] _str;_str = temp;}return *this;}~string(){if (_str){delete[] _str;_str = nullptr;_size = _capacity = 0;}}size_t size()const{return _size;}size_t capacity()const{return _capacity;}char& operator[](size_t i){assert(i < _size);return _str[i];}const char& operator[](size_t i) const{assert(i < _size);return _str[i];}const char* c_str(){return _str;}// 迭代器typedef char* iterator;iterator begin(){return _str;}iterator end(){return _str + _size;}void reserve(size_t n){if (n > _capacity){char* newstr = new char[n + 1];strcpy(newstr, _str);delete[] _str;_str = newstr;_capacity = n;}//_str[_size] = '\0';}void push_back(char ch){if (_size == _capacity){size_t newcapacity = _capacity == 0 ? 2 : _capacity * 2;reserve(newcapacity);}_str[_size] = ch;_size++;_str[_size] = '\0';}void append(const char* str){size_t len = strlen(str);if (_size + len > _capacity){size_t newcapacity = _size + len;reserve(newcapacity);}strcpy(_str + _size, str);_size += len;}string& operator+=(char ch){push_back(ch);return *this;}string& operator+=(const char* str){append(str);return *this;}string& insert(size_t pos, char ch){assert(pos <= _size);if (_size == _capacity){size_t newcapacity = _capacity == 0 ? 2 : _capacity * 2;reserve(newcapacity);}size_t end = _size;while (end > pos)  {_str[end + 1] = _str[end];end--;}_str[pos+1] = _str[pos];_str[pos] = ch;_size++;return *this;}string& insert(size_t pos, const char* str){assert(pos <= _size);size_t len = strlen(str);if (len == 0) return *this;  // 插入空字符串无需操作if (_size + len > _capacity)  //扩容{reserve(_size + len);}size_t end = _size;while (end >pos)  // 挪动数据{_str[end + len] = _str[end];end--;}_str[pos + len] = _str[pos];// 1for (size_t i = 0;i < len;i++)  // 插入数据{_str[pos + i] = str[i];}// 2//strncpy(_str + pos, str, len); //如果使用strcpy的话会把\0也拷贝进去_size += len;//_str[_size] = '\0';return *this;}void resize(size_t n, char ch = '\0')  // 改变size大小,大了用\0补齐,小了直接将n位置设为\0{if (n < _size){_str[n] = '\0';_size = n;}else{if (n > _capacity){reserve(n);}for (size_t i = 0;i < n - _size;i++)// for (size_t i = _size;i < n;i++){_str[i + _size] = ch;// _str[i] = ch;}_size = n;_str[_size] = '\0';}}void erase(size_t pos, size_t len = npos)  // 从pos位置开始删除npos个元素{assert(pos < _size);if (len >= _size - pos)  // 如果删的元素个数大于pos到size这段区间的值,则直接将pos位置作为字符串结束位置{_str[pos] = '\0';_size = pos;}else                 // 否则需要挪动数据,将最后删除的一个元素的下一个元素往前挪{for (size_t i = pos + len;i <= _size;i++){_str[pos] = _str[pos + len];pos++;}_size -= len;}}size_t find(char ch, size_t pos = 0)  // 默认从初始位置开始找{assert(pos < _size);size_t i = pos;while (i < _size){if (_str[i] == ch){return i;}}return npos;}size_t find(const char* str, size_t pos = 0){assert(pos < _size);char* p = strstr(_str, str);  // strstr是在_str中找子串(str,找到后返回子串的首地址,找不到返回空指针if (p == nullptr){return npos;}elsereturn p - _str;  // 指针相减=元素个数}// 清空字符串有效内容(保留容量)void clear(){_size = 0;          // 有效长度置0_str[_size] = '\0'; // 确保字符串以'\0'终止}// s1<s2bool operator<(const string& s){int ret = strcmp(_str, s._str);return ret < 0;}bool operator==(const string& s){int ret = strcmp(_str, s._str);return ret == 0;}bool operator<=(const string& s){return (*this < s || *this == s);}bool operator>=(const string& s){return !(*this < s);}bool operator>(const string& s){return !(*this <= s);}bool operator!=(const string& s){return !(*this == s);}};size_t string::npos = -1;ostream& operator<<(ostream& out, const string& str){for (size_t i = 0;i < str.size();i++){out << str[i];}return out;}istream& operator>>(istream& in, string& str){while (1){char ch;ch = in.get();if (ch == ' ' || ch == '\n'){break;}else{str += ch;}}return in;}istream& getline(istream& in, string& str){while (1){char ch;ch = in.get();if (ch == '\n'){break;}else{str += ch;}}return in;}
}int main() {jess2::string s1("hello");s1.insert(0, "qwe");jess2::string::iterator it = s1.begin();while (it != s1.end()){printf("%c ", *it);++it;}return 0; // 程序结束时析构s2→释放str,再析构s1→再次释放str→崩溃
}

http://www.dtcms.com/a/352831.html

相关文章:

  • vue拖动排序,vue使用 HTML5 的draggable拖放 API实现内容拖并排序,并更新数组数据
  • 【无标题】淘宝直播间详情数据
  • 云原生安全架构设计与零信任实践
  • 三格电子——高频一体式工业级RFID读写器的应用
  • 核心内涵解析:销采一体化 CRM 是什么?
  • 贴片式TE卡 +北京君正+Rk瑞芯微的应用
  • 亚马逊ASIN定投广告的智能化突破:从人工苦力到数据驱动的华丽转身
  • Part 1️⃣:相机几何与单视图几何-第六章:相机模型
  • Android中点击链接跳转到对应App页面的底层原理
  • Linux 云服务器日志清理自动化方法
  • 第二阶段Winfrom-8:特性和反射,加密和解密,单例模式
  • 点评项目(Redis中间件)第一部分Redis基础
  • golang 12 package 和 module
  • SegEarth-R1: Geospatial Pixel Reasoning via Large Language Model
  • week5-[字符数组]长度和
  • GraphRAG数据可视化
  • Java中JUnit知识点
  • Qt表格组件封装与远程数据库连接:从数据展示到交互体验
  • 阿里云——应用交付与负载均衡
  • 用户体验设计 | 从UX到AX:人工智能如何重构交互范式?
  • 阿里云轻量应用服务器与ECS对比
  • 4步用代码拆解数学建模中的TOPSIS评价决策! ! !
  • 树的常见算法及Java实现
  • LeetCode算法日记 - Day 23: 外观数列、数青蛙
  • 欧洲数字化养殖平台 Herdwatch 借力 Iceberg + StarRocks 提升分析能力
  • 【Matplotlib学习】驾驭画布:Matplotlib 布局方式从入门到精通完全指南
  • 【RabbitWQ】基于 Java 实现轻量级消息队列(二)
  • 医疗AI时代的生物医学Go编程:高性能计算与精准医疗的案例分析(一)
  • 【重学 MySQL】九十、Linux下MySQL的安装与卸载指南
  • 如何保证DDC楼宇自控系统与IBMS集成管理系统的稳定性和可靠性?