C++笔记归纳5:string
标准模板库是C++标准库的重要组成部分,不仅是一个可复用的组件库而且是一个包含数据结构和算法的软件框架对于给定的若干个单词组成的句子每个单词均由大小写字母混合构成,单词间使用单个空格分隔输出最后一个单词的长度输入:HelloNowcoder输出:13int main()// 不要使用cin>>line,因为会它遇到空格就结束了return 0;getline函数当输入到指定字符时按下回车才会停止i
string
目录
一、STL简介
1.1.STL的概念
STL(Standard Template Libaray):标准模板库
是C++标准库的重要组成部分,不仅是一个可复用的组件库
而且是一个包含数据结构和算法的软件框架
1.2.STL的版本
原始版本
在惠普实验室完成的原始版本,开源使用,HP版本是所以STL实现版本的始祖
P.J.版本
继承HP版本,被Windows Visual C++采用,不能公开,修改,可读性低
RW版本
继承HP版本,被C++ Bulider采用,不能公开,修改,可读性一般
SGI版本(主要学习版本)
继承HP版本,被GCC采用,可移植性好,可公开,修改,可读性高
1.3.STL的六大组件
容器:数据结构
算法:排序,逆置,交换...
空间配置器:内存池
......

二、string类
严格来说,string属于C++标准库,但并不属于STL
而从功能上来说,是可以归类到STL的容器里面的
2.1.string类的主要用法
#include<iostream>
#include<string>
#include<assert.h>
using namespace std;
class my_string
{
public:
char& operator[](size_t i)
{
assert(i < _size);
return _str[i];
}
private:
char* _str;
size_t _size;
size_t _capacity;
};
int main()
{
//string():默认构造
string s1;
//string(const char* s):带参构造
string s2("Hello World");
//string(const char& str):拷贝构造
string s3(s2);
cin >> s1;
cout << s1 << endl;
cout << s2 << endl;
cout << s3 << endl;
//string(const char& str,size_t pos,size_t len = npos):取第6位之后的5个字符
string s4(s2, 6, 5);
cout << s4 << endl;
//string(const char& str,size_t pos,size_t len = npos):取第6位之后的所有字符
string s5(s2, 6);
cout << s5 << endl;
//npos代表-1,表示字符串的末尾
//string(const char* s,int n):取前五个字符初始化
string s6("Hello World", 5);
cout << s6 << endl;
//char& operator[](size_t i):下标访问符运算符重载
s6[0] = 'x';
cout << s6 << endl;
return 0;
}

2.2.迭代器遍历
迭代器:string类的内部类,所有的容器都有自己的迭代器,可以通过它来访问容器
#include<iostream>
#include<string>
#include<list>
using namespace std;
void test_string1()
{
string s1;
string s2("hello world");
cout << s1 << s2 << endl;
s2[0] = 'x';
cout << s1 << s2 << endl;
//遍历方法1:下标 + []
/*for (size_t i = 0; i < s2.size(); i++)
{
cout << s2[i] << " ";
}
cout << endl;*/
//遍历方法2:迭代器
string::iterator it = s2.begin();
while (it != s2.end())
{
cout << *it << " ";
++it;
}
cout << endl;
//迭代器访问链表
list<int> lt = { 1,2,3,4,5,6,7 };
list<int>::iterator lit = lt.begin();
while (lit != lt.end())
{
cout << *it << " ";
++it;
}
cout << endl;
}
int main()
{
test_string1();
return 0;
}

