string使用及模拟实现

前言

一、string是什么?

string 类是basic_string 模板类的一个实例

使用char来实例化basic_string 模板类

string在底层实际是:basic_string模板类的别名,typedef basic_string<char, char_traits, allocator> string;

二、使用

1.引入库

#include<string>

2.具体使用

1.构造 拷贝构造

1直接用字符串初始化

2用另一个string 拷贝构造

3赋值构造用字符串

4赋值构造用另一个string

void test1()
{
	string s1 ( "abc");//构造
	string s2 ( s1);//拷贝构造
	string s3 = "abc";//赋值函数重载 
	string s4 = s1;//赋值函数重载 
	
}

 2增删查改

获取size capacity

size是有效元素个数,capacity是可用的空间

  string s = "123456";
    //size 大小
    s.size();
   //capacity容量
    s.capacity();


  插入
  尾插一个字符

    //尾插
    s.push_back('1');
  

尾插字符串或字符

  s += "abc";//一般用这个更便捷
    s.append("23");//加字符串


 append也可用迭代器 迭代器是一个类似指针的东西(但不一定是指针

   s.append(s.begin(), s.end());


    扩容//注意只扩容 不缩容

    s.reserve(5); 


    改变大小为5 扩充size 用’0‘填充 或减少size  不够空间就扩capacity 
  

 s.resize(5,'0');


    clear 置空 即把size置空 capacity不变

    s.clear();

访问元素

区别 对于越界的处理不同 s[10]如果越界会抛异常
  

  s.at(10);
    s[10];


    插入 在0位置插入
  

  s.insert(0, "123");

    删除 在开头删除5个字符

    s.erase(0, 5);


    替换 在0位置的后三个字符替换为"a"
    

s.replace(0, 3, "a");


    find  完全相同才算找到 //从3位置找a (3可以缺省,表示从头找到尾) 

    也可找字串

    s.find('a', 3);
    s.find("abc", 4);



   rfind  从后面开始寻找
    

s.rfind('a');


    find_first_of有一个字符就返回下标//类似strstr(s1,s2)在s1找s2中的任一个字符
  

 s.find_first_of("sga");


返回const char*   为了兼容c

   s.c_str();


    //转化

stod为string to double 将字符串转到double 其他类似

    int i=stoi("21");
    double j = stod("1");
    long double x = stold("12");
    string s = to_string(1);


    

3模拟实现(简化版)

一些比较重要的函数

构造函数

不传参时这里的str为\0  ,注意不是'/0'这是字符 , 也不是"/0" 这等效/0/0

这样可以与下面size和capacity对应为0

string(const char* str = "")
		{
			_size = strlen(str);
			_capacity = _size;
			_str = new char[_capacity + 1];//开多一个保存\0
			strcpy(_str, str);
		}

析构函数

~string()
		{
			delete[]_str;
			_str = nullptr;
			_size = _capacity = 0;
		}

swap

不使用算法库里类模板的swap直接交换是为了 减少三次构造拷贝 

void swap(string& s)
		{
			std::swap(_str, s._str);
			std::swap(_size, s._size);
			std::swap(_capacity, s._capacity);
		}

拷贝构造 直接复用构造函数

注意 比如s2(s1)  本质时s2与tmp进行交换 tmp._str指向的内容变成了s2指向的内容 如果_str没有初始化 临时变量 tmp析构时会释放未申请的空间

swap(tmp);如果_str没有初始化 临时变量 tmp析构时会释放未申请的空间
所以_str=nullptr 最好初始化一下

            

string(const string& s1)
		{
			string tmp(s1._str);
			swap(tmp);
		}

 赋值

	string& operator=(string s)
		{
			swap(s);
			return *this;
		}

 这里如果传const string &s 用传统写法

则要i判断一下s与(*this)是否相同

reverse

创建n+1 是为了保存多一个\0

void reverse(size_t n)
		{
			if (n > _capacity)
			{
				char* tmp = new char[n + 1];
				strcpy(tmp, _str);
				delete[]_str;
				_str = tmp;
				_capacity = n;
			}
		}

+=  注意点1.要记得最后加上‘\0’

                 2.扩容时要考虑一下容量为0的情况

string& operator+=(char ch)
		{
			if (_size == _capacity)
				reverse(_capacity == 0 ? 4 : 2 * _capacity);
			_str[_size] = ch;
			_size++;
			_str[_size] = '\0';
			return *this;
		}
string& operator+=(const string& s)
		{
			size_t len = strlen(s._str);
			if (len + _size > _capacity)
				reverse(len + _size);
			strcpy(_str + _size, s._str);
			_size += len;
			return *this;
		}

插入

void insert(size_t pos,char ch)//输入参数
		{
			assert(pos <= _size);
			if (_size == _capacity)
				reverse(_capacity == 0 ? 4 : 2 * _capacity);
			int end = _size;//不是size_t类型
			while (end>=(int)pos)//防止int类型的end整形提升为size_t
			{
				_str[end + 1] = _str[end];
				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)
				reverse(_size + len);
			int end = _size;
			while (end >= (int)pos)
			{
				_str[end + len] = _str[end];
				end--;
			}
			strncpy(_str + pos, str, len);
			_size += len;
		}

getline

	void getline(string& s)
	{
		s.clear();
		char buff[128];
		int i = 0;
		char ch = cin.get();
		while (ch != '\n')
		{
			buff[i++] = ch;
			if (i == 127)
			{
				buff[i] = '\0';
				s += buff;
				i = 0;
			}
			ch = cin.get();
		}
		if (i > 0)
		{
			buff[i] = '\0';
			s += buff;
		}
	}

完整版

class String
	{
	public:

		

		String(const char* str = "")//注意不是'/0'这是字符  也不是"/0"等效/0/0
			:_size(strlen(str))//strlen为0
		{
			_capacity = _size;
			_str = new char[_capacity + 1];
			strcpy(_str, str);
		}

		const char* c_str()const
		{
			return _str;
		}

		//遍历
		size_t size()const
		{
			return _size;
		}
		
		char& operator[](const size_t i)
		{
			return _str[i];
		}
		const char& operator[](const size_t i)const
		{
			return _str[i];
		}
		//旧写法
		//String(const String& s)///注意要用引用 否则无穷递归
		//{
		//	char* tmp = new char[s._capacity+1];//要加一 拷贝'\0'
		//	//_str  = new char[s._capacity+1];
		//	//直接赋值也行 因为_str原本为空
		//	strcpy(tmp, s._str);
		//	delete[]_str;
		//	_str = tmp;
		//	_size = s._size;
		//	_capacity = s._capacity;
		//}
		//新写法
		String(const String& s)
		{
			String tmp(s._str);//构造函数 
			swap(tmp);//非算法库的交换  
			//注意 s2(s1)  本质时s2与tmp进行交换 tmp._str指向的内容变成了s2指向的内容 如果_str没有初始化 临时变量 tmp析构时会释放未申请的空间
			//所以——str=nullptr 最好初始化一下
			//但vs编译器处理了
		}
		//String& operator=(String s)//s2=s1;//先拷贝构造 s 
		//{
		//	//String tmp(s);拷贝构造
		//	//swap(tmp);
		//	swap(s);//更简介
		//	return *this;
		//}
		~String()
		{
			delete[]_str;
			_str = nullptr;
			_size = _capacity = 0;
		}
		void clear()
		{
			_size = 0;
			_str[0] = '\0';
		}
		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;
		}
		void reserve(size_t n)
		{
			if (n > _capacity)//拷贝
			{
				char* tmp = new char[n + 1];//注意不是n
				strcpy(tmp, _str);
				delete[]_str;
				_str = tmp;
				_capacity = n;
			}
		}
		void push_back(char ch)//扩容2倍
		{
			if (_size == _capacity)
			{
				reserve(_capacity == 0 ? 4 : 2 * _capacity);
				_capacity = 2 * _capacity;
			}
			_str[_size++] = ch;
			_str[_size] = '\0';//注意
		}
		void append(const char* str)//扩容 根据str算
		{
			size_t len = strlen(str);
			if (_size + len > _capacity)
				reserve(_size + len);
			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 insert(size_t pos, char ch) // 这里的pos可以改成int 则检查时用pos >= 0
		{
			assert(pos <= _size);//assert(pos >= 0 && pos <= _size);
			if (_size == _capacity)
			{
				reserve(_capacity == 0 ? 4 : 2 * _capacity);//注意
				_capacity = 2 * _capacity;
			}

			//易错 如果pos是无符号整型 end是有符号整形 有符号整形会提升为无符号整形 
			//	//则end永远不会为复数
			//	//改进方法
			//	//1 将—_size强制转换为(int)
			// 改法一
			// int end = _size;
			//while (end >= (int)pos)
			//{
			//	_str[end + 1] = _str[end];
			//	end--;
			//}
			//改法二
			//end=pos+1;以end大于pos为结束条件
			int end = _size + 1;
			while (end > (int)pos)
			{
				_str[end] = _str[end - 1];
				end--;
			}
			_size++;
			_str[pos] = ch;

		}
		void insert(size_t pos, const char* str)//这里的pos可以改成int 则检查时用pos>=0
		{
			assert(pos <= _size);//assert(pos >= 0 && pos <= _size);
			int len = strlen(str);
			if (len + _size > _capacity)
			{
				reserve(len + _size);
			}

			int end = _size;
			while (end >= (int)pos)
			{
				_str[end + len] = _str[end];//将字符串拷贝到另一个字符串中间 不包括\0
				end--;
			}
			strncpy(_str + pos, str, len);
			_size += len;
		}
		void erase(size_t pos, size_t len = npos)//静态成员变量 类内声明 类外定义
		{
			/*if (len == npos || pos + len > _size)*///如果len为npos-1,pos为2会溢出
				//尽量不要加法比较
			if (len == npos || pos > _size - len)//重点
			{
				_str[pos] = '\0';
				_size = pos;
			}
			assert(pos < _size);
			//法一
			strcpy(_str + pos, _str + pos + len);
			//法二
			/*int begin = pos;
			while (begin+len<=_size)
			{
				_str[begin] = _str[begin + len];
				begin++;
			}*/
		}
		void resize(size_t n, char ch = '\0')
		{
			if (n <= _size)
			{
				_str[n] = '\0';
				_size = n;
			}
			else
			{
				reserve(n);
				while (n > _size)
				{
					_str[_size] = ch;
					_size++;
				}
				_str[_size] = '\0';
			}
		}
		String& operator=(const String&s)
		{
			char*tmp = new char[s._capacity + 1];//赋值要用临时变量暂时存储
			strcpy(tmp, s._str);
			delete[]_str;
			_str = tmp;
			_capacity = s._capacity;
			_size = s._size;
			return *this;
		}
		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
		{
			for (size_t i = pos;i < _size;i++)
			{
				if (_str[i] == ch)
					return i;
			}
			return npos;
		}
		size_t find(const char* sub, size_t pos = 0)const//寻找字串 strstr kmp
		{
			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 == npos || 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=nullptr;
		size_t _size=0;
		size_t _capacity=0;
	public:
		static const int npos;//静态成员变量 类内声明 类外定义 属于每一个对象

	};
	bool operator==(const String& s1, const String& s2) //应该放在全局函数 可以比较	
	{
		return strcmp(s1.c_str(), s2.c_str());//strcmp比较字符串 相等返回0
		//不能访问私有成员 用c_str 有隐式类型转化  可以比较 "abc"=s1
	}
	bool operator<(const String& s1, const String& s2) 
	{
		return strcmp(s1.c_str(), s2.c_str());
	}
	bool operator>(const String& s1, const String& s2)
	{
		return strcmp(s1.c_str(), s2.c_str());
	}
	void swap(String& s1, String& s2)
	{
		s1.swap(s2);
	}
	ostream& operator<<(ostream& out,const String& s) //不用友元 流插入不一定要友元 没有访问私有成员  一定是全局
	{
		//out << s.c_str();

		for (auto i : s)//已经实现迭代器 
		{
			cout << i;
		}
		return out;
	}
	//istream& operator>>(istream& in, String& s)
	//{
	//	//in>>s._str;
	//	s.clear();
	//	char ch;
	//	//in >> ch;//in读不到空格和\0
	//	cin.get(ch);//可以读字符//cin是个类
	//	while (ch != ' ' && ch != '\n')
	//	{
	//		s += ch;//
	//		//in >> ch;
	//		cin.get(ch);
	//	}
	//	return in;
	//}
	istream& operator>>(istream& in, String& s)
	{
		size_t i = 0;
		char buff[128];
		char ch = cin.get();
		while (ch != ' ' && ch != '\n')
		{
			buff[i++] = ch;
			if (i == 127)//到结尾后
			{
				buff[i] = '\n';
				s += buff;
				i = 0;
			}
		}
		if (i > 0)//i==0  buff没有有效内容了
		{
			buff[i] = '\n';
			s += buff;
		}
		return in;
	}
	void getline(istream& in, String& s)
	{
		s.clear();
		char ch;
		//in >> ch;//in读不到空格和\0
		cin.get(ch);//可以读字符//cin是个类
		while( ch != '\n')
		{
			s += ch;//
			//in >> ch;
			cin.get(ch);
		}
	}
	const int String::npos = -1;//不用static


	void test_MyString1()
	{
		String s = "1234";;
		const String s1 = "1234";
		String::const_iterator it = s1.begin();//类域名  iterator 有很多
		while (it != s1.end())
		{
			cout << *it;
			it++;
		}
		cout << endl;
		//for (auto i : s)//范围for 本质是迭代器的替换  
		//{
		//	cout << i;
		//}
	}
	//增加
	void test_MyString2()
	{
		String s = "hello";
		s.push_back('1');
		s.append("ad");

		cout << s.c_str() << endl;

		s += "134";
		s += '1';
		cout << s.c_str();

	}
	void test_MyString3()
	{
		String s = "helo";
		s.insert(0, "abc");
		s.insert(0, "abc");
		s.insert(0, "abc");
		s.insert(0, "abc");
		//s.erase(0, 3);
		cout << s.c_str();
	}
	void test_MyString4()
	{
		String s;
		s.resize(5, 'c');
		cout << s.c_str() << endl;
	}


	//深浅拷贝
	void test_MyString5()
	{
		String s1 = "hello world";//构造函数
		String s2 = s1;//等效String s2(s1);拷贝构造
		/*cout << s1.c_str() << endl;
		cout << s2.c_str() << endl;*/
		//系统自动生成的拷贝构造 拷贝的_str指向同一块空间 在析构的时候 
		//对同一块空间析构了两次
		String s3 = "hahaha";
		String s4 = "hehehe";
		cout << s3.c_str() << endl;
		//cout << s4.c_str() << endl;
		//s3 = s4;
		//cout << s3.c_str() << endl;
		//cout << s4.c_str() << endl;
		//s3 = "66666";//这里的拷贝构造 是构造函数
		cout << s3.c_str() << endl;
		//赋值函数重载 也是浅拷贝
	}

	//swap
	void test_MyString6()
	{
		String s1 = "hello world";
		String s2 = "hehe";
		cout << s1.c_str() << endl;
		cout << s2.c_str() << endl;
		swap(s1, s2);//防止勿用
		//用非成员函数重载 算法库里的swap是模板  模板和具体函数重名时 优先调用具体函数
		s1.swap(s2);
		std::swap(s1, s2);//三次拷贝//算法库里的swap
		cout << s1.c_str() << endl;
		cout << s2.c_str() << endl;
	}

	//find
	void test_MyString7()
	{
		string s1 = "hello world";
		string s2 = "world";
		size_t pos1 = s1.find(s2);
		if (pos1 != String::npos)
			cout << pos1 << endl;;
		size_t pos2 = s1.find('w');
		if (pos2 != String::npos)
			cout << pos2 << endl;;

	}

	//substr 
	void test_MyString8()
	{
		String s1 = "hello world";
		String s2 = s1.substr(6, 5);
		cout << s2.c_str() << endl;
	}

	//==
	void test_MyString9()
	{
		String s1 = "hello world";
		String s2 = s1;
		cout << (s1==s2)<< endl;
	}
	//<< >>
	void test_MyString10()
	{
		String s1 = "hello world";
		String s2;
		cout << s1 << endl;
		cin >> s1;
		cout << s1;
	}

}


评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值