中国做网站的公司排名,英国零售电商网站开发,彩票网站建设一条龙,怎么创办自己的网站一学习string的原因
1.从个人理解角度上#xff1a;
在刚开始学习之前#xff0c;我只知道学习完string在以后的刷题中能提高做题效率#xff0c;在对字符串的处理string库中也许有对应的接口去实现需求#xff0c;不用自己去写函数的实现。
但在学string中改变了之前的…一学习string的原因
1.从个人理解角度上
在刚开始学习之前我只知道学习完string在以后的刷题中能提高做题效率在对字符串的处理string库中也许有对应的接口去实现需求不用自己去写函数的实现。
但在学string中改变了之前的看法不仅是要会用接口而且在理解了接口的底层原理后能更好的去理解使用它。 总结使用——明理——扩展 2从C语言上
C语言中字符串是以\0结尾的一些字符的集合为了操作方便C标准库中提供了一些str系列的库函数但是这些库函数与字符串是分离开的不太符合OOP的思想而且底层空间需要用户自己管理稍不留神可能还会越界访问。
二标准库中的string
使用string必须用它对应的头文件#includestring
在学习接口及其文档的说明可以到cpulspuls官网中去学习
cplusplus.com - The C Resources Network
1(拷贝)构造和析构 string() 构造string类的空字符串(其中有’\0‘) string(char* s) 构造的同时进行string的拷贝(支持单参数隐式类型转换) string(const string str) string拷贝构造 string(const string str , size_t pos , size_t len npos) 进行str子串的获取pos是子串的起始位置len是子串的结束位置(没有说明就默认是str的结束位置) ~string() string的析构 2运算符重载
使string的访问向数组访问数据一样数组名[] char operator[] (size_t pos) const char operator[] (size_t pos) const string类的赋值对象可以是string,字符串字符 string operator (const string str) string operator (const char* s) string operator (char c) string类字符串的尾插对象可以是string,字符串字符 string operator (const string str) string operator (const char*s) string operator (char c) 实现string类与char*,char类型的字符(串)进行拼接(反过来也成立) string operator (const string lhs , const char* rhs) string operator (const char* lhs , const string rhs) string operator (const string lhs , char rhs) string operator (char rhs , const string lhs) 流插入流提取的重载 istream operator (istream is , string str) ostream operator (ostream os , string str) 3迭代器
迭代器行为像指针一样但不指针 iterator begin() 指向string的开始 const_iterator begin() const iterator end() 指向string的末尾 const_iterator end() const 反向迭代器 iterator rbegin() 指向string的末尾 irerator rend() 指向string的开始 有了迭代器我们就有了两种遍历string的方式迭代器与范围for(本质是迭代器)
#includestring
int main()
{string s1(hello warld);string::iterator it s1.begin();while (it ! s1.end()){cout *it ;it;}cout endl;for (auto ch : s1){cout ch ;}cout endl;return 0;
} 2查询string的各种数据 size_t size() const 返回string内的字符个数(不包含’\0‘) size_t length() const size_t capacity() const 返回string的空间大小(包含’\0‘) void reserve(size_t n0) 开n个空间大小 (知道要插入的多少数据提前开好空间就不用进行扩容提高了效率) 但空间不够时string应该要进行对应的空间扩容。那么是要扩容多少呢2倍扩 我们用的方式在string末尾每次插入一个字符循环100次来观察capacity的变化: #includestring
int main()
{string s;size_t sz s.capacity();cout capacity changed: sz \n;cout making s grow:\n;for (int i 0; i 100; i){s (c);if (sz ! s.capacity()){sz s.capacity();cout capacity changed: sz \n;}}return 0;
} 在VS底层中实现的扩容先开出15内存空间等到空间不够时第一次扩容是2倍扩接下来的扩容都是大概按1.5来扩容。 而在g下的扩容全是按照2倍扩容来的 但两者的扩容方式不同会不会有什么影响呢 没有 2倍扩容也好1.5倍扩容也好说到底它们的目标都是要进行扩容。实现起来的细节是不做考虑的。 void resize(size_t n,char c) 开n个空间大小并插入n-size()个字符c resize一般不缩容在以下的不同情况使用resize会有不同的效果 5增删查改 viod push_back (char c) 在string的末尾插入一个字符 void pop_back (char c) 在string的末尾删除一个字符 string insert(size_t pos , char c) 在pos下插入字符(字符串string) string erase(size_t pos , size_t len npos) 在pos位置下删除len个字符(不加len默认删除pos后面的全部字符) (左闭右开) void swap(string str) 不仅仅是数据的交换还有size,capacity的交换 6其它操作 void swap(string x , string y) 与算法库的swap分离开(如果调用算法库的swap三次拷贝一次析构代价太大) const char* c_str() const 类型转换成char*方便打印(string类的要用到运算符重载) string substr(size_t pos 0 , size_t len npos) 从pos位置到len个位置的字符串截取 size_t find(char c , size_t pos0) 返回字符c所在的下标 string replace(size_t pos , size_t len npos , char c) 在pos到len的位置插入c字符 两者的特性结合可用来解决 字符串空格的替换 三string的模拟实现
在string类中共有三个成员_str字符串_size字符个数_capacity空间大小
其中_size个数不包含‘\0’但在_capacity中要多开出一个空间存储‘\0’
3.1成员函数
3.1.1构造函数 在实现构造时如果string构造不传参默认为空串。 但不意味着给缺省值时就是”\0“因为写成”“是有’\0‘的存在的。 //string(const char* str \0)
string(const char* str ):_size(strlen(str))
{_capacity _size;_str new char[_size 1];strcpy(_str, str);//把字符串拷贝给_str成员对象}
3.1.2拷贝构造 拷贝构造里不单单只是数据拷贝要在拷贝之前先new一块跟str一样长的空间在进行数据的拷贝。也就是所谓的深拷贝 一般的思路是直接开出一个与拷贝对象一样长的空间再把对应的数据赋值过去就完事了 string(const string str)
{_str new char[str._capacity 1];strcpy(_str, str._str);_size str._size;_capacity str._capacity;
}
但这种属于是’自己买菜自己做‘。是比较传统的写法。 有这样的写法构造一个与拷贝对象一样的临时对象在进行交换(深拷贝)比较方便简洁: string(const string str)
{string tmp(str.c_str());swap(tmp);
} 有了拷贝构造与它对应的还有一个赋值运算符的重载也是类似的道理 string operator(const string str)
{char* tmp new char[str._capacity 1];strcpy(tmp, str._str);delete[] _str;//释放_str tmp;_size str._size;_capacity str._capacity;return *this;
}//现代写法
string operator(const string str)
{string tmp(str.c_str());swap(tmp);return *this;
}string operator(string ss)
{ swap(ss);return *this;
}
3.1.3析构函数
~string()
{delete[] _str;_str nullptr;_size _capacity 0;
}
3.2迭代器 实现的迭代器因为要支持可读可写与只读两个版本实现时要实现俩个版本出来 对应的begin()end()实现成string类中的成员函数 typedef char* iterator;//类似指针的作用
typedef const char* const_iterator;
const_iterator begin() const
{return _str;
}
const_iterator end() const
{return _str _size;
}iterator begin()
{return _str;
}
iterator end()
{return _str _size;
}
3.3扩容 传参进来的n如果比_capacity要小则需要进行空间的扩容到n1(’\0‘要开给它) void reserve(size_t n)
{//扩容if (_capacity n){char* tmp new char[n 1];strcpy(tmp , _str);_str tmp;_capacity n;}
} 它的扩容不是我们在C语言学的函数realloc扩容那样有两种情况原地扩与异地扩。它的扩容就是直接看出一块新的空间在把_str的数据拷贝的 3.4插入字符(串)
3.4.1append
将原来的_capacity与拼接形成新的string的_size个数进行比较空间不够才进行扩容
void append(const char* s)
{size_t len strlen(s);//扩容if (_capacity _size len){reserve(_size len);}/*for (size_t i _size; i len; i){_str[i] *s;s;}*/strcpy(_str _size, s);_size len;
}
3.4.2insert 在pos位置上插入字符也好字符串也好都需要进行是否扩容与数据挪动字符串的挪动必须是从后往前进行字符串的覆盖 void insert(size_t pos, char ch)
{assert(pos _size);if (_capacity _size){//扩容reserve(_capacity 0 ? 4 : _capacity * 2);}size_t end_size;while (endpos){_str[end1] _str[end];end--;}_str[pos] ch;_size 1;
} 但在这有个细节如果pos的位置是0即进行头插这将会进行程序出现问题因为但end0循环进入end--使得end-1。但end的类型是size_t不会为负数修改方式有两种 1修改类型2让end_size1进行循环时end永远都pos: void insert(size_t pos, char ch){assert(pos _size);if (_capacity _size){//扩容reserve(_capacity 0 ? 4 : _capacity * 2);}1size_t end _size;//当pos0时size_t end0end--!-1int end _size;while (end (int)pos){_str[end 1] _str[end];end--;2size_t end _size 1;while (endpos){_str[end] _str[end - 1];end--;}_str[pos] ch;_size 1;}
插入的是字符串也是同理的 void insert(size_t pos, const char* str){size_t len strlen(str);assert(pos _size);if (_capacity _size len){//扩容reserve(_size len);}/*int end _size;while (end (int)pos){_str[end len] _str[end];end--;}*/size_t end _size len;while (end poslen-1){_str[end] _str[end-len];end--;}strncpy(_strpos, str, len);_size len;} 3.4.3push_back
复用insert进行尾插或者直接实现
void push_back(char ch)
{if (_capacity _size){reserve(_capacity 0 ? 4 : _capacity * 2);}_str[_size] ch;_str[_size] \0;//insert(_size, ch);
}
3.4.4赋值运算符重载
string operator(char ch)
{push_back(ch);return *this;
}
string operator(const char* str)
{append(str);return *this;
}
3.5resize 实现resize是要了解的是使用resize一般不缩容。要插入n个字符是有两种情况n_size是仅需保留前n个字符串后加’\0‘即可n_capacity就要进行扩容与插入n-_size个字符 void resize(size_t n, char ch npos){if (n _size){_str[n] \0;_size n;}else{//扩容reserve(n);//nsize后面有要求加字符for (size_t i _size; i n; i){_str[i] ch;}_str[n] \0;_size n;}} 对应resize与reserve基本上都是有扩容的功能的但resize能在进行扩容的基础上将数据进行初始化比较适合在对数据的管理上使用它。 3.6find 进行string的遍历如果找到该字符就返回该字符的下标如果是找字符串找到了就返回该字符串的起始位置: size_t find(char c, size_t pos 0) const
{assert(pos _size);for (size_t i pos; i _size; i){if (c _str[i]){return i;}}return npos;
}
size_t find(const char* s, size_t pos 0) const
{assert(pos _size);const char* pstrstr(_str pos, s);if(p){return p - _str;//指针-指针s的下标}else{return npos;}
} 3.7运算符重载 流提取或者是流插入重载成全局函数好方便调用 ostream operator (ostream os, const string str)
{for (int i 0; i str.size(); i){os str[i];}return os;
}istream operator(istream in, string s)
{s.clear();//防止string中有数据影响流插入char ch;//in ch;ch in.get();//s.reserve(128);while (ch ! \n ch ! ){s ch;ch in.get();}return in;
} 在重载流插入时如果按照上面的实现方式的话在每次插入时都要进行扩容非常影响程序运行的效率。 如果要进行reserve开空间但至于要看多少不能确定。所以就有人进行对这段代码的优化 istream operator(istream in, string s)
{s.clear();char ch;//in ch;ch in.get();char buff[128];size_t i 0;while (ch ! ch ! \n){buff[i] ch;// [0,126]if (i 127){buff[127] \0;s buff;i 0;}ch in.get();}if (i 0){buff[i] \0;s buff;}return in;
} 要不要扩容取决于会不会超出buff的数组总个数非常的妙 完整源代码
#pragma once
#define _CRT_SECURE_NO_WARNINGS 1
#includeassert.h
#includeiostream
using namespace std;
namespace bit
{class string{public://迭代器typedef char* iterator;//类似指针的作用typedef const char* const_iterator;const_iterator begin() const{return _str;}const_iterator end() const{return _str _size;}iterator begin(){return _str;}iterator end(){return _str _size;}//在末尾加n个ch字符void resize(size_t n, char ch npos){if (n _size){_str[n] \0;_size n;}else{//扩容reserve(n);//nsize后面有要求加字符for (size_t i _size; i n; i){_str[i] ch;}_str[n] \0;_size n;}}//先开空间void reserve(size_t n){//扩容if (_capacity n){char* tmp new char[n 1];strcpy(tmp, _str);_str tmp;_capacity n;}}//末尾插人字符void push_back(char ch){if (_capacity _size){reserve(_capacity 0 ? 4 : _capacity * 2);}_str[_size] ch;_str[_size] \0;//insert(_size, ch);}//末尾插入字符串void append(const char* s){size_t len strlen(s);//扩容if (_capacity _size len){reserve(_size len);}/*for (size_t i _size; i len; i){_str[i] *s;s;}*/strcpy(_str _size, s);_size len;}string operator(char ch){push_back(ch);return *this;}string operator(const char* str){append(str);return *this;}//在pos位置插入字符void insert(size_t pos, char ch){assert(pos _size);if (_capacity _size){//扩容reserve(_capacity 0 ? 4 : _capacity * 2);}//size_t end _size;//当pos0时size_t end0end--!-1/*int end _size;while (end (int)pos){_str[end 1] _str[end];end--;}*/size_t end _size 1;//size-1Xwhile (endpos){_str[end] _str[end - 1];end--;}_str[pos] ch;_size 1;}void insert(size_t pos, const char* str){size_t len strlen(str);assert(pos _size);if (_capacity _size len){//扩容reserve(_size len);}/*int end _size;while (end (int)pos){_str[end len] _str[end];end--;}*/size_t end _size len;while (end poslen){_str[end] _str[end-len];end--;}strncpy(_strpos, str, len);_size len;}//删除pos位置的字符void erase(size_t pos, int len npos){assert(pos _size);if (len npos || len _size - pos)//lenpos会溢出{_str[pos] \0;_size pos;}else{strcpy(_str pos, _str pos len);_size - len;}}//string(const char* str \0)string(const char* str ):_size(strlen(str)){_capacity _size;_str new char[_size 1];strcpy(_str, str);//把字符串拷贝给_str成员对象}//传统写法//s2(s1)//string(const string str)//{// _str new char[str._capacity 1];// strcpy(_str, str._str);// _size str._size;// _capacity str._capacity;//}//s2s1//string operator(const string str)//{// char* tmp new char[str._capacity 1];// strcpy(tmp, str._str);// delete[] _str;//置空// _str tmp;// _size str._size;// _capacity str._capacity;// return *this;//}//现代写法string(const string str){string tmp(str.c_str());swap(tmp);}string operator(string str){swap(str);return *this;}~string(){delete[] _str;_str nullptr;_size _capacity 0;}const char* c_str() const{return _str;}size_t size() const{return _size;}size_t capacity() const{return _capacity;}char operator[](size_t pos){assert(pos _size);return _str[pos];}const char operator[](size_t pos) const{assert(pos _size);return _str[pos];}void clear(){_size 0;_str[_size] \0;}//string的成员函数void swap(string str){//共有三种swap使用算法string.swapswapstd::swap(_str, str._str);std::swap(_size, str._size);std::swap(_capacity, str._capacity);}bool empty()const{if (_size 0){return true;}return false;}size_t find(char c, size_t pos 0) const{assert(pos _size);for (size_t i pos; i _size; i){if (c _str[i]){return i;}}return npos;}size_t find(const char* s, size_t pos 0) const{assert(pos _size);const char* pstrstr(_str pos, s);if(p){return p - _str;//指针-指针s的下标}else{return npos;}}string substr(size_t pos 0, size_t len npos) const{string s;if (len _size-pos){for (size_t i pos; i _size; i){s _str[i];}}else{for (size_t i pos; i pos len; i){s _str[i];}}return s;}private:char* _str;size_t _size;size_t _capacity;public:static const int npos;//类中共有的对象};const int string::npos -1;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 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 || strcmp(s1.c_str(), s2.c_str()) 0);}//重载成全局函数ostream operator (ostream os, const string str){for (int i 0; i str.size(); i){os str[i];}return os;}istream operator (istream is, string str)//getline的实现方式 {str.clear();char ch is.get();char buffer[128];size_t i 0;while (ch ! ch ! \n){//str ch;buffer[i] ch;if (i 127){buffer[127] \0;//开128个空间str buffer;//重新计算i 0;}ch is.get();}if (i 0){//有多少看多少buffer[i] \0;str buffer;}return is;}/*istream operator (istream is, string str){str.clear();char ch is.get();while(ch ! ch ! \n){str ch;ch is.get();}return is;}*///定义一个全局的swap就不会去调用算法的swapvoid swap(string s1,string s2){s1.swap(s2);}