2.3.范围for遍历
#include<iostream>
#include<string>
using namespace std;
void test_string1()
{
string s1;
string s2("hello world");
cout << s1 << s2 << endl;
s2[0] = 'x';
cout << s1 << s2 << endl;
//遍历方法1:下标 + []
for (size_t i = 0; i < s2.size(); i++)
{
cout << s2[i] << " ";
}
cout << endl;
//遍历方法2:迭代器
string::iterator it = s2.begin();
while (it != s2.end())
{
cout << *it << " ";
++it;
}
cout << endl;
//遍历方法3:范围for(C++11)
//自动赋值,自动迭代,自动判断结束
for (auto ch : s2)
{
cout << ch << " ";
}
cout << endl;
}
int main()
{
test_string1();
return 0;
}
注:
迭代器可以修改s2,但范围for无法修改s2
ch是s2中每个字符的拷贝,修改的只是临时变量
而迭代器中的*it类似于指针解引用
可以在ch前加解引用,取别名后就能够修改s2
#include<iostream>
#include<string>
using namespace std;
void test_string1()
{
string s1;
string s2("hello world");
cout << s1 << s2 << endl;
s2[0] = 'x';
cout << s1 << s2 << endl;
//遍历方法1:下标 + []
for (size_t i = 0; i < s2.size(); i++)
{
cout << s2[i] << " ";
}
cout << endl;
//遍历方法2:迭代器
string::iterator it = s2.begin();
while (it != s2.end())
{
*it += 2;
cout << *it << " ";
++it;
}
cout << endl;
//遍历方法3:范围for(C++11)
//自动赋值,自动迭代,自动判断结束,底层为迭代器
//for (auto ch : s2):别名
for (auto ch : s2)//拷贝
{
ch -= 2;
cout << ch << " ";
}
cout << endl;
cout << s2 << endl;
}
int main()
{
test_string1();
return 0;
}

2.4.auto关键字
C/C++:auto修饰的变量是具有自动存储器的局部变量
C++11:作为一个新的类型指示符来指示编译器
auto声明的变量必须由编译器在编译时期推导而得
主要用法:
#include<iostream>
#include <string>
#include <map>
using namespace std;
int main()
{
std::map<std::string, std::string> dict = { { "apple", "苹果" },{ "orange","橙子" }, {"pear","梨"} };
//std::map<std::string, std::string>::iterator it = dict.begin();
auto it = dict.begin();
while (it != dict.end())
{
cout << it->first << ":" << it->second << endl;
++it;
}
return 0;
}
数组不能包含auto的元素类型

auto必须推导同一个类型

auto必须有初始值设定

auto可以作返回值,但需要谨慎使用

auto不能作函数参数

2.5.范围for
C++11:引入了基于范围的for循环
for循环后的括号由冒号“:”分为两部分
前一部分:范围内用于迭代的变量;后一部分:被迭代的范围
作用:在数组和容器对象上进行遍历
特点:自动迭代,自动取数据,自动判断结束
底层:替换为迭代器
#include<iostream>
#include <string>
using namespace std;
int main()
{
int array[] = { 1, 2, 3, 4, 5 };
// C++98的遍历
for (int i = 0; i < sizeof(array) / sizeof(array[0]); ++i)
{
array[i] *= 2;
}
for (int i = 0; i < sizeof(array) / sizeof(array[0]); ++i)
{
cout << array[i] << " ";
}
cout << endl;
// C++11的遍历
for (auto& e : array)
{
e *= 2;
}
for (auto e : array)
{
cout << e << " " ;
}
cout << endl;
string str("hello world");
for (auto ch : str)
{
cout << ch << " ";
}
cout << endl;
return 0;
}

2.6.迭代器
迭代器是一种封装
屏蔽了底层容器的结构和细节
提供了同一的类似访问容器的方式

#include<iostream>
#include<string>
using namespace std;
void test_string1()
{
string s2("hello world");
//正向迭代器
string::iterator it = s2.begin();
while (it != s2.end())
{
cout << *it << " ";
++it;
}
cout << endl;
//反向迭代器
string::reverse_iterator rit = s2.rbegin();
while (rit != s2.rend())
{
cout << *rit << " ";
++rit;
}
cout << endl;
}
int main()
{
test_string1();
return 0;
}
注:反向迭代器也是++rit,但是是倒着走的

