C++中string的模拟实现和其中的一些问题

1.深浅拷贝问题

问题代码:

//C++标准库命名空间最大的意义,解决定义一个和库函数名字一样的类也是可以的
namespace sakeww
{
	class string
	{
	public:
		string(const char* str)
			:_str(new char[strlen(str) + 1])
		{
			strcpy(_str, str);
		}

		~string()
		{
			delete[] _str;
			_str = nullptr;
		}
	private:
		char* _str;
	};

	void test_string1()
	{
		string s1("hello string!");//没问题
		string s2(s1);//出现问题了
	}
}

问题和解答:

这段代码是有问题,为什么?思考一下,然后再看下面解析
答:

  1. s1自己创建了一个空间,里面放了hello string!

  2. 拷贝构造是默认函数,我们不写,编译器会默认生成一份
    生成的会对内置类型进行值拷贝(浅拷贝)
    自定义类型会去调用他的拷贝构造

  3. s2会将s1中的每个字节拷贝到s2
    也就是将s1中的每个指针拷贝到s2中,相当于s2._str 指向了s1._str
    在这里插入图片描述

  4. 在执行析构函数的时候,s1释放了_str,s2不能释放空空间,所以出现错误

解决:

进行深拷贝

namespace sakeww
{
	class string
	{
	public:
		string(const char* str)
			:_str(new char[strlen(str) + 1])
		{
			strcpy(_str, str);
		}
		string(const string& s)
			:_str(new char[strlen(s._str) + 1])
		{
			strcpy(_str, s._str);
		}

		~string()
		{
			delete[] _str;
			_str = nullptr;
		}
	private:
		char* _str;
	};

	void test_string1()
	{
		string s1("hello string!");
		string s2(s1);
	}
}

2.=

问题代码:

		string& operator=(const string& s)
		{
		    delete[] _str;
			_str = new char[strlen(s._str + 1)];
			strcpy(_str, s._str);

			return *this;
		}

问题与解答:

这是一个模拟=的代码
当想要实现s1 = s1时,这个就会报错

解决:

起始这个问题的问题就是 开局直接delete,导致后面的赋值不了
两种写法:

		string& operator=(const string& s)
		{
		//第一种直接判断是否相等,然后在做决定
			//if (this != &s) {
			//	delete[] _str;
			//	_str = new char[strlen(s._str + 1)];
			//	strcpy(_str, s._str);
			//}
			
        //这一块的问题也就是最开始空间释放了,然后拷贝这个空间的内容,我们新创建一个空间
			char* temp = new char[strlen(s._str + 1)];
			strcpy(_str, s._str);
			delete[] _str;
			_str = temp;

			return *this;
		}

3.定义空string

问题代码:

namespace sakeww
{
	class string
	{
	public:
		string()
			:_str(nullptr)
		{}
		string(const char* str)
			:_size(strlen(str))
			,_capacity(_size)
		{
			_str = new char[_capacity + 1];
			strcpy(_str, str);
		}
		string(const string& s)
			:_size(s._size)
			, _capacity(s._capacity)
		{
			_str = new char[_capacity + 1];
			strcpy(_str, s._str);
		}
		
		const char* c_str()
		{
			return _str;
		}

		~string()
		{
			delete[] _str;
			_str = nullptr;
			_size = _capacity = 0;
		}
	private:
		char* _str;
		size_t _size;
		size_t _capacity;//有效字符空间数,不算\0
	};

	void test_string1()
	{
		string s1("hello string!");
		string s2;
		cout << s1.c_str() << endl;
	}
}

问题与解答:

为什么会崩溃?
s2调用的是默认构造函数,
默认构造函数给个空是不行的

这是stl中的string
在这里插入图片描述
内容是空
size是0
capacity是15

解决:

namespace sakeww
{
	class string
	{
	public:
		string(const char* str = "")
			:_size(strlen(str))
			,_capacity(_size)
		{
			_str = new char[_capacity + 1];
			strcpy(_str, str);
		}
		string(const string& s)
			:_size(s._size)
			, _capacity(s._capacity)
		{
			_str = new char[_capacity + 1];
			strcpy(_str, s._str);
		}
		
