【c++】string深度刨析以及实现

#pragma once
#include<iostream>
using namespace std;
#include<assert.h>
namespace bite
{
	class string
	{
	public:
		//迭代器  //像指针 底层不一定是指针 
		typedef char* iterator;
		iterator begin()
		{
			return _str;
		}
		iterator end()
		{
			return _str + _size;
		}

		//const 版本
		typedef const char* const_iterator;
		const_iterator begin() const
		{
			return _str;
		}
		const_iterator end() const
		{
			return _str + _size;
		}

		
	/*	string()
			:_str(nullptr)
			, _size(0)
			,_capacity(0)
		{
		}*/

		//string(char* str)    //常量字符串不能直接赋值
		//	:_str(new char[strlen(str) + 1])  //不是很好,strlen是在运行时,所以不适宜多调用
		//	, _size(strlen(str))
		//	,_capacity(strlen(str))
		//{
		//	strcpy(_str, str);
		//}
		// string(const char* str="\0");  可以但是实际不推荐  因为\0的后面还有\0
		//string(const char* str=nullptr)  缺省参数,strlen不能初始化为空

		const char* c_str() const
		{
			return _str;
		}
	
		//二合一
		//构造
		string(const char* str="")
			:_size(strlen(str))
		{
			_str = new char[_size+1];
			_capacity = _size;
			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=s3
		//string& 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(const string& s)
			:_str(nullptr)
		{
            //调用构造
			string stmp(s._str);
            //this和tmp进行交换
			swap(stmp);
		}

         //s1==s3
		//string& operator=(const string&ss)
		//{
		//	//调用拷贝构造
		//	string tmp(ss);
		//	swap(tmp);
		//	return *this;
		//}

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

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

		//下标+【】遍历
		size_t  size() const
		{
			return _size;
		}
		size_t capacity()  const
		{
			return _capacity;
		}
		char& operator[](size_t pos)
		{
			assert(pos < _size);
			return _str[pos];
		}
		//const 构造
		const char& operator[](size_t pos)  const
		{
			assert(pos < _size);
			return _str[pos];
		}

		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];//多开一个‘\0’
				strcpy(tmp, _str);
				delete[] _str;
				_str = tmp;
				_capacity = n;
			}
		}

		void push_back(char ch)
		{
			//扩容2倍
		/*	if (_size == _capacity)
			{
				reserve(_capacity == 0 ? 4 : _capacity * 2);
			}
			_str[_size] = ch;
			_size++;
			_str[_size] = '\0';*/

			insert(_size,ch);

			
		}

		void append(const char* str)
		{
			扩容
			//int len = strlen(str);
			//if (_size + len > _capacity)
			//{
			//	reserve(_size + len);
			//}
			//strcpy(_str + _size, str);
			//_size += len;
			insert(_size, str);
		}

		string& operator+=(char ch)
		{
			push_back(ch);
			return *this;
		}
		string& operator+=(const char* str)
		{
			append(str);
			return *this;
		}

		//pos位置插入
		void insert(size_t pos, char ch)
		{
			//等于_size就是尾插
			assert(pos <= _size);
			if (_size == _capacity)
			{
				reserve(_capacity == 0 ? 4 : _capacity * 2);
			}
			size_t end = _size+1;
			//
			//这么写头插时因为end是size_t会发生整形提升
			//while (end>=pos)
			/*while (end >= pos)

			{
				_str[end + 1] = _str[end];
				--end;
			}*/

			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];
				--end;			
			}

			strncpy(_str+pos,str,len);
			_size += len;
		}

		//pos位置删除len个字符
		void erase(size_t pos,size_t len = npos)
		{
			assert(pos < _size);
			//pos+len len处于接近阈值的时候会出现溢出的风险
			//if (len == npos || pos + len >= _size)
			//解决溢出风险
			if (len == npos || _size-pos <=len)
			{
				_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);
		}

		//查找位置 ,返回下标
		size_t find(char ch,size_t pos=0) const
		{
			assert(pos < _size);
			for (int i = pos; i < _size; ++i)
			{
				if (_str[i]==ch)
				{
					return i;
				}
			}
			return npos;
		}


		size_t find(const char*sub, size_t pos = 0) const
		{
			assert(pos < _size);
			const char* p = strstr(_str+pos, sub);

			if (pos)
			{
				return p - _str;
			}
			else
			{
				return npos;
			}
		}

		string substr(size_t pos=0, size_t len = npos)
		{

			string sub;
			//if(pos==npos||len>=_size-pos)
			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;

		}

		void clear()
		{
			_size = 0;
			_str[_size] = '\0';
		}

	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);
	}


	void swap(string& x, string& y)
	{
		x.swap(y);
	}

	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;
		//会出现死循环的情况 scanf和cin默认空格和换行做分割符无法取到
		//in >> ch;
		//get遇到一个字符娶一个字符
		ch = in.get();
		while (ch!=' '&&ch!='\n')
		{
			s += ch;
			ch = in.get();
		}
		return in;
	}


	void test_string1()
	{
		string s1("hello world");

		for (size_t i = 0; i < s1.size(); ++i)
		{
			cout << s1[i] << " ";
		}
		cout << endl;

		string s2("hello worrld");
		string::iterator it = s2.begin();
		while (it!=s2.end())
		{
			cout << *it << " ";
			++it;
		}
		cout << endl;

		for (auto ch : s2)
		{
			cout << ch << " ";
		}
		cout << endl;

		s2.push_back('s');
		for (auto ch : s2)
		{
			cout << ch << " ";
		}
		cout << endl;

		s2.append("xxxxx");
		for (auto ch : s2)
		{
			cout << ch << " ";
		}
		cout << endl; 
		
		const string s3("hello world");
		for (auto ch : s3)
		{
			cout << ch << " ";
		}
		cout << endl;

		//迭代器不一定是原生指针,typeid可以查看原生类型
		cout << typeid(std::string::iterator).name() << endl;
	}

	void test_string2()
	{
		string s1("hello world");
		s1 += 'x';
		s1 += "ssss";
		s1.insert(0,'o');
		for (auto ch : s1)
		{
			cout << ch << " ";
		}
		cout << endl;

	}

	void test_string3()
	{
		string s3("hello world");
		for (auto ch : s3)
		{
			cout << ch << " ";
		}
		cout << endl;
		s3.erase(5, 3);
		cout << s3.c_str() << endl;
	}


	void test_string4()
	{
		string s1("hello world");
		cout << s1.c_str() << endl;

		//没实现拷贝构造时
		//程序崩溃,析构的时候对同一块空间多次析构
		//发生浅拷贝,值拷贝
		string s2(s1);
		cout << s2.c_str() << endl;

		s2.insert(5, "xxxxx");
		cout << s2.c_str() << endl;
	}


	void test_string5()
	{
		string s1("hello world");
		string s2("hello world");
		cout << s1.c_str() << endl;
		cout << s2.c_str() << endl;
       //深拷贝代价:三次拷贝加一次析构 
		//swap(s1, s2);
		//s1.swap(s2);

		//全局重载
		swap(s1, s2);
		cout << s1 << endl;
		cout << s2 << endl;

		cout << (s1 == s2) << endl;


		string s4;
		string s5;
		cin >> s4 >> s5;
		cout << s4 << endl;
		cout << s5 << endl;

	}
}

     vs和g++下string结构的说明      

  注意:下述结构是在32位平台下进行验证,32位平台下指针占4个字节