普通迭代器:可读可写
#include<iostream>
#include<string>
using namespace std;
void test_string1()
{
string s2("hello world");
//正向迭代器
string::iterator it = s2.begin();
while (it != s2.end())
{
*it += 10;
cout << *it << " ";
++it;
}
cout << endl;
//反向迭代器
string::reverse_iterator rit = s2.rbegin();
while (rit != s2.rend())
{
*rit -= 10;
cout << *rit << " ";
++rit;
}
cout << endl;
}
int main()
{
test_string1();
return 0;
}

const迭代器:只能读,不能写
#include<iostream>
#include<string>
using namespace std;
void test_string1()
{
const string s3("hello world");
//正向迭代器
//string::const_iterator cit = s3.begin();
auto cit = s3.begin();
while (cit != s3.end())
{
cout << *cit << " ";
++cit;
}
cout << endl;
//反向迭代器
//string::const_reverse_iterator rcit = s3.rbegin();
auto rcit = s3.rbegin();
while (rcit != s3.rend())
{
cout << *rcit << " ";
++rcit;
}
cout << endl;
}
int main()
{
test_string1();
return 0;
}

2.7.容量操作(Capacity)
size与length:
返回字符串有效字符长度
length向前兼容size,size更具有通用性
#include<iostream>
#include<string>
using namespace std;
void test_string3()
{
string s2("hello world");
cout << s2.length() << endl;
cout << s2.size() << endl;
}
int main()
{
test_string3();
return 0;
}

max_size:
最大的size数
#include<iostream>
#include<string>
using namespace std;
void test_string3()
{
string s2("hello world");
cout << s2.max_size() << endl;
}
int main()
{
test_string3();
return 0;
}

capacity:
容量(当前申请的空间大小)
#include<iostream>
#include<string>
using namespace std;
void test_string3()
{
string s2("hello world");
cout << s2.capacity() << endl;
}
int main()
{
test_string3();
return 0;
}

在VS2019中
去除\0,第一次扩容2倍,后续扩容1.5倍
当字符串的字符个数小于16时,会存在_Buf数组里
当字符串的字符个数大于16时,会存在_ptr指向的堆区空间


在g++4.8中
总是2倍扩容,并且没有_Buf数组

reserve:为字符串预留空间
提前开好空间可以避免扩容
如果n大于当前容量,则会将容量扩容到n,或者大于n
在VS中,reserve不会发送缩容(牺牲空间换时间)
在g++中,reserve会发送缩容(保证空间)
#include<iostream>
#include<string>
void test_string3()
{
string s1;
s1.reserve(100);
size_t sz = s1.capacity();
cout << "capacity changed:" << sz << endl;
}
int main()
{
test_string3();
return 0;
}

注:string返回的capacity值不包括\0
resize:将有效字符的个数改成n个,多出的空间用字符c填充
2.8.访问及遍历操作
operator[]:返回pos位置的字符,越界访问时会断言报错
at:与[]功能一样,但越界 访问时会抛异常
2.9.修改操作
push_back:在字符串后尾插字符c
append:在字符串后追加一个字符串
operator+=:在字符串后追加字符串str
insert:插入字符
#include<iostream>
#include<string>
using namespace std;
void test_string3()
{
string s("hello world");
s.push_back(' ');
s.push_back('x');
s.append("yyyyyy");
cout << s << endl;
s += ' ';
s += "333333";
cout << s << endl;
s.insert(0, "hello yonaka ");
cout << s << endl;
}
int main()
{
test_string3();
return 0;
}

erase:删除字符
#include<iostream>
#include<string>
using namespace std;
void test_string3()
{
string s("hello world");
s.erase(6, 1);
cout << s << endl;
s.erase(0, 1);
cout << s << endl;
s.erase(s.begin());
cout << s << endl;
s.erase(s.end());
cout << s << endl;
s.erase(s.size()-1,1);
cout << s << endl;
string ss("hello world");
ss.erase(6);
cout << ss << endl;
}
int main()
{
test_string3();
return 0;
}

