【cpp--->string】

本文详细介绍了STL中的string容器,包括其编码表、接口用法如构造、容量调整、数据修改、访问数据、字符串运算等,并提供了相关解题示例。此外,还探讨了string的模拟实现,包括插入、删除、扩容等操作的难点和代码实现,以及在实现中可能遇到的问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、stl

1.stl是什么

stl是标准库中的重要组成部分,stl是美国惠普实验室完成的初始版本,也就是HP版本是开源的。
后来Plauger继承HP版本开发PJ版本,被Windows VS的C++采用;SGI版本;
由Silicon Graphics Computer Systems,Inc公司开发,继承自HP版 本。被GCC(Linux)采用,可移植性好,可公开、修改甚至贩卖,从命名风格和编程 风格上看,阅读性非常高

2.stl的六大组件

在这里插入图片描述

二、string容器

1.编码表

string是一个模板类,string是被typedef basis_string出来类型,除了char类型还有char16_t和char32_t以及wchar_t类型,这些类型所代表的含义是它们所支持的编码表不同,char支持UTF-8编码表,char16_t支持UTF-16编码表,char32_t支持UTF-32编码表;

因为计算机只能存储二进制,编码表起初是美国为了在计算机中显示字母和标点符号,规定的编码表,这个表是一个映射表,一个将字母和数字映射到计算机中的表,这个表叫ascll码表,编码方式只支持1字节编码;

美国人制定的表只能映射美国的字母和标点符号,其他国家为了更好的使用计算机,也需要自己的编码表,于是就出现了万国码表,就是UTF-8,UTF-8支持所有国家文字的编码,而且编码方式是可变的,字母支持1字节编码,其他字符可能会按照2字节,4字节等编码;而UTF-16只能支持2字节,UTF-32只能支持4字节。

2.string接口用法

2.1构造接口

//使用常量字符串指针构造
	string s1("hello world");
	//单参数隐式类型转换
	string s2 = "hello world";
	//用常量字符串前n个字符构造s7
	string s7("hello world", 3);
	//拷贝构造
	string s3(s2);
	//用s1的下标为6的字符开始,往后面3个字符构造s5
	string s5(s1, 6, 3);
	//不传参第三个参数,默认是npos,npos是一个无符号整数最大值,
	//n如果大于s1.size()则默认将s1数据拷贝完。
	string s4(s1, 3);
	//用6个c构造s6
	string s6(6, 'c');

在这里插入图片描述

2.2 容量调整接口

1.reserve()扩充容器的容量

string s1;
	//扩容前空间大小
	cout << "capacity:" << s1.capacity() << endl;
	s1.reserve(100);
	//扩容后空间大小
	cout << "capacity:" << s1.capacity() << endl;

在这里插入图片描述
2.string不同版本扩容机制
PJ版本的string的内部是有4个成员变量int size、char buffer[16],int capacity,char*str,如果要存储的字符串不超过15个字符,那么数据会先存储在buffer[16]中,而不用扩容开辟空间可以提高插入效率;后边数据增多扩容是按照1.5倍扩容的。而Linux下的SGI版本没有buffer[],是按照2倍扩容的。

下面一组代码可测试初不同版本下的string扩容方案

int main()
{
	string s1;
	size_t N = 100;
	//记录容器容量
	size_t capacity = s1.capacity();
	//打印第一次容器容量
	cout << "capacity:" << capacity << endl;
	for (int i = 0; i < N; i++)
	{
		//插入数据
		s1.push_back('a');
		//随着数据怎长,扩容后capacity将!=容器容量
		if (capacity != s1.capacity())
		{
			//更新容器容量
			capacity = s1.capacity();
			//打印更新后的容器容量
			cout << "capacity:" << capacity << endl;
		}
	}
	return 0;
}

在这里插入图片描述
为了提高效率我们在知道要用多少空间的情况下可以提前将空间一步到位。

#include<iostream>
#include<string>
using namespace std;