		const char* c_str()
		{
			return _str;
		}

		~string()
		{
			delete[] _str;
			_str = nullptr; 
			_size = _capacity = 0;
		}
	private:
		char* _str;
		size_t _size;
		size_t _capacity;//有效字符空间数,不算\0
	};

	void test_string1()
	{
		string s1("hello string!");
		string s2;
		cout << s1.c_str() << endl;
		cout << s2.c_str() << endl;
	}
}

4.范围for问题

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

范围for是基于迭代器实现的
他会去直接找你实现的sakeww::string中的begin和end
如果你的模拟实现中begin和end的名字是其他的,那么这个返回for就会执行不了

5.深拷贝和赋值(现代写法)

传统写法:深拷贝在这里插入图片描述
赋值:
在这里插入图片描述
现代写法:

		void swap(string& s)
		{
			std::swap(_str, s._str);
			std::swap(_size, s._size);
			std::swap(_capacity, s._capacity);
		}
		string(const string& s)
			:_str(nullptr)
			,_size(0)
			,_capacity(0)
		{
			string tmp(s._str);
			swap(tmp);
		}
		string& operator=(string& s)
		{
			swap(s);
			return *this;
		}

6. insert插中,越界问题

正确代码:

		string& insert(size_t pos, const char* s)
		{
			assert(pos <= _size);
			size_t len = strlen(s);
			if (_size + len > _capacity)
			{
				reserve(_size + len);
			}

			size_t end = _size + len;
			//在头插中,pos=0,end在遍历时,一直是大于零的,因为只需要插入len个字符,所以在前面len个位置就不用end--了
			while (end >= pos+len)
			{
				_str[end] = _str[end - len];
				--end;
			}

			strncpy(_str + pos, s, len);
			_size += len;

			return *this;
		}

7. 比较运算符重载成全局函数

因为我们已经将[],重载,所以我们可以重载成全局函数
c_str已经重载,所以可以使用c语言的一些函数

	bool operator<(const string& s1, const 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;
		//	else
		//	{
		//		++i1;
		//		++i2;
		//	}
		//}
		当两个字符其中一个结束后
		//return i2 < s2.size() ? true : false;

		//运用strcmp
		return strcmp(s1.c_str(), s2.c_str()) < 0;
	}

8.重载<< 之 为什么不能用out<<s.c_str()?

这种方式不符合规范
范围for是根据size来确定输出的
s.c_str()遇见\0就会停止,如果字符中其中一个字符数\0在中间,那么就会停止

9.关于浅拷贝

如果非要浅拷贝可以运用引用计数的方法。

浅拷贝的问题和解决:

  1. 析构两次
    当第二个变量指向同一块空间,那么就将引用计数++
    当析构时,就将引用计数–
  2. 其中一个对象进行修改会影响其中一个。
    当需要对其进行修改的时候,然后再进行深拷贝,然后进行修改(延迟拷贝思想)

缺陷:

  1. 引用计数存在线程安全问题,需要加锁,在多线程环境下要付出代价
  2. 在动态库和静态库有些场景会有问题

10.string的模拟实现

//C++标准库命名空间最大的意义,解决定义一个和库函数名字一样的类也是可以的
namespace sakeww
{
	class string
	{
	public:
		//迭代器
		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;
		}


		string(const char* str = "")
			:_size(strlen(str))
			,_capacity(_size)
		{
			_str = new char[_capacity + 1];
			strcpy(_str, str);
		}

		void swap(string& s)
		{
			std::swap(_str, s._str);
			std::swap(_size, s._size);
			std::swap(_capacity, s._capacity);
		}
		//传统写法:
		//string(const string& s)
		//	:_size(s._size)
		//	, _capacity(s._capacity)
		//{
		//	_str = new char[_capacity + 1];
		//	strcpy(_str, s._str);
		//}
		//现代写法
		string(const string& s)
			:_str(nullptr)
			,_size(0)
			,_capacity(0)
		{
			string tmp(s._str);
			swap(tmp);
		}


		//s1=s3传统写法:
		//string& operator=(const string& s)
		//{
		//	//if (this != &s) {
		//	//	delete[] _str;
		//	//	_str = new char[strlen(s._str + 1)];
		//	//	strcpy(_str, s._str);
		//	//}