replace:替换字符
#include<iostream>
#include<string>
using namespace std;
void test_string3()
{
string s("hello world bye world");
s.replace(5, 4, "%%");
cout << s << endl;
}
int main()
{
test_string3();
return 0;
}

find:查找字符
返回值:
如果匹配,返回第一个匹配成功的字符下标
如果不匹配,返回-1(string::npos)
#include<iostream>
#include<string>
using namespace std;
void test_string3()
{
string s("hello world bye world");
//方法1:
size_t pos = s.find(' ');
while (pos != string::npos)
{
cout << s << endl;
s.replace(pos, 1, "%%");
pos = s.find(' ',pos+2);
}
cout << s << endl;
//方法2:
string tmp;
//tmp.reserve(s.size());
for (auto ch : s)
{
if (ch == ' ')
{
tmp += "%%";
}
else
{
tmp += ch;
}
}
cout << tmp << endl;
//s = tmp;
s.swap(tmp);
cout << s << endl;
}
int main()
{
test_string3();
return 0;
}

c_str:返回c格式字符串
#define _CRT_SECURE_NO_WARNINGS 1
#include<iostream>
#include<string>
using namespace std;
void test_string3()
{
string file;
cin >> file;
FILE* fout = fopen(file.c_str(), "r");
char ch = fgetc(fout);
while (ch != EOF)
{
cout << ch;
ch = fgetc(fout);
}
fclose(fout);
}
int main()
{
test_string3();
return 0;
}

substr:在str中从pos位置开始,截取n个字符后返回
#define _CRT_SECURE_NO_WARNINGS 1
#include<iostream>
#include<string>
using namespace std;
void test_string3()
{
string s("test.cpp");
size_t pos = s.find('.');
string suffix = s.substr(pos);
cout << suffix << endl;
}
int main()
{
test_string3();
return 0;
}

rfind:从字符串pos位置开始往前找字符c,返回该字符在字符串中的位置
#define _CRT_SECURE_NO_WARNINGS 1
#include<iostream>
#include<string>
using namespace std;
void test_string3()
{
string s("test.cpp.zip");
size_t pos = s.rfind('.');
string suffix = s.substr(pos);
cout << suffix << endl;
}
int main()
{
test_string3();
return 0;
}

find_first(any)_of:返回字符串内任何符合字符的下标
#define _CRT_SECURE_NO_WARNINGS 1
#include<iostream>
#include<string>
using namespace std;
void test_string3()
{
string str("Please,replace the vowels in this sentence by asterisks");
size_t found = str.find_first_of("abcd");
while (found != string::npos)
{
str[found] = '*';
found = str.find_first_of("abcd", found + 1);
}
cout << str << endl;
}
int main()
{
test_string3();
return 0;
}

find_first(any)_not_of:返回字符串内任何不符合字符的下标
#define _CRT_SECURE_NO_WARNINGS 1
#include<iostream>
#include<string>
using namespace std;
void test_string3()
{
string str("Please,replace the vowels in this sentence by asterisks");
size_t found = str.find_first_not_of("abcd");
while (found != string::npos)
{
str[found] = '*';
found = str.find_first_not_of("abcd", found + 1);
}
cout << str << endl;
}
int main()
{
test_string3();
return 0;
}

find_last(any)_of:返回字符串内任何符合字符的下标(从后开始)
#define _CRT_SECURE_NO_WARNINGS 1
#include<iostream>
#include<string>
using namespace std;
void SplitFilename(const string& str)
{
cout << "Splitting:" << str << '\n';
size_t found = str.find_last_of("/\\");
cout << "path:" << str.substr(0, found) << '\n';
cout << "file:" << str.substr(found + 1) << '\n';
}
void test_string3()
{
string str1("/usr/bin/man");
string str2("c:\\windows\\winhelp.exe");
SplitFilename(str1);
SplitFilename(str2);
}
int main()
{
test_string3();
return 0;
}

