网站 制作,信产部网站备案,哈尔滨软件开发公司排名,定制vx免费这一节主要总结string类的常见接口#xff0c;以及完成了string类的模拟实现。 目录
标准库的String类
string类常见接口
string类对象的常见构造
string析构函数#xff1a;~string
string类对象的容量操作
string类对象的访问及遍历操作
string类对象的修改操作
s… 这一节主要总结string类的常见接口以及完成了string类的模拟实现。 目录
标准库的String类
string类常见接口
string类对象的常见构造
string析构函数~string
string类对象的容量操作
string类对象的访问及遍历操作
string类对象的修改操作
string类非成员函数
string类的模拟实现
经典的string类问题
浅拷贝
深拷贝
传统版写法的String类
现代版写法的String类
string类的模拟实现 标准库的String类
为了符合C面向对象的特性引入了string类string是表示字符串的字符串类。下面说明一下string类的特点 1.string是表示字符串的字符串类 2.string类是basic_string模板类的一个实例它使用char来实例化basic_string模板类并用char_traits和allocator作为basic_string的默认参数 3.string类独立于所使用的编码来处理字节如果用来处理多字节或变长字符(如UTF-8)的序列这个类的所有成员(如长度或大小)以及它的迭代器将仍然按照字节(而不是实际编码的字符)来操作。 提示 在使用string类时必须包含#include头文件以及using namespace std;
string类常见接口
string类对象的常见构造
重点1.string() 默认构造构造空的string类对象即空字符串 重点2.string(const string str) 拷贝构造使用str拷贝构造string类对象 重点3.string(const char* s) 使用C-string来构造string类对象 4.string(const string str, size_t pos, size_t lennpos) 从str的第pos个位置取len个字符构造如果len超过字符串长度那么只取到字符串末尾如果len未给出那么采用默认参数npos(0xFFFFFFFF) 5.string(const char* s, size_t n) 使用C-string的前n个字符来构造string类对象 6.string(size_t n char c) string类对象包含n个字符c void test_string1()
{string s0;string s1(hello world);string s2(s1);string s3(s1, 5, 3);string s4(s1, 5, 10);string s5(s1, 5);string s6(10, $);cout s0 endl;cout s1 endl;cout s2 endl;cout s3 endl;cout s4 endl;cout s5 endl;cout s6 endl;
} string析构函数~string
在string类对象生命周期结束时会被自动调用。
string类对象的容量操作
1.size()和length() 这两个均可以返回字符串有效字符长度功能一样。可以通过size遍历字符串。有两个功能一样的函数是由于历史原因造成的 2.max_size() 返回string最大的大小 3.capacity() 返回string类对象的容量大小。 //查看扩容机制
void test_string4()
{string s;size_t sz s.capacity();cout capacity change: sz endl;cout making s growing endl;for (int i 0; i 100; i){s.push_back(c);if (sz ! s.capacity()){sz s.capacity();cout capacity changed sz endl;}}
} 虽然是15、31...但是没有包括‘\0’所以实际大小应该是16、32、48...第一次是2倍扩容以后每次是1.5倍扩容VS平台下。但是在Linux平台下扩容机制不太一样 这就说明STL是一个标准具体怎么实现由编译器决定
4.clear() 清除对象的数据但是不会清空间。 5.shrink_to_fit() 在clear()后为了释放一些已开辟的空间可以缩容 6.reserve() 如果提前知道要开辟空间的大小可以用reserve()为字符串提前预留空间防止频繁扩容因为扩容一般是异地扩容效率低下。 避免了频繁扩容
还有一个问题reserve会不会缩容呢不会的当reserve()比capacity大才会扩容 7. resize() 调整字符串大小。 如果采用第一种那么默认用‘\0’插入第二种用指定的char插入。 string类对象的访问及遍历操作 1.operator[]返回pos位置的字符 string s1(hello world);
for (size_t i 0; i s1.size(); i)
{cout s1[i] ;
} 2.迭代器beginend可以通过迭代器的方式遍历字符串begin获取第一个字符的迭代器end获取最后一个字符下一个位置的迭代器 string::iterator it1 s1.begin();while (it1 ! s1.end()){cout *it1 ;it1;} 我们感觉到迭代器很像指针但是并不是指针 3.rbeginrend支持倒着遍历循环中用rit而不是--rit对于反向迭代器而言就是倒着走 string s1(hello world);
string::reverse_iterator rit s1.rbegin();
while (rit ! s1.rend())
{cout *rit ;rit;
} 除了正向和反向的正常iterator外还有正向和反向的const_iterator是用来为const string构造迭代器其特点是只能读不能写
//只读
const string s3(hello world);
string::const_iterator it3 s3.begin();
while (it3 ! s3.end())
{//*it3 3;//报错因为只读cout *it3 ;
}
cout endl;
//string::const_reverse_iterator 4.用范围for遍历字符串 for (auto s : s1){cout s ;} 但是范围for并没有什么特别之处其底层是迭代器
string类对象的修改操作
1.pushback() 在字符串末尾尾插字符 2.append() 在字符串后追加字符串 3.operator 在字符串后追加字符串str 4.assign() 用新内容覆盖原字符串 5.insert() 在某一个位置插入字符串 注意insert()尽量不要使用因为在使用过程中会挪动数据效率低。
6.erase() 删除字符 同样erase()函数能不用就不用。
7.replace() 字符的替换 8.find() 查找某个字符的位置 小练习将一个字符串中的空格替换成“%20”。 总结一下insert、erase、replace能少用就少用因为基本都要挪动数据效率不高。
9.c_str 返回C格式字符串 在C程序中我们有可能会调用C语言的函数比如fopen函数但是在程序中文件名可能是string对象需要转换成C字符串才行。
void test_string()
{string filename(test.cpp);FILE* fout fopen(filename.c_str(), r);
}
10.find 找到某一个字符或字符串第一次出现的位置 例如获取后缀
void test_string()
{string s1(test.cpp);size_t pos s1.find(.);if (pos ! string::npos){string suffix s1.substr(pos);cout suffix endl;}else{cout 没有后缀 endl;}
}
11.rfind 从后往前找某一个字符或字符串第一次出现的位置 例如获取后缀“file.c.tar.zip”的zip
void test_string()
{string s1(test.c.tar.zip);size_t pos s1.rfind(.);if (pos ! string::npos){string suffix s1.substr(pos);cout suffix endl;}else{cout 没有后缀 endl;}
}
string类非成员函数
1.operator 尽量少用因为传值返回导致深拷贝效率低 2.operatoroperator 输入和输出运算符重载 3.getline 获取一行字符串。弥补了cin的缺点cin遇到了空格或者换行就提取结束。 4. ! 大小比较 string类的模拟实现
经典的string类问题
首先来看一个经典关于浅拷贝的问题
class String
{
public:
/*String()
:_str(new char[1])
{*_str \0;}
*/
//String(const char* str \0) 错误示范
//String(const char* str nullptr) 错误示范,使用strlen时会涉及解引用
String(const char* str )
{// 构造String类对象时如果传递nullptr指针可以认为程序非if (nullptr str){assert(false);return;}_str new char[strlen(str) 1];strcpy(_str, str);
}
~String()
{if (_str){delete[] _str;_str nullptr;}
}
private:char* _str;
};
// 测试
void TestString()
{String s1(hello bit!!!);String s2(s1);
} 说明在上面的String类中没有显式定义拷贝构造和赋值运算符重载因此当使用s1去构造时s2时编译器会调用默认构造函数。这样导致的问题是s1和s2指向同一块内存空间在释放空间时同一块空间会被释放两次而引起程序崩溃这种拷贝方式称为浅拷贝。 浅拷贝 浅拷贝也称值拷贝编译器只是将对象中的值拷贝过来。如果对象中管理资源最后就会导致多个对象共享同一份资源当一个对象销毁时就会将该资源释放掉而此时另一些对象不知道该资源已经被释放以为还有效所以当继续对资源进项操作时就会发生发生了访问违规。 可以用深拷贝解决浅拷贝问题也就是每个对象都有一份独立的资源不要和其他对象共享。
深拷贝
如果一个类中涉及到资源的管理其拷贝构造函数、赋值运算符重载以及析构函数必须要显式给出。一般情况都是按照深拷贝方式提供。 传统版写法的String类
namespace ghs
{class string{public:string(const char* str):_size(strlen(str)){_str new char[_size 1];strcpy(_str, str);_capacity _size;}string(const string str){_str new char[str._capacity 1];strcpy(_str, str._str);_size str._size;_capacity str._capacity;}//s1 s2string 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(){delete[] _str;_str nullptr;_size 0;_capacity 0;}private:char* _str nullptr;size_t _size 0; size_t _capacity 0;public:static const int npos;};const int string::npos -1;
}
在传统版的写法中拷贝构造和赋值构造都是通过自己手动开空间然后使用strcpy函数拷贝完成的下面的现代写法在实现上更为简洁
现代版写法的String类
namespace ghs
{class string{public:string(const char* str):_size(strlen(str)){_str new char[_size 1];strcpy(_str, str);_capacity _size;}string(const string s){string tmp(s._str);swap(tmp);}//s1 s2//string operator(const string str)//{// string ss(str);// swap(ss);// return *this;//}//上面的再优化这种更推荐string operator(string ss)//传值传参调用拷贝构造{swap(ss);return *this;}~string(){delete[] _str;_str nullptr;_size 0;_capacity 0;}private:char* _str nullptr;size_t _size 0; size_t _capacity 0;public:static const int npos;};const int string::npos -1;
}
在现代写法中拷贝构造是通过调用string(const char* str)构造函数来生成临时的string对象tmp再将*this和tmp交换完成拷贝。在赋值构造中使用传值传参其实调用拷贝构造生成临时对象ss再将ss和*this交换完成拷贝。
总结传统版的现代版的效率一样只是现代版调用了之前已经实现好的函数所以看起来更加简洁
string类的模拟实现
namespace ghs
{class string{public:/*string():_str(nullptr),_size(0),_capacity(0){}*//*string():_str(new char[1]),_size(0),_capacity(0){_str[0] \0;}*/typedef 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;}string(const char* str):_size(strlen(str)){_str new char[_size 1];strcpy(_str, str);_capacity _size;}//string(const string str)//{// _str new char[str._capacity 1];// strcpy(_str, str._str);// _size str._size;// _capacity str._capacity;//}string(const string s){string tmp(s._str);swap(tmp);}//s1 s2//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 ss(str);swap(ss);return *this;}//上面的再优化这种更推荐//string operator(string ss)//传值传参调用拷贝构造//{// swap(ss);// return *this;//}const char* c_str()const{return _str;}size_t size()const{return _size;}char operator[](size_t pos){assert(pos _size);return _str[pos];}void reserve(size_t sz){if (sz _capacity){char* p new char[sz 1];strcpy(p, _str);delete[] _str;_str p;_capacity sz;}}void push_back(char ch){//扩容2倍/*if (_size _capacity){reserve(_capacity 0 ? 4 : 2 * _capacity);}_str[_size] ch;_size;_str[_size] \0;*/insert(_size, ch);}void append(const char* str){//扩容//size_t len strlen(str);//if (_size len _capacity)//{// reserve(_size len);//}//strcpy(_str _size, str);///*for (size_t i 0; i len; i)//{// push_back(str[i]);//}*///_size len;insert(_size, str);}string operator(char ch){push_back(ch);return *this;}string operator(const char* str){append(str);return *this;}const char operator[](size_t pos)const{assert(pos _size);return _str[pos];}size_t capacity()const{return _capacity;}void insert(size_t pos, char ch){assert(pos _size);if (_size _capacity){reserve(_capacity 0 ? 4 : 2 * _capacity);}for (size_t end _size1; end pos; end--){_str[end] _str[end - 1];}_str[pos] ch;_size;}void insert(size_t pos, const char* str){assert(pos _size);size_t len strlen(str);if (_size len _capacity){reserve(len _size);}for (size_t end _sizelen; end poslen-1; end--){_str[end] _str[end-len];}/*for (size_t i 0; i len; i){_str[pos i] str[i];}*/strncpy(_str pos, str, len);_size len;}void erase(size_t pos, size_t len npos){assert(pos _size);if (len npos || len_size-pos){_size pos;_str[_size] \0;}else{strcpy(_str pos, _str pos len);_size - len;}}void resize(size_t n, char ch \0){if (n _size){_str[n] \0;_size n;}else{reserve(n);for (size_t i _size; i n; i){_str[i] ch;}_str[n] \0;_size n;}}void swap(string s){std::swap(_str, s._str);std::swap(_size, s._size);std::swap(_capacity, s._capacity);}size_t find(char ch, size_t pos 0)const{assert(pos _size);for (size_t i pos; i _size; i){if (_str[i] ch){return i;}}return npos;}size_t find(const char* str, size_t pos 0)const{assert(pos _size);char* tmp strstr(_strpos, str);if (tmp ! nullptr){return tmp - _str;}else{return npos;}}string substr(size_t pos 0, size_t len npos){string s;if (len npos || 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;}void clear(){_size 0;_str[_size] \0;}/*bool operator(const string str){int ret strcmp(_str, str._str);return ret 0;}*/~string(){delete[] _str;_str nullptr;_size 0;_capacity 0;}private:char* _str nullptr;size_t _size 0; size_t _capacity 0;public:static const int npos;};const int string::npos -1;void swap(string x, string y){x.swap(y);}bool operator(const string s1,const string s2){int ret strcmp(s1.c_str(), s2.c_str());return ret 0;}bool operator(const string s1, const string s2){int ret strcmp(s1.c_str(), s2.c_str());return ret 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);}ostream operator(ostream out, const string str){for (auto ch : str){out ch; }return out;}istream operator(istream in, string str){char ch;str.clear();//in ch;ch in.get();char buff[100];size_t i 0;while (ch ! ch ! \n){buff[i] ch;if (i 99){buff[99] \0;str buff;i 0;}ch in.get();}if (i 0){buff[i] \0;str buff;}return in; }istream getline(istream in, string str){char ch;str.clear();char buff[100];//in ch;/*ch in.get();while (ch ! \n){str ch;ch in.get();}*/ch in.get();size_t i 0;while (ch ! \n){buff[i] ch;if (i 99){buff[99] \0;str buff;i 0;}ch in.get();}if (i 0){buff[i] \0;str buff;}return in;}
}