		//	char* temp = new char[s._capacity + 1];
		//	strcpy(temp, s._str);
		//	delete[] _str;
		//	_str = temp;
		//	_size = s._size;
		//	_capacity = s._capacity;

		//	return *this;
		//}
		//现代写法:
		string& operator=(string& s)
		{
			swap(s);
			return *this;
		}



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

		size_t size()const
		{
			return _size;
		}

		char& operator[](size_t pos)
		{
			assert(pos < _size);
			return _str[pos];
		}

		const char& operator[](size_t pos) const
		{
			assert(pos < _size);
			return _str[pos];
		}

		//reserve对容量进行改变
		void reserve(size_t n)
		{
			if (n > _capacity)
			{
				char* tmp = new char[n+1];
				strcpy(tmp,_str);
				delete[] _str;

				_str = tmp;
				_capacity = n;
			}
		}
		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';
			}
		}
		void push_back(char ch)
		{
			//if (_size == _capacity)
			//{
			//	reserve(_capacity == 0 ? 4: _capacity * 2);
			//}
			//_str[_size] = ch;
			//++_size;
			//_str[_size++] = '\0';

			insert(_size, ch);
		}
		void append(const char* str)
		{
			//size_t 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;
		}

		size_t find(char ch)
		{
			for (size_t i = 0; i < _size; ++i)
			{
				if (ch == _str[i])
					return i;
			}
			return npos;
		}
		size_t find(const char* s, size_t pos = 0)
		{
			const char* ptr = strstr(_str + pos, s);
			if (ptr == nullptr)
			{
				return npos;
			}
			else
			{
				return ptr - _str;
			}
		}

		string& insert(size_t pos, char ch)
		{
			assert(pos <= _size);

			if (_size == _capacity)
			{
				reserve(_capacity == 0 ? 4 : _capacity * 2);
			}

			/*
			如果pos是零,那么--end = -1(最大)
			size_t end = _size;
			while (end >= pos)
			{
			    _str[end + 1] = _str[end];
			    --end;
			}*/

			/*
			
			int end = _size;
			while (end >= (int)pos)
			{
			    _str[end + 1] = _str[end];
			    --end;
			}*/

			size_t end = _size + 1;
			while (end > pos)
			{
				_str[end] = _str[end - 1];
				--end;
			}

			_str[pos] = ch;
			++_size;

			return *this;
		}
		string& insert(size_t pos, const char* s)
		{
			assert(pos <= _size);
			size_t len = strlen(s);
			if (_size + len > _capacity)
			{
				reserve(_size + len);
			}

			size_t end = _size + len;
			while (end >= pos+len)
			{
				_str[end] = _str[end - len];
				--end;
			}

			strncpy(_str + pos, s, len);
			_size += len;

			return *this;
		}

		//从pos位置,删除npos个字符
		string& erase(size_t pos = 0, size_t len = npos)
		{
			assert(pos < _size);

			if (len == npos || pos + len >= _size)
			{
				_str[pos] = '\0';
				_size = pos;
			}
			else
			{
				strcpy(_str + pos, _str + pos + len);
				_size -= len;
			}

			return *this;
		}


		~string()
		{
			delete[] _str;
			_str = nullptr; 
			_size = _capacity = 0;
		}
		void clear()
        {
            _str[0] = '\0';
            _size = 0;
        }
	private:
		char* _str;
		size_t _size;
		size_t _capacity;//有效字符空间数,不算\0

	public: 
		static const size_t npos = -1;
	};

	bool operator<(const string& s1, const 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;
		//	else
		//	{
		//		++i1;
		//		++i2;
		//	}
		//}
		当两个字符其中一个结束后
		//return i2 < s2.size() ? true : false;

		//运用strcmp
		return strcmp(s1.c_str(), s2.c_str()) < 0;
	}
	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();
		char ch = in.get();
		while (ch != ' ' && ch != '\n')
		{
			s += ch;
			ch = in.get();
		}
		return in;
	}


	void test_string1()
	{
		string s1;
		cin >> s1;
		cout << s1 << endl;
	}
}

  • 11
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值