2.10.非成员函数
operator+
#define _CRT_SECURE_NO_WARNINGS 1
#include <iostream>
#include <string>
using namespace std;
void test_string3()
{
string s1("hello");
string s2 = s1 + "world";
cout << s2 << endl;
string s3 = "world" + s1;
cout << s3 << endl;
}
int main()
{
test_string3();
return 0;
}
三、string试题
试题1:仅仅反转字母
题目内容:
给你一串字符s,所有非英文字母保留在原有位置
所有英文字母(大写或小写)位置反转,返回反转后的s
示例:
输入:s = "ab-cd"
输出:"dc-ba"
class Solution
{
public:
bool isLetter(char ch)
{
if(ch >= 'a' && ch <= 'z')
{
return true;
}
if(ch >= 'A' && ch <= 'Z')
{
return true;
}
return false;
}
string reverseOnlyLetters(string s)
{
int left = 0,right = s.size()-1;
while(left < right)
{
while(left < right && !isLetter(s[left]))
{
++left;
}
while(left < right && !isLetter(s[right]))
{
--right;
}
swap(s[left++],s[right--]);
}
return s;
}
};
试题2:字符串中的第一个唯一字符
题目内容:
给定一个字符串s,找到它的第一个不重复的字符
并且返回它的索引,如果不存在,则返回-1
示例:
输入:s = "loveleetcode"
输出:2
class Solution
{
public:
int firstUniqChar(string s)
{
int count[26] = {0};
//统计次数
for(auto ch : s)
{
count[ch - 'a']++;
}
for(size_t i = 0;i < s.size();++i)
{
if(count[s[i] - 'a']== 1)
{
return i;
}
}
return -1;
}
};
试题3:字符串相加
题目内容:
给定两个字符串形式的非负数num1和num2,计算它们的和
并且同样以字符串的形式返回,不能直接将输入字符串转换为整型
示例:
输入:num1 = "456",num2 = "77"
输出:"533"
方法1:insert头插
class Solution
{
public:
string addStrings(string num1, string num2)
{
string str;
int end1 = num1.size()-1,end2 = num2.size()-1;
int next = 0;
while(end1 >= 0 || end2 >= 0)
{
int val1 = end1 >= 0 ? num1[end1--] - '0' : 0;
int val2 = end2 >= 0 ? num2[end2--] - '0' : 0;
int ret = val1 + val2 + next;
next = ret / 10;
ret = ret % 10;
str.insert(str.begin(),'0' + ret);
}
if(next == 1)
{
str.insert(str.begin(),'1');
}
return str;
}
};
方法2:+=尾插+reverse逆置
class Solution
{
public:
string addStrings(string num1, string num2)
{
string str;
int end1 = num1.size()-1,end2 = num2.size()-1;
int next = 0;
while(end1 >= 0 || end2 >= 0)
{
int val1 = end1 >= 0 ? num1[end1--] - '0' : 0;
int val2 = end2 >= 0 ? num2[end2--] - '0' : 0;
int ret = val1 + val2 + next;
next = ret / 10;
ret = ret % 10;
str += ('0' + ret);
}
if(next == 1)
{
str += '1';
}
reverse(str.begin(),str.end());
return str;
}
};
试题4:字符串最后一个单词的长度
题目内容:
对于给定的若干个单词组成的句子
每个单词均由大小写字母混合构成,单词间使用单个空格分隔
输出最后一个单词的长度
示例:
输入:HelloNowcoder
输出:13
#include<iostream>
#include<string>
using namespace std;
int main()
{
string line;
// 不要使用cin>>line,因为会它遇到空格就结束了
// while(cin>>line)
while (getline(cin, line))
{
size_t pos = line.rfind(' ');
cout << line.size() - pos - 1 << endl;
}
return 0;
}
补充:getline函数
当输入到指定字符时按下回车才会停止
#include<iostream>
#include<string>
using namespace std;
int main()
{
string str;
getline(cin, str, '*');
size_t pos = str.rfind(' ');
cout << str.size() - pos - 1 << endl;
return 0;
}

