上传到服务器的网站打开是空白,网站定制兴田德润i在哪里,英文外贸网站建设,如何做闲置物品自己的网站1.我们为什么要学习string类#xff1f;
1.1 c语言中的字符串
我们先了解一下什么是OOP思想 OOP思想#xff0c;即面向对象编程#xff08;Object-Oriented Programming#xff09;的核心思想#xff0c;主要包括“抽象”、“封装”、“继承”和“多态”四个方面。 抽象…1.我们为什么要学习string类
1.1 c语言中的字符串
我们先了解一下什么是OOP思想 OOP思想即面向对象编程Object-Oriented Programming的核心思想主要包括“抽象”、“封装”、“继承”和“多态”四个方面。 抽象抽象是忽略一个主题中与当前目标无关的那些方面以便充分地注意与当前目标有关的方面。抽象并不打算了解全部问题而只是选择其中的一部分暂时不用部分细节。抽象包括两个方面一是过程抽象二是数据抽象。封装封装是把客观事物封装成抽象的类并且类可以把自己的数据和方法只让可信的类或者对象操作对不可信的进行信息隐藏。封装是面向对象的特征之一是对象和类概念的主要特性。简单的说一个类就是一个封装了数据以及操作这些数据的代码的逻辑实体。在一个对象内部某些代码或某些数据可以是私有的不能被外界访问。通过这种方式对象对内部数据提供了不同级别的保护以防止程序中无关的部分意外的改变或错误的使用对象的私有部分。继承继承是一种层次模型用于表示类型之间的父子关系。它提供了一种明确表达共性的方法。一个新类可以从现有的类中继承这个过程称为类继承。新类继承了原始类的特性新类称为子类原始类称为父类。子类可以从父类中继承方法和实例变量并且子类可以覆盖父类中的方法或者添加新的方法。多态多态性是指允许你将父对象设置成为和一个或更多的他的子对象相等的技术赋值之后父对象就可以根据当前赋值给它的子对象的特性以不同的方式运作。简单的说就是用同样的对象引用调用同样的方法但是做了不同的事情。多态性分为参数的多态性和方法的多态性。多态性语言具有灵活、抽象、行为共享、代码共享的优势可以很好地解决应用程序函数同名问题。 总的来说OOP思想通过抽象、封装、继承和多态等特性使得软件开发更加模块化、灵活和易于维护。在面向对象编程中开发者可以创建对象来模拟真实世界的实体并通过这些对象之间的交互来实现复杂的软件功能。 C语言中字符串是以\0结尾的一些字符的集合为了操作方便C标准库中提供了一些str系列的库函数但是这些库函数与字符串是分离开的因此不太符合OOP的思想而且底层空间需要用户自己管理稍不留神可能还会越界访问。
1.2 字符串相关的面试题
字符串相加
在OJ中有关字符串的题目基本以string类的形式出现而且在常规工作中为了简单、方便、快捷基本都使用string类很少有人去使用C库中的字符串操作函数。
2.标准库中的string类
2.1 string类
首先介绍一下一个网址这个网址可以帮助我们去快速学习我们的库中的知识
https://cplusplus.com/
建议多加使用该网站。
下面的内容了解即可~~
字符串是表示字符序列的类标准的字符串类提供了对此类对象的支持其接口类似于标准字符容器的接口但添加了专门用于操作单字节字符字符串的设计特性。string类是使用char(即作为它的字符类型使用它的默认char_traits和分配器类型(关于模板的更多信息请参阅basic_string)。string类是basic_string模板类的一个实例它使用char来实例化basic_string模板类并用char_traits和allocator作为basic_string的默认参数(根于更多的模板信息请参考basic_string)。注意这个类独立于所使用的编码来处理字节:如果用来处理多字节或变长字符(如UTF-8)的序列这个类的所有成员(如长度或大小)以及它的迭代器将仍然按照字节(而不是实际编码的字符)来操作。
总结
string是表示字符串的字符串类该类的接口与常规容器的接口基本相同再添加了一些专门用来操作string的常规操作。string在底层实际是basic_string模板类的别名typedef basic_stringchar, char_traits, allocator string;不能操作多字节或者变长字符的序列。
在使用string类时必须包含#include头文件以及using namespace std;
2.2 string类的常用接口 2.2.1 string类对象的常见构造 这是文档中的string类的所有构造函数但我们只学习其中最常见的即可 string() 重点 构造空的string类对象即空字符串 string(const char* s) 重点 用C-string来构造string类对象 string(size_t n, char c) string类对象中包含n个字符c string(const strings) 重点 拷贝构造函数 void Teststring()
{string s1; // 构造空的string类对象s1string s2(hello bit); // 用C格式字符串构造string类对象s2string s3(s2); // 拷贝构造s3
}
2.2.2 string类对象的容量操作 size重点 返回字符串有效字符长度 length 返回字符串有效字符长度 capacity 返回空间总大小 empty 重点 检测字符串是否为空串是返回true否则返回false clear 重点 清空有效字符 reserve 重点 为字符串预留空间** resize 重点 将有效字符的个数改成n个多出的空间用字符c填充 注意
size()与length()方法底层实现原理完全相同引入size()的原因是为了与其他容器的接口保持一致一般情况下基本都是用size()。clear()只是将string中有效字符清空不改变底层空间大小。resize(size_t n) 与 resize(size_t n, char c)都是将字符串中有效字符个数改变到n个不同的是当字符个数增多时resize(n)用0来填充多出的元素空间resize(size_t n, char c)用字符c来填充多出的元素空间。注意resize在改变元素个数时如果是将元素个数增多可能会改变底层容量的大小如果是将元素个数减少底层空间总大小不变。reserve(size_t res_arg0)为string预留空间不改变有效元素个数当reserve的参数小于string的底层空间总大小时reserver不会改变容量大小。
2.2.3 string类对象的访问及遍历操作 operator[] 重点 返回pos位置的字符const string类对象调用begin end
begin获取一个字符的迭代器 end获取最后一个字符下一个位置的迭代器 rbegin rend
begin获取一个字符的迭代器 end获取最后一个字符下一个位置的迭代器
范围forC11支持更简洁的范围for的新遍历方式
2.2.4 string类对象的修改操作 push_back 在字符串后尾插字符c append 在字符串后追加一个字符串 operator (重点) 在字符串后追加字符串str c_str(重点) 返回C格式字符串 find npos(重点) 从字符串pos位置开始往后找字符c返回该字符在字符串中的位置 rfind 从字符串pos位置开始往前找字符c返回该字符在字符串中的位置 substr 在str中从pos位置开始截取n个字符然后将其返回
注意
在string尾部追加字符时s.push_back(c) / s.append(1, c) / s c三种的实现方式差不多一般情况下string类的操作用的比较多操作不仅可以连接单个字符还可以连接字符串。对string操作时如果能够大概预估到放多少字符可以先通过reserve把空间预留好。
2.2.5 string类非成员函数
operator 尽量少用因为传值返回导致深拷贝效率低 operator 重点 输入运算符重载 operator 重点 输出运算符重载 getline 重点 获取一行字符串 relational operators 重点 大小比较
上面的几个接口大家了解一下下面的OJ题目中会有一些体现他们的使用。
string类中还有一些其他的操作这里不一一列举大家在需要用到时不明白了查文档即可。
2.2.6 常见面试题 仅仅反转字母
找字符串中第一个只出现一次的字符
字符串中最后一个单词的长度 验证一个字符串是否回文
字符串相加
3.string类的模拟实现
3.1 经典的string类问题
上面已经对string类进行了简单的介绍大家只要能够正常使用即可。在面试中面试官总喜欢让学生自己来模拟实现string类最主要是实现string类的构造、拷贝构造、赋值运算符重载以及析构函数。大家看下以下string类的实现是否有问题 // 为了和标准库区分此处使用String
class String
{
public:/*String():_str(new char[1]){*_str \0;}*///String(const char* str \0) 错误示范//String(const char* str nullptr) 错误示范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类
class String
{
public:String(const char* str ){// 构造String类对象时如果传递nullptr指针可以认为程序非 if (nullptr str){assert(false);return;}_str new char[strlen(str) 1];strcpy(_str, str);}String(const String s): _str(new char[strlen(s._str) 1]){strcpy(_str, s._str);}String operator(const String s){if (this ! s){char* pStr new char[strlen(s._str) 1];strcpy(pStr, s._str);delete[] _str;_str pStr;}return *this;}~String(){if (_str){delete[] _str;_str nullptr;}}
private:char* _str;
}; 现代版本的string类
class String
{
public:String(const char* str ){if (nullptr str){assert(false);return;}_str new char[strlen(str) 1];strcpy(_str, str);}String(const String s): _str(nullptr){String strTmp(s._str);swap(_str, strTmp._str);}// 对比下和上面的赋值那个实现比较好String operator(String s){swap(_str, s._str);return *this;}/*String operator(const String s){if(this ! s){String strTmp(s);swap(_str, strTmp._str);}return *this;}*/~String(){if (_str){delete[] _str;_str nullptr;}}
private:char* _str;
};
3.3 string类的模拟实现代码基本完整版
namespace bit
{class string{public:typedef char* iterator;typedef const char* const_iterator;public:const char* c_str() const{return _str;}string(const char* str ):_size(strlen(str)){_capacity _size;_str new char[_capacity 1];strcpy(_str, str);}//s2(s1)string(const string s){_str new char[s._capacity 1];strcpy(_str, s._str);_size s._size;_capacity s._capacity;}//s1 s3string operator(const string s){char* tmp new char[s._capacity 1];strcpy(tmp, s._str);delete[] _str;_str tmp;_size s._size;_capacity s._capacity;return *this;}~string(){delete[] _str;_str nullptr;_size _capacity 0;}iterator begin(){return _str;}iterator end(){return _str _size;}const_iterator begin() const{return _str;}const_iterator end() const{return _str _size;}//遍历 capacitysize_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];}bool empty() const{return _str nullptr;}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 reserve(size_t n){if (n _capacity){char* tmp new char[n 1];strcpy(tmp, _str);delete[] _str;_str tmp;_capacity n;}}// modifyvoid push_back(char ch){//扩容2倍if (_size _capacity){reserve(_capacity 0 ? 4 : 2 * _capacity);}_str[_size] ch;_size;_str[_size] \0;}void append(const char* str){//扩容size_t len strlen(str);if (_size len _capacity){reserve(_size _capacity);}strcpy(_str _size, str);_size len;}string operator(char ch){push_back(ch);return *this;}string operator(const char* str){append(str);return *this;}void clear() {_size 0;_str[_size] \0;}/*void swap(string s){string tmp s;}*/// 在pos位置上插入字符ch/字符串strvoid insert(size_t pos, char ch){assert(pos _size);//扩容2倍if (_size _capacity){reserve(_capacity 0 ? 4 : 2 * _capacity);}size_t end _size 1;while (end pos){_str[end] _str[end - 1];end--;}_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(_size len);}size_t end _size len;while (end pos len - 1){_str[end--] _str[end - len];}strncpy(_str pos, str, len);_size len;}// 删除pos位置上的元素void erase(size_t pos, size_t len npos){assert(pos _size);if (len npos || len _size - pos){_str[pos] \0;_size pos;}else{strcpy(_str pos, _str pos len);_size - len;}}void swap(string s){std::swap(_str, s._str);std::swap(_size, s._size);std::swap(_capacity, s._capacity);}// 返回ch在string中第一次出现的位置size_t find(char ch, size_t pos 0) const{assert(pos _size);for (size_t i pos; i _size; i){if (ch _str[i])return i;}return npos;}// 返回子串sub在string中第一次出现的位置size_t find(const char* sub, size_t pos 0) const{assert(pos _size);const char* p strstr(_str pos, sub);if (p){return p - _str;}else{return npos;}}string substr(size_t pos 0, size_t len npos){string sub;if (len _size - pos){for (size_t i pos; i _size; i){sub _str[i];}}else{for (size_t i pos; i pos len; i){sub _str[i];}}return sub;}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){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);}bool operator!(const string s1, const string s2){return !(s1 s2);}ostream operator(ostream out, const string s){for (auto ch : s){out ch;}return out;}istream operator(istream in, string s){s.clear();char ch;ch in.get();char buff[128];size_t i 0;while (ch ! ch ! \n){buff[i] ch;if (i 127){buff[127] \0;s buff;i 0;}ch in.get();}if (i 0){buff[i] \0;s buff;}return in;}istream getline(istream in, string s){s.clear();char ch;ch in.get();char buff[128];size_t i 0;while (ch ! \n){buff[i] ch;if (i 127){buff[127] \0;s buff;i 0;}ch in.get();}if (i 0){buff[i] \0;s buff;}return in;}
}