vs下string的结构

       string总共占28个字节,内部结构稍微复杂一点,先是有一个联合体,联合体用来定义string中字符串的存储空间:

  • 当字符串长度小于16时,使用内部固定的字符数组来存放
  • 当字符串长度大于等于16时,从堆上开辟空间       
union _Bxty
{ // storage for small buffer or pointer to larger one
 value_type _Buf[_BUF_SIZE];
 pointer _Ptr;
 char _Alias[_BUF_SIZE]; // to permit aliasing
} _Bx;

 这种设计也是有一定道理的,大多数情况下字符串的长度都小于16,那string对象创建好之后,内 部已经有了16个字符数组的固定空间,不需要通过堆创建,效率高。

其次:还有一个size_t字段保存字符串长度一个size_t字段保存从堆上开辟空间总的容量

最后:还有一个指针做一些其他事情。
故总共占16+4+4+4=28个字节。

g++下string的结构 

G++下,string是通过写时拷贝实现的,string对象总共占4个字节,内部只包含了一个指针,该指 针将来指向一块堆空间,内部包含了如下字段:

  • 空间总大小
  • 字符串有效长度
  • 引用计数 (解决浅拷贝析构多次的问题)   有引用计数以后,先--引用技术 ,如果减完以后,引用技术==0 代表当前是最后一个管理资源的对象,那就释放。

浅拷贝的修改会影响其他对象的问题 :

检查引用计数,如果等于1,说明资源是自己独占的,不用拷贝
如果大于1,先拷贝再写,这就是写时拷贝

反正都要拷贝,意义是什么?

不是每个对象都会修改,拷贝后不修改就赚   

struct _Rep_base
{
 size_type _M_length;
 size_type _M_capacity;
 _Atomic_word _M_refcount;
};

指向堆空间的指针,用来存储字符串。 

写时拷贝

写时拷贝在读取时的缺陷 

扩展阅读

面试中string的一种正确写法 

STL中的string怎么了 

 

  • 3
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值