四、string的模拟实现
4.1.源文件编写
#include "string.h"
namespace bit
{
//初始化静态npos变量
const size_t string::npos = -1;
//申请空间
void string::reserve(size_t n)
{
//如果n大于当前空间值
if (n > _capacity)
{
//申请n+1个空间
char* tmp = new char[n + 1];
//将原数据拷贝到新空间
strcpy(tmp, _str);
//释放旧空间
delete[] _str;
//更新指向空间
_str = tmp;
//更新空间大小
_capacity = n;
}
}
//尾插字符
void string::push_back(char ch)
{
//判断空间大小
if (_size == _capacity)
{
reserve(_capacity == 0 ? 4 : _capacity * 2);
}
//尾插字符
_str[_size] = ch;
//更新有效数据
++_size;
}
//+=字符运算符重载
string& string::operator+=(char ch)
{
//调用尾插函数
push_back(ch);
//返回对象别名
return *this;
}
//追加字符串
void string::append(const char* str)
{
//计算追加字符长度
size_t len = strlen(str);
//如果当前空间不够
if (_size + len > _capacity)
{
//大于2倍,需要多少开多少,小于2倍按2倍扩容
reserve(_size + len > 2 * _capacity ? _size + len : 2 * _capacity);
}
//将追加数据拷贝到当前空间
strcpy(_str + _size,str);
//更新有效数据
_size += len;
}
//+=字符串运算符重载
string& string::operator+=(const char* str)
{
//调用追加字符串函数
append(str);
//返回对象别名
return *this;
}
//在指定位置插入字符
void string::insert(size_t pos, char ch)
{
//判断有效值
assert(pos <= _size);
//如果当前空间不够
if (_size == _capacity)
{
//扩容
reserve(_capacity == 0 ? 4 : _capacity * 2);
}
int end = _size;
//对pos强制类型转换,避免整型提升后无法比较-1
while (end >= (int)pos)
{
_str[end + 1] = _str[end];
--end;
}
_str[pos] = ch;
++_size;
}
void string::insert(size_t pos, const char* str)
{
assert(pos <= _size);
size_t len = strlen(str);
if (len == 0)
{
return;
}
if (_size + len > _capacity)
{
reserve(_size + len > 2 * _capacity ? _size + len : 2 * _capacity);
}
//类似希尔的间隔思想
size_t end = _size + len;
while (end > pos + len - 1)
{
_str[end] = _str[end - len];
--end;
}
for (size_t i = 0; i < len; i++)
{
_str[pos + i] = str[i];
}
_size += len;
}
//删除字符串
void string::erase(size_t pos, size_t len)
{
assert(pos < _size);
if (len >= _size - pos)
{
_str[pos] = '\0';
_size = pos;
}
else
{
for (size_t i = pos + len; i < _size; i++)
{
_str[i - len] = _str[i];
}
_size -= len;
}
}
size_t string::find(char ch, size_t pos)
{
for (size_t i = pos; i < _size; i++)
{
if (_str[i] == ch)
{
return i;
}
}
return npos;
}
size_t string::find(const char* str, size_t pos)
{
assert(pos < _size);
const char* ptr = strstr(_str + pos, str);
if (ptr == nullptr)
{
return npos;
}
else
{
return ptr - str;
}
}
//获取指定位置后的字符串
string string::substr(size_t pos, size_t len)
{
//判断有效值
assert(pos < _size);
//len大小只能为有效值
if (len > _size - pos)
{
len = _size - pos;
}
//声明子串
string sub;
//开空间
sub.reserve(len);
//循环取字符
for (size_t i = 0; i < len; i++)
{
sub += _str[pos + i];
}
//返回子串
return sub;
}
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 !(s1 == s2);
}
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 (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& s)
{
for (auto ch : s)
{
out << ch;
}
return out;
}
istream& operator>>(istream& in, string& s)
{
//清除原有数据
s.clear();
const int N = 256;
char buff[N];
int i = 0;
char ch;
//in >> ch;流提取会默认设置分割符
ch = in.get();
while (ch != ' ' && ch != '\n')
{
buff[i++] = ch;
if (i == N - 1)
{
buff[i] = '\0';
s += buff;
i = 0;
}
ch = in.get();
}
if (i > 0)
{
buff[i] = '\0';
s += buff;
}
return in;
}
void test_string1()
{
string s1;
string s2("hello world");
cout << s1.c_str() << endl;
cout << s2.c_str() << endl;
for (size_t i = 0; i < s2.size(); i++)
{
s2[i] += 2;
}
cout << endl;
//范围for本质:使用迭代器
for (auto e : s2)
{
cout << e << " ";
}
cout << endl;
string::iterator it = s2.begin();
while (it != s2.end())
{
*it += 2;
cout << *it << " ";
++it;
}
cout << endl;
cout << s2.c_str() << endl;
}
void test_string2()
{
string s1("hello world");
s1 += "bye";
cout << s1.c_str() << endl;
s1 += "hello bit";
cout << s1.c_str() << endl;
s1.insert(5, 'a');
cout << s1.c_str() << endl;
}
void test_string3()
{
string s1("hello world");
s1.erase(6,3);
cout << s1.c_str() << endl;
s1.erase(2);
cout << s1.c_str() << endl;
}
void test_string4()
{
string s("test.cpp.zip");
size_t pos = s.find(".");
string suffix = s.substr(pos);
cout << suffix.c_str() << endl;
string copy(s);
cout << copy.c_str() << endl;
s = suffix;
cout << suffix.c_str() << endl;
cout << s.c_str() << endl;
s = s;
cout << s.c_str() << endl;
}
void test_string5()
{
string s1("hello world");
string s2("hello world");
cout << (s1 > s2) << endl;
}
}
int main()
{
bit::test_string5();
return 0;
}
4.2.头文件编写
#define _CRT_SECURE_NO_WARNINGS 1
#pragma once
#include <iostream>
#include <string.h>
#include <assert.h>
using namespace std;
namespace bit
{
class string
{
public:
//仿迭代器(模拟指针的行为)
//迭代器是一种封装
//屏蔽了底层容器的结构和细节
//提供了同一的类似访问容器的方式
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;
}
//默认构造函数(短小频繁调用的函数可以之间放在类中,默认为inline)
string(const char* str = "")
{
_size = strlen(str);
//_capacity不包含\0
_capacity = _size;
//+1补充\0
_str = new char[_capacity + 1];
//将数据拷贝
strcpy(_str, str);
}
//赋值运算符重载
string& operator=(const string& s)
{
//防止自己给自己赋值
if (this != &s)
{
//释放原空间
delete[] _str;
_str = new char[s._capacity + 1];
strcpy(_str, s._str);
_size = s._size;
_capacity = s._capacity;
}
return *this;
}
//析构函数
~string()
{
delete[] _str;
_str = nullptr;
_size = _capacity = 0;
}
//拷贝构造函数(深拷贝)
string(const string& s)
{
_str = new char[s._capacity + 1];
strcpy(_str, s._str);
_capacity = s._capacity;
_size = s._size;
}
//返回c格式字符串
const char* c_str() const
{
return _str;
}
//返回字符串字符个数
size_t size() const
{
return _size;
}
//清除数据
void clear()
{
_str[0] = '\0';
_size = 0;
}
//返回当前容量
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 reserve(size_t n);
void push_back(char ch);
void append(const char* str);
string& operator+=(char ch);
string& operator+=(const char* str);
void insert(size_t pos, char ch);
void insert(size_t pos, const char* str);
void erase(size_t pos, size_t len = npos);
size_t find(char ch, size_t pos = 0);
size_t find(const char* str, size_t pos = 0);
string substr(size_t pos = 0, size_t len = npos);
private:
char* _str;
size_t _size;
size_t _capacity;
const static size_t npos;
};
void test_string1();
void test_string2();
bool operator<(const string& s1, const string& s2);
bool operator<=(const string& s1, const string& s2);
bool operator>(const string& s1, const string& s2);
bool operator>=(const string& s1, const string& s2);
bool operator==(const string& s1, const string& s2);
bool operator!=(const string& s1, const string& s2);
ostream& operator<<(ostream& out, const string& s);
istream& operator>>(istream& in, string& s);
}
五、编码
文字由各种各样的符号构成
但这些符号无法直接在内存或磁盘中存储
内存和磁盘只有二进制(0/1)
编码:值和符号的映射关系
ASCII编码表:英文符号和值的映射关系