int main()
{
	string s1;
	size_t N = 100;
	一次性扩容
	s1.reserve(100);
	//记录容器容量
	size_t capacity = s1.capacity();
	//打印第一次容器容量
	cout << "capacity:" << capacity << endl;
	for (int i = 0; i < N; i++)
	{
		//插入数据
		s1.push_back('a');
		//随着数据怎长,扩容后capacity将!=容器容量
		if (capacity != s1.capacity())
		{
			//更新容器容量
			capacity = s1.capacity();
			//打印更新后的容器容量
			cout << "capacity:" << capacity << endl;
		}
	}
	return 0;
}

在这里插入图片描述
3.resize()调整数据长度

string s1;
	cout << "size:" << s1.size() << endl;
	//增加并初始化数据
	s1.resize(10, 'c');
	cout << "size:" << s1.size() << endl;
	//第二个参数可以省略
	s1.resize(10);
	//也可以删除数据
	s1.resize(5);

在这里插入图片描述
4.其他接口
判空和清空,以及缩容接口

#include<iostream>
#include<string>
using namespace std;

int main()
{
	string s1;
	s1.resize(10, 'c');
	//判空
	cout << "判空"<<s1.empty() << endl;
	//清空
	s1.clear();
	cout << "判空" << s1.empty() << endl;
	
	//缩容
	s1.resize(10, 'c');
	s1.reserve(20);
	cout << "size:" << s1.size() << "capacity:" << s1.capacity() << endl;
	s1.shrink_to_fit();
	cout << "size:" << s1.size() << "capacity:" << s1.capacity() << endl;
	return 0;
}

在这里插入图片描述

2.3数据修改接口

1.尾插接口

#include<iostream>
#include<string>
using namespace std;

int main()
{
	string s1,s2;
	//插入单个字符
	s1.push_back('c');
	//插入一个字符串
	s1.append(" hello world");
	//简洁使用方法,+=操作符重载可同时满足插入单个字符和字符串
	s2 += 'c';
	s2 += "hello world";
	return 0;
}

在这里插入图片描述
2.任意位置插入任意字符
任意位置插入牵扯到移动数据,效率不高,应该避免使用

int main()
{
	//这里的位置都是按照下标从0开始算的

	//插入字符串返回的是string&对象
	string s1("hello");
	string s2(" world");
	//在对象的pos位置插入一个对象的数据
	s1.insert(2 , s2);
	//在对象的pos位置插入一个对象的pos位置开始的完后n个字符
	s1.insert(s1.size(), s2, 2, s2.size() - 2);
	//在对象的pos位置插入一个常量字符串
	s1.insert(2, "a");
	//在对象的pos位置插入常量字符串的前n个字符。
	s1.insert(2, "aaaa", 2);
	//在对象的pos位置插入n个字符
	s2.insert(2, 3, 'a');

	//用迭代器插入,返回迭代器
	//在对象指定迭代器位置插入1个字符
	s2.insert(s2.begin() + 2, 'c');
	//在对象指定迭代器位置插入n个字符
	s2.insert(s2.begin() + 2, 3,'cccc');
	
	return 0;
}

3.任意位置删除任意字符
任意位置删除牵扯到移动数据,效率不高,应该避免使用

int main()
{
	//这里的位置都是按照下标从0开始算的
	string s1("hello");
	string s2(" world");
	//删除字符串下标位置返回的是string&对象
	//删除下标pos位置的n个字符
	s1.erase(2, 2);

	//删除迭代器位置返回的是迭代器
	//删除迭代器pos位置的字符
	s1.erase(s1.begin()+2);
	//删除迭代器区间的字符,右闭左开
	s2.erase(s2.begin(), s2.end() - 2);
	
	return 0;

}

4.替换任意位置的任意字符

int main()
{
	//这里的位置都是按照下标从0开始算的
	string s1("hello world");
	string s2("aaa");
	//将pos位置开始的n个字符替换为一个常量字符串
	s1.replace(5, 2, "%%d");

	//将pos位置开始的n个字符替换为一个对象
	s1.replace(5, 2, s2);

	//将pos位置开始的n个字符替换为一个对象的前n个字符
	s1.replace(5, 2, s2,1);
	return 0;
}

5.交换接口
string内部的交换接口交换的是string属性的指针,而不是深拷贝,节省了交换成本,如果使用算法中的交换,需要多开一个临时对象,然后使用赋值构造深拷贝对象的空间。

