C++string的模拟实现

文章目录

  • 一、string 的模拟实现
    • 1.1 深浅拷贝问题
    • 1.2 四种默认函数的实现
    • 1.3 string类对象的容量操作
    • 1.4 string类对象的访问及遍历操作
    • 1.5 string类对象的修改操作
    • 1.6 string类非成员函数


一、string 的模拟实现

1.1 深浅拷贝问题

浅拷贝: 也称位拷贝,编译器只是将对象中的值拷贝过来。如果对象中管理资源,最后就会导致多个对象共享同一份资源,当一个对象销毁时就会将该资源释放掉,而此时另一些对象不知道该资源已经被释放,以为还有效,所以 当继续对资源进项操作时,就会发生发生了访问违规。要解决浅拷贝问题,C++中引入了深拷贝。

C++string的模拟实现_第1张图片

C++默认生成的拷贝构造函数和赋值运算符重载是浅拷贝,所以需要我们自己实现。

1.2 四种默认函数的实现

创建一个类,就需要考虑对应的默认函数,是否能够满足需求

string类的私有成员变量

	private:
		char* _str;
		size_t  _size;      //字符串当前有效字符个数
		size_t  _capacity;  //最大能存储的有效字符个数,不包含\0

构造函数和析构函数

		//默认构造函数
		//string(const char* str = nullptr) 错误示范  --- 不能初始化为空指针,空指针无法访问,而空字符串是可以打印的,当调用c_str的时候返回空指针,再调用就会出错
		string(const char* str = "")
			: _size(strlen(str))
			, _capacity(_size)
		{
			_str = new char[_size + 1];
			strcpy(_str, str);
		}
		string(size_t n, char c)
			:_size(n)
		{
			_str = new char[_size + 1];
			memset(_str, c, n);
			_capacity = _size;
		}
		~string()
		{
			delete[] _str;
			_str = nullptr;
			_size = 0;
			_capacity = 0;
		}

设计深浅拷贝的拷贝构造函数和赋值运算符重载函数

有两种写法

  1. 传统写法
//传统写法
		//拷贝构造函数
		string(const string& s)
			:_size(s._size)
			, _capacity(s._capacity)
			
		{
			_str = new char[_capacity + 1];
			strcpy(_str, s._str);
		}
		//赋值运算符重载
		string& operator=(const string& s)
		{
			if (this != &s)
			{
				//不是自己给自己赋值
				//防止空间开辟失败,先用个临时变量
				char* tmp = new char[strlen(s._str) + 1];

				strcpy(tmp, s._str);
				delete[] _str;
				_str = tmp;
				_size = s._size;
				_capacity = s._capacity;
			}
			return *this;
		}
  1. 现代写法
		//重载专属string的交换函数,标准库string里面也有
		void swap(string& s)
		{
			std::swap(_size, s._size);
			std::swap(_str, s._str);
			std::swap(_capacity, s._capacity);
		}
		//现代写法,交给其他函数完成
		string(const string& s)
			:_str(nullptr)   //让指针初始化指向空
			,_size(0)
			,_capacity(0)
		{
			string tmp(s._str);
			swap(tmp);
		}
		string& operator=(string tmp)//tmp就是一个拷贝(拷贝构造)
		{
			//不需要判断是否给自己赋值
			//其实这样也无法判断,tmp本身就是一个拷贝,它们的_str值一定不一样
			swap(tmp);
			return *this;
		}

1.3 string类对象的容量操作

		//返回当前有效字符个数
		size_t size() const
		{
			return _size;
		}
		//返回最多能存储的有效字符个数
		size_t capacity() const
		{
			return _capacity;
		}
		bool empty()
		{
			if (_size == 0)
				return true;
			else
				return false;
		}
		//清空字符串
		void clear()
		{
			_size = 0;
			_str[0] = '\0';
		}
		//为字符串预留空间
		void reserve(size_t n)
		{
			if (n > _capacity)
			{
				//增容
				char* tmp = new char[n + 1];
				strcpy(tmp, _str);
				delete[] _str;
				_str = tmp;
				_capacity = n;
			}
		}
		//将有效字符的个数该成n个,多出的空间用字符c填充
		void resize(size_t n, char ch = '\0')
		{
			if (n <= _size)
			{
				//缩小字符串,不填充字符
				_size = n;
				_str[_size] = '\0';
			}
			else
			{
				if (n > _capacity)
				{
					//不够,要扩容
					reserve(n);
				}
				memset(_str + _size, ch, n - _size);
				_size = n;
				_str[_size] = '\0';
			}
		}

1.4 string类对象的访问及遍历操作

		typedef char* iterator;
		typedef const char* const_iterator;
		//返回pos位置的字符,const string类对象调用
		char& operator[](size_t pos)
		{
			assert( pos < _size);
			return _str[pos];
		}
		iterator begin()
		{
			return _str;
		}
		iterator end()
		{
			return _str + _size;
		}
		const_iterator begin() const
		{
			return _str;
		}
		const_iterator end() const
		{
			return _str + _size;
		}

C++11采用的范围for,本质是调用了,迭代器,当迭代器实现之后就可以使用了。但是begin,end函数名不能更改,否则无法识别。

1.5 string类对象的修改操作

	//定义私有变量
	static const size_t npos  ;

类外定义