![]()


打印的过程是查编码表的过程

统一码(Unicode):万国码,将全世界所有国家的文字进行编码
统一码中,“字”的对应数字是2383,有很多方式将数字23383表示成程序中的数据
包括:UTF-8,UTF-16,UTF-32
UTF(UCS Transformation Format):统一码字符集转换格式
将统一码定义的数字转换为程序数据

char类型的basic_string,为默认的string,支持UTF8多字节的变长编码
1个字节,2个字节,3个字节或4个字节表示符号

char16_t类型的basic_string,为默认的u16string
以2个字节来表示符号

char32_t类型的basic_string,为默认的u32string
以4个字节来表示符号

宽字符类型的basic_string,为默认的wstring
用宽字节来表示符号

当有特殊需求时,需要用GPK编码方式
GPK:包含了全部中日韩文字
比如:windows中文版
#include <iostream>
using namespace std;
int main()
{
char str[] = "宵时待雨";
char16_t str16[] = u"宵时待雨";
char32_t str32[] = U"宵时待雨";
const wchar_t* wstr = L"宵时待雨";
cout << sizeof(str) << endl;
cout << sizeof(str16) << endl;
cout << sizeof(str32) << endl;
cout << sizeof(wstr) << endl;
return 0;
}

六、现代写法优化
void swap(string& s)
{
std::swap(_str, s._str);
std::swap(_size, s._size);
std::swap(_capacity, s._capacity);
}
//拷贝构造优化(使用swap算法)
string(const string& s)
{
string tmp(s._str);//普通构造
swap(tmp);
}
//赋值运算符重载优化
string& operator=(const string& s)
{
//防止自己给自己赋值
if (this != &s)
{
string tmp(s._str);
swap(tmp);
}
return *this;
}
注:
此时_str要给nullptr缺省值
tmp出作用域自动调用析构函数释放