int main()
{
	string s1("hello worllld i love you");
	string s2("hello");
	s1.swap(s2);
	return 0;

} 

2.4 访问数据接口

1.[]操作符重载
有检查下标越界的功能,返回指定位置的字符,普通接口返回值可修改,const接口不可修改。

#include<iostream>
#include<string>
using namespace std;
void func(const string& s)
{
	//const[]重载,返回指定位置的字符,不可修改
	//编译器自动识别匹配const[]重载接口
	for (int i = 0; i < s.size(); i++)
	{
		cout << s[i];
	}
	cout << endl;
}
int main()
{
	string s1("hello world");
	//普通[]重载,返回指定位置的字符,可修改
	for (int i = 0; i < s1.size(); i++)
	{
		cout << ++s1[i];
	}
	cout << endl;

	
	return 0;

}

2.at()返回指定位置的字符
at同[]操作符重载功能类似,at同样有const接口,不同的是[]小标访问越界会断言报错,at则会抛异常。

#include<iostream>
#include<string>
using namespace std;

int main()
{
	string s1("hello world");
	//普通[]重载,返回指定位置的字符,可修改
	for (int i = 0; i < s1.size(); i++)
	{
		cout << ++s1.at(i);
	}
	cout << endl;
	//越界访问
	s1.at(100);
	s1[100];
	return 0;

}

在这里插入图片描述

2.5 字符串运算接口

1.字符查询

#include<iostream>
#include<string>
using namespace std;

int main()
{
	string s1("hello worllld i love you");
	string s2("hello");

	//从s1的pos位置开始查找字符串常量,返回字符串的下标位置
	cout << s1[s1.find("hello", 0)] << endl;

	//从pos位置查找字符串常量的前n个字符。
	size_t pos=s1.find("lllll", 0,3);
	cout << pos << endl;

	//从s1的pos位置开始查找对象,返回对象字符串的下标位置
	s1.find(s2, 0);

	//从s1的pos位置开始查找对象,返回对象字符串的下标位置
	s1.find('c', 0);

	//从后往前查找,默认位置是npos,用来查找对象和查找方式与find相同
	cout << s1.rfind('l', 1) <<endl ;

	//从对象的pos位置往后匹配字符串中所有字符,返回pos
	size_t pos=s1.find_first_of("h ",0);
	if(pos != string::npos)
	{
		s1.replace(pos, 1, "%20");
	}

	//从pos位置往后匹配非字符串中的字符
	size_t pos2=s2.find_first_not_of("he", 0);
	if(pos2 != string::npos)
	{
		s2.replace(pos2, 1, "%20");
	}


	//从对象的pos位置往前匹配字符串中所有字符,返回pos
	size_t pos = s1.find_last_of("h ", s1.size());
	if (pos != string::npos)
	{
		s1.replace(pos, 1, "%20");
	}

	//从对象的pos位置往前匹配不是字符串中所有字符,返回pos
	size_t pos = s1.find_last_not_of("h ", s1.size());
	if (pos != string::npos)
	{
		s1.replace(pos, 1, "%20");
	}
	return 0;
}

2.字符串比较

int main()
{
	string s1("hello worllld i love you");
	string s2("hello");

	//string对象与string对象比较
	cout << (s1 < s2) << endl;
	cout << (s1 != s2) << endl;
	cout << (s1 == s2) << endl;
	//string对象与字符串比较
	cout << (s1 > "hello") << endl;

	return 0;

} 

3.提取字符串子串

int main()
{
	string s1("hello worllld i love you");
	string s2("hello");

	//返回一个从对象pos位置截取的len长度的子串string对象
	cout << s1.substr(6, 3)<< endl;

	return 0;

} 

4.find应用查找并替换字符串中的字符

#include<iostream>
#include<string>
using namespace std;