const size_t string::npos = -1;
		//返回C字符串
		const char* c_str() const
		{
			return _str;
		}
		//在字符串后尾插字符c
		void push_back(char ch)
		{
			//判断是否需要扩容
			if (_size == _capacity)
			{
				reserve(_capacity == 0 ? 4 : 2 * _capacity);
			}
			_str[_size++] = ch;
			_str[_size] = '\0';
		}

		//在字符串后追加一个字符串
		void append(const char* str)
		{
			size_t len = strlen(str);
			//判断是否需要扩容
			if (_size + len > _capacity)
			{
				reserve(_capacity == 0 ? 4 : 2 * _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;
		}

		//从字符串pos位置开始往后找字符c,返回该字符在字符串中的位置,找不到返回npos
		size_t find(char ch, size_t pos = 0)
		{
			for (size_t i = pos; i < _size; ++i)
			{
				if (_str[i] == ch)
					return i;
			}
			return npos;
		}
		size_t find(const char* s, size_t pos = 0)
		{
			//直接使用C语言库函数查找
			const char* tmp = strstr(_str + pos, s);
			if (tmp == nullptr)
			{
				return npos;
			}
			else
			{
				return tmp - _str;
			}
		}
		//在str中从pos位置开始,截取n个字符,然后将其返回
		string substr(size_t pos = 0, size_t len = npos) const
		{
			string tmp(*this);
			size_t tlen = len > _size ? _size - pos : len - pos; //真实的长度
			strncpy(tmp._str, _str + pos, tlen);
			tmp._size = tlen ;
			tmp._str[tmp._size] = '\0';//补上
			return tmp;
		}

插入和删除

//在Pos位置插入字符串
		string& insert(size_t pos,  char ch)
		{
			assert(pos <= _size);
			//判断是否需要扩容
			if (_size == _capacity)
			{
				reserve(_capacity == 0 ? 4 : _capacity * 2);
			}
			//移动
			size_t end = _size + 1;
			while (end > pos)
			{
				//往后移
				_str[end] = _str[end - 1];
				--end;
			}
			_str[pos] = ch;
			++_size;
			return *this;
		}
		//在Pos位置插入字符串
		string& insert(size_t pos, const char* s)
		{
			assert(pos <= _size);

			size_t len = strlen(s);
			//判断是否需要扩容
			if (_size + len > _capacity)
			{
				reserve(_capacity == 0 ? 4 : _capacity * 2);
			}
			//移动
			size_t end = _size + len;
			while (end >= pos + len)
			{
				//往后移
				_str[end] = _str[end - len];
				--end;
			}
			strncpy(_str + pos, s, len);
			_size += len;
			return *this;
		}

		string& erase(size_t pos = 0, size_t len = npos)
		{
			assert(pos < _size);
			if (len == npos || pos + len >= _size)
			{
				_size = 0;
				_str[0] = '\0';
			}
			else
			{
				//用后面的覆盖前面的
				strcpy(_str + pos, _str + pos + len);
				_size -= len;
			}
			return *this;
		}

1.6 string类非成员函数

	//流提取和流插入并不是一定要用友元函数,
	//如果不需要用到类的私有成员函数就不需要用友元,这里就没有用
	ostream& operator<<(ostream& out,  string& s)
	{
		// 不管字符数组中的内容是啥,size是多少,就要输出多少个有效字符
		//如果使用cout<
		for (size_t i = 0; i < s.size(); ++i)
		{
			out << s[i];
		}
		return out;
	}
	istream& operator>>(istream& in, string& s)
	{
		s.clear();
		char ch = in.get();
		while (ch != ' ' && ch != '\n')
		{
			s += ch;
			ch = in.get();
		}
		return in;
	}
	istream& getline(istream& in, string& s)
	{
		s.clear();
		char ch = in.get();
		while ( ch != '\n')
		{
			s += ch;
			ch = in.get();
		}
		return in;
	}

	//实现字符串的比较,并不是比较长度,而是比较每一个字符
	//实现一个大于或者小于加上一个等于,就可以实现其他的比较

	bool operator>(string& s1, string& s2) 
	{
		size_t i1 = 0, i2 = 0;
		while (i1 < s1.size() && i2 < s2.size())
		{
			if (s1[i1] > s2[i2])
			{
				//遇到大于的就返回真
				return true;
			}
			else if(s1[i1] < s2[i2])
			{
				return false;
			}
			++i1;
			++i2;
		}
		//退出循环的三种情况、
		//"abcd"  "abcd"   false
		//"abcd"  "abcde"  false
		//"abcde"  "abcd"  true
		return i1 < s1.size() ? true : false;
	}
	bool operator==(string& s1, string& s2) 
	{
		size_t i1 = 0, i2 = 0;
		while (i1 < s1.size() && i2 < s2.size())
		{
			if (s1[i1] != s2[i2])
				return false;
			++i1;
			++i2;
		}
		//退出循环仍然是那三种情况
		//"abcd"  "abcd"   ture
		//"abcd"  "abcde"  false
		//"abcde"  "abcd"  false

		if (i1 == s1.size() && i2 == s2.size())
		{
			return true;
		}
		else
		{
			return false;
		}
	}

	bool operator!=(string& s1, string& s2)
	{
		return !(s1 == s2);
	}
	bool operator< (string& s1, string& s2)
	{
		return !(s1 > s2 || s1 == s2);
	}
	bool operator<=(string& s1, string& s2)
	{
		return !(s1 < s2);
	}
	bool operator>=(string& s1, string& s2)
	{
		return s1 > s2 || s1 == s2;
	}

你可能感兴趣的:(C++,c++,开发语言,后端)