//赋值运算符重载再优化
string& operator=(string tmp)
{
swap(tmp);
return *this;
}
注:赋值运算符重载传值不会出现无穷递归问题,所以可以直接传tmp值
七、swap函数问题
模板中的swap函数(非成员函数,无this指针):

a是对象s1的别名,b是对象s2的别名
s1会拷贝构造出一个对象c(深拷贝),然后将s2赋给s1(深拷贝),最后将c赋给s2(深拷贝)
一共调用了三次深拷贝,所以不能直接交换对象,而是通过_str,_size,capacity进行交换
可以手动实现string中的swap函数(如图所示:swap优先会调用手动实现的swap函数,而非模板
中的swap函数),所以以下两种写法都正确



八、string浅拷贝问题
析构两次:
当使用默认构造函数或赋值运算符创建新对象时
新对象和原对象中的指针会指向同一块内存
当两个对象同时离开作用域时,会调用两次析构函数
对同一块内存释放了两次
一个修改会影响到另一个:
因为两个对象共同拥有同一块内存
所以对一个对象进行修改时会影响到另一个对象
可以用引用计数和写时拷贝的方法解决析构多次的问题
计算当前有几个对象指向这块资源
g++的string结构(使用引用计数和写时拷贝):
创建一个柔性数组结构,分别存放size,capacity,和引用计数变量
最后一个变量为指向堆空间的指针,存储字符串
s1对象中的指针指向这个结构空间,此时计数为1
s2对象中的指针也指向这块空间,此时计数加1
更多推荐



所有评论(0)