int main()
{
	//将s1中的空格替换成%20
	string s1("hello world i love you");

	//方法一
	size_t pos = s1.find(" ");
	//统计" "的数量,准备一次性开空间使用
	int num = 0;
	for (auto e : s1)
	{
		if (e == ' ')
			num++;
	}
	//一次性将替换后的空间开好,避免替换的时候重复开空间操作
	s1.reserve(s1.size() + num*2);
	while (pos != string::npos)
	{
		//替换
		s1.replace(pos, 1, "%20");
		//更新pos位置
		pos = s1.find(" ",pos+3);
	}
	for (auto e : s1)
	{
		cout << e;
	}
	cout << endl;


	//方法二
	int num = 0;
	for (auto e : s1)
	{
		if (e == ' ')
			num++;
	}
	//一次性将替换后的空间开好,避免替换的时候重复开空间操作
	s1.reserve(s1.size() + num * 2);

	//开一个新的string对象
	string newobject;
	//遍历查找s1中的" "
	for (auto e : s1)
	{
		//判断如果是" "就+="%20",如果不是就+=s1的字符
		if (e != ' ')
		{
			newobject += e;
		}
		else
		{
			newobject += "%20";
		}
	}
	for (auto e : newobject)
	{
		cout << e;
	}
	cout << endl;
	return 0;

}

2.6 迭代器

对于const对象不能写数据,所以要添加const迭代器适配const对象,const限制的是对象内部的数据修改,不是迭代器。迭代器有正向迭代器和反向迭代器,正向迭代器的头是反向迭代器的尾。

#include<iostream>
#include<string>
using namespace std;

int main()
{
	string s1("hello world");
	//普通迭代器,从前往后遍历
	string::iterator it1 = s1.begin();
	while (it1 != s1.end())
	{
		cout <<++(*it1);
		it1++;
	}
	cout << endl;

	string s2("hello world");
	//反向迭代器,从后往前遍历
	string::reverse_iterator it2 = s2.rbegin();
	while (it2 != s2.rend())
	{
		cout << ++(*it2);
		it2++;
	}
	cout << endl;

	//const正向迭代器,it的值不能被修改
	string::const_iterator it3 = s1.begin();
	while (it3 != s1.end())
	{
		cout << *it3;
		it3++;
	}
	cout << endl;

	//const正向迭代器,it的值不能被修改
	string::const_reverse_iterator it4 = s2.rbegin();
	while (it4 != s2.rend())
	{
		cout << *it4;
		it4++;
	}
	cout << endl;
	return 0;

}

在这里插入图片描述

2.7接口使用方法总结

在这里插入图片描述

3.string接口解题应用

1. 翻转字符串

力扣链接
从字符串两段开始找是字母的字符,两端都找到了就交换头尾,然后更新头尾位置,找下一个是字母的字符,直到头尾相遇。主要考察string的遍历

class Solution {
public:
    //判断是否为大小写字母
    bool isLetter(const char c)
    {
        if((c>='a'&&c<='z')||(c>='A'&&c<='Z'))
        {
            return true;
        }
        return false;
    }
    string reverseOnlyLetters(string s) {
        //记录字符串两端是字母的字符
        int begin=0,end=s.size()-1;
        while(begin<end)
        {
            //从头部找是字母的字符
            while((begin<end)&&!isLetter(s[begin]))
            {
                ++begin;
            }
            //从尾部找是字母的字符
            while((begin<end)&&!isLetter(s[end]))
            {
                --end;
            }
            //交换连个字符
            swap(s[begin],s[end]);
            //更新头尾的位置
            ++begin;
            --end;
        }
        return s;
    }
};

2. 找字符串中第一个只出现一次的字符

力扣链接
遍历字符串将字符串映射到哈希表中,然后再遍历字符串找出第一个字母映射的哈希表是1的字母返回。主要考察string的遍历

class Solution {
public:
    int firstUniqChar(string s) {
        int stringHash[26]={0};
        //映射
        for(auto e:s)
        {
            stringHash[e-'a']++;
        }
        //判断
        for(int i=0;i<s.size();i++)
        {
            if(stringHash[s[i]-'a']==1)
            {
                return i;
            }
        }
        return -1;
    }
};

3.字符串里面最后一个单词的长度

牛客链接
反向查找字符串的中的第一个空格,如果没有空格就输出真个字符串的长度,如果有空格输出字符串长度减去pos-1.

#include <iostream>
#include<string>
using namespace std;

int main() {
    string str;
    getline(cin, str);
    //反向查找字符串的中的第一个空格
    size_t pos = str.rfind(' ');
    //输出长度
    if (pos != string::npos) {
        cout << str.size() - pos - 1 << endl;
    } else {
        cout <<str.size()<< endl;
    }
    return 0;
}

4.字符数字相加

力扣链接
从个位也就是字符串末尾开始自己算,计算前转化字符数字为整型,遍历num1和num2将计算结果依次转化成字符型保存,最终返回。

class Solution {
public:
    string addStrings(string num1, string num2) {
        //定义结果接收变量,进位值接收变量,计算位下标接变量
        string result;
        int next=0;
        int end1=num1.size()-1;
        int end2=num2.size()-1;
        //遍历计算num1和num2
        while(end1>=0||end2>=0)
        {
            //转化并记录num1和num2的末尾
            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;
            //保存结果
            result+=(ret+'0');
            //更新计算位
            end1--;
            end2--;
        }
        //判断是否还有进位
        if(next==1)
        {
            result+='1';
        }
        //翻转字符串
        reverse(result.begin(),result.end());
        return result;
    }
};

三、string模拟实现

1.实现难点

1.1任意位置插入和删除

任意位置插入需要检查访问越界的问题,考虑扩容,主要是数据移动。数据移动可以将尾插和其他位置一并考虑,移动的时候从\0的位置开始移动,直到将pos位置开始加上插入字符串的长度的空间腾空位置,然后从pos位置开始将要插入的字符串拷贝过来,需要注意的两点:1.拷贝的字符串不能包含\0,不能会截断\0后面的数据;2.在移动过程中pos是无符号整型,如果将end结束条件为end<pos,并且pos为0时会造成死循环,因为end为0时再减1会成为npos,所以end的结束条件要设为>pos。

任意位置删除,可以复用strncpy拷贝pos+要删除的数据的长度位置开始往后的所有数据包含\0覆盖要删除的字符区域。

1.2扩容空间和扩容字符长度

扩容空间分为两种情况考虑,一种是空间大小小于或者等于原始空间大小不处理,其次是大于,需要重新开辟空间拷贝原始数据到新空间,释放老空间,更新空间地址,字符长度以及空间大小。

扩容字符长度需要考虑要扩容的空间大小是否大于原始空间大小,大于则还要考虑是否大于空间容量,大于则扩容,然后将多出来的空间初始化为传参的字符或者默认\0;小于字符长度则直接更新字符长度,最后给字符末尾加\0.

1.3构造

无参和字符串指针参数构造可以合二为一,如果传参默认是空值str的初始值不能为nullptr,因为str被cout输出时会解引用指针,发生空指针的解引用行为。所以至少要开辟一个空间出来赋值\0.

1.4流插入和流提取重载

流插入需要注意的是要用到ostream库函数实现的类,ostream对象在左string对象在右,所以必须是全局函数。

流提取是不会接收空格和回车字符的,所以要用到istream类中的get成员函数接收所有包括空格和回车的字符,然后筛选,遇到空格或者回车就结束插入到string对象中。为了避免频繁调用insert可以用一个buffer缓冲区,接收完字符一次性插入到string对象中。

1.5开辟空间

无论什么时候在开辟空间的时候都必须多开一个空间,因为要存储\0.

2.代码实现

#pragma once
#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
#include<string.h>
#include<assert.h>
using namespace std;
namespace kk
{
	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;
		}
		//构造
		string(const char* str = "")
			:_size(strlen(str))
		{
			_capacity = _size==0?4:_size;
			_str = new char[_capacity + 1];
			//将str的数据拷贝给_str;
			strcpy(_str, str);
		}
		//拷贝构造
		string(const string& s)
			:_size(s._size)
			, _capacity(s._capacity)
		{
			//开空间
			_str = new char[_capacity + 1];
			//拷贝数据
			strcpy(_str, s._str);
		}
		//析构
		~string()
		{
			delete[] _str;
			_str = nullptr;
			_size = _capacity = 0;
		}
		//扩容
		void reserve(size_t n)
		{
			//只处理n>_capacity的情况
			if (n > _capacity)
			{
				//重新开空间
				char* tmp = new char[n + 1];
				//拷贝数据至新空间
				strcpy(tmp, _str);
				//释放老空间
				delete[] _str;
				//更新空间地址
				_str = tmp;
				//更新_capacity
				_capacity = n;
			}
		}
		
		//头插
		void push_back(const char& ch)
		{
			insert(_size, ch);
		}
		void append(const char* str)
		{
			insert(_size, str);
		}
		//[]重载
		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 resize(size_t n, char ch='\0')
		{
			//n大于size的情况
			if (n > _size)
			{
				//n大于_capacity的情况
				if (n > _capacity)
				{
					reserve(n);
				}
				//初始化多出来的空间
				for (size_t i = _size; i < n; i++)
				{
					_str[i] = ch;
				}
				_str[n] = 0;
				//更新_size
				_size = n;
			}
			//n小于size的情况
			else if(n<_size)
			{
				//更新size
				_size = n;
				_str[_size] = 0;
			}
		}
	
		//尾删
		void pop_back()
		{
			assert(_size > 0);
			erase(_size-1, 1);
		}
		//任意位置插入
		string& insert(size_t pos, char ch)
		{
			//断言pos的位置
			assert(pos <= _size);
			//扩容
			if (_size + 1 > _capacity)
			{
				reserve(_capacity * 2);
			}
			//从size挪动数据知道空出pos
			size_t end = _size + 1;
			while (end > pos)
			{
				_str[end] = _str[end - 1];
				end--;
			}
			//插入数据
			_str[pos] = ch;
			//更新size
			_size++;
			return *this;
		}
		string& insert(size_t pos, const char* str)
		{
			//断言pos的位置
			assert(pos <= _size);
			size_t len = strlen(str);
			//扩容
			if (_size + len > _capacity)
			{
				reserve(_size+len);
			}
			//从size挪动数据知道空出pos
			size_t end = _size + len;
			while (end > pos+len-1)
			{
				_str[end] = _str[end - len];
				end--;
			}
			//插入数据,这里不能将str的\0拷贝,会截断后面的数据。
			strncpy(_str + pos, str,len);
			//更新size
			_size+=len;
			return *this;
		}
		//任意位置删除
		string& erase(size_t pos, size_t len=npos)
		{
			//断言pos位置
			assert(pos < _size);
			//pos+len大于_size,这里不能胜len==npos的判断,要把这个判断放在最前面,不然会溢出崩溃
			if (len == npos||pos + len > _size)
			{
				_str[pos] = 0;
				_size = pos;
			}
			//pos+len小于_size
			else
			{
				//直接用pos+len后边的数据拷贝覆盖删除的数据
				strcpy(_str + pos, _str + pos + len);
				//更新_size
				_size -= len;
			}
			return *this;

		}
		//赋值重载
		string& operator=(const string& s) 
		{
			//自己给自己赋值不用处理
			if (this != &s)
			{
				//可能s的空间大于或者小于this的空间,这时候都需要重新开空间
				char* tmp = new char[s._capacity+1];
				//拷贝s对象的数据到this对象
				strcpy(tmp, s._str);
				//释放this的空间
				delete[] _str;
				//更新this的空间地址为tmp
				_str = tmp;
				//更新size和capacity
				_size = s._size;
				_capacity = s._capacity;
				return *this;
			}
		}
		string& operator=(char ch)
		{
			//自己给自己赋值不用处理
				//可能s的空间大于或者小于this的空间,这时候都需要重新开空间
				char* tmp = new char(ch);
				//释放this的空间
				delete[] _str;
				//更新this的空间地址为tmp
				_str = tmp;
				//更新size和capacity
				_size = 1;
				_capacity = 1;
				return *this;
		}
		//交换
		void swap(string& s)
		{
			std::swap(_str, s._str);
			std::swap(_size, s._size);
			std::swap(_capacity, s._capacity);

		}
		//+=重载
		string& operator+=(char ch)
		{
			push_back(ch);
			return *this;
		}
		string& operator+=(const string& s)
		{
			append(s._str);
			return *this;
		}
		//+重载
		string operator+(const string& s)
		{
			string tmp(*this);
			tmp += s;
			return tmp;
		}
		string operator+(char ch)
		{
			string tmp(*this);
			tmp += ch;
			return tmp;
		}

		//逻辑运算重载	
		bool operator==(const string& s)
		{
			return strcmp(_str, s._str) == 0;
		}
		bool operator!=(const string& s)
		{
			return !(*this == s);
		}
		bool operator>(const string& s)
		{
			return strcmp(_str, s._str) > 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);
		}
		//查找
		size_t  find(char ch, size_t pos = 0)
		{
			assert(_size != 0);
			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)
		{
			assert(pos < _size);
			char* ret = strstr(_str+pos, str);
			if (ret)
			{
				return ret-_str;
			}
			return npos;
		}

		const size_t size()const
		{
			return _size;
		}
		const size_t capacity()const
		{
			return _capacity;
		}
		const char* c_str()const
		{
			return _str;
		}
		static const size_t npos;
	private:
		char* _str;
		size_t _size;
		size_t _capacity;
	};
	const size_t string::npos = -1;
	
	ostream& operator<<(ostream& out, const string& s)
	{
		for (auto e : s)
		{
			out << e;
		}
		return out;
	}
	istream& operator>>(istream& in, string& s)
	{
		//接收用户输入
		char ch = in.get();
		//缓冲区
		char buffer[128];
		int index = 0;
		//循环接收用户输入
		while (ch != ' ' && ch != '\n')
		{
			buffer[index++] = ch;
			//如果buffer满了就插入到s中去
			if (index == 127)
			{
				buffer[index] = 0;
				s += buffer;
				//让buffer从头开始存储
				index = 0;
			}
			ch = in.get();
		}
		//如果index不等于0说明buffer中还有数据
		if (index != 0)
		{
			buffer[index] = 0;
			s += buffer;
		}
		return in;
	}
	//构造测试
	void test_string()
	{
		string s1("");
		string s2("s1");
		string s3(s2);
		cout << s1.c_str() << endl;
		cout << s2.c_str() << endl;
		cout << s3.c_str() << endl;
		
	}
	//任意位置插入和删除测试
	void test_string2()
	{
		string s1("kangxinjian");
		s1.insert(4, " ");
		s1.insert(8, ' ');
		s1.insert(0, '!');
		s1.insert(14, "!");
		cout << s1.c_str() << endl;
		s1.erase(0, 1);
		cout << s1.c_str() << endl;
		s1.erase(4, 1);
		cout << s1.c_str() << endl;
		s1.erase(7, 1);
		cout << s1.c_str() << endl;
		s1.erase(11, 1);
		cout << s1.c_str() << endl;

		s1.erase(4, 3);
		cout << s1.c_str() << endl;
		s1.erase(4, 4);
		cout << s1.c_str() << endl;
		s1.erase(0, 3);
		cout << s1.c_str() << endl;
	}
	//+=、=测试
	void test_string3()
	{
		string s1("kangxinjian");
		string s2, s3;
		s3 += ' ';
		s3 += "kang xin jian";
		s3 += s1;
		cout << s3 << endl;


		string s4("kangxinjian");	
		s2 = s4;
		cout << s2 << endl;
		s2 = 'c';
		cout << s2 << endl;
	}
	//查询测试
	void test_string4()
	{

		string s4("kangxinjian");
		size_t pos = s4.find("a");
		while (pos != kk::string::npos)
		{
			cout << "i:" << pos << ": " << "icontent: " << s4[pos] << endl;
			pos = s4.find("a", pos + 1);
		}
		cout << endl;
	}
	//迭代器+[]测试
	void test_string5()
	{

		string s4("hello world");
		string::iterator it1 = s4.begin();
		while (it1 != s4.end())
		{
			cout << ++(*it1++);
		}
		cout << endl;

		string s5("hello world");
		string::const_iterator it2 = s5.begin();
		while (it2 != s5.end())
		{
			cout << (*it2++);
		}
		cout << endl;

		for (auto e : s5)
		{
			cout << e;
		}
		cout << endl;
	}
}

3.容易出错的地方

出错点


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值