string类的模拟实现以及浅拷贝问题

目录

问:为什么要有string类?

介绍一下标准库中的string类

浅拷贝的问题以及解决方法:

1.回顾浅拷贝问题:

2.浅拷贝的两种解决方法

>>1.深拷贝

>>2.写时拷贝

string类的模拟实现:


问:为什么要有string类?

答:C 语言中,字符串是以 '\0' 结尾的一些字符的集合,为了操作方便, C 标准库中提供了一些 str 系列的库函数,但是这些库函数与字符串是分离开的,不太符合OOP 的思想,而且底层空间需要用户自己管理,稍不留神可能还会越界访问。

介绍一下标准库中的string类

  • 1. string 是表示字符串的字符串类
  • 2.该类的接口与常规容器的接口基本相同,再添加了一些专门用来操作 string 的常规操作。
  • 3.string 在底层实际是: basic_string 模板类的别名, typedef basic_string<char, char_traits, allocator> string;
  • 4.不能操作多字节或者变长字符的序列。

浅拷贝的问题以及解决方法:

1.回顾浅拷贝问题:

什么叫浅拷贝?

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

我们在学习类的时候,知道了类的默认构造函数在创建类类型对象的时候就是浅拷贝。

例如:

class String
{
public:
	String(const char* str = "jack")
	{
		_str = (char*)malloc(strlen(str) + 1);
		strcpy(_str, str);
	}
	~String()
	{
		cout << "~String()" << endl;
		free(_str);
	}
private:
	char* _str;
};
int main()
{
	String s1("hello");
	String s2(s1);
	return 0;
}

 

 结论:当我们调用默认的拷贝构造函数创建s2的时候,并没有为s2申请新的空间,而是将旧空间(s1)的地址拷贝了一份给了s2,最终s1和s2指向了同一块空间,当我们程序运行结束调用析构函数的时候,s1需要释放自己的空间,s2也需要释放自己的空间,最后就造成了同一块空间多次,导致程序崩溃。

2.浅拷贝的两种解决方法

>>1.深拷贝

原理:我们自己完成拷贝构造函数与赋值运算符的重载,不使用系统默认的,给每个对象独立分配资源,保证多个对象之间不会因共享资源而造成多次释放造成程序崩溃问题。

代码实现:

传统版:

//传统版
namespace bite
{
	class string
	{

	public:

		string(const char* str = "")
		{
			if (nullptr == str)
			{
				assert(false);
			}
			int len = strlen(str);
			_str = new char[len + 1];
			strcpy(_str, str);
			_str[len] = '\0';
		}

		string(const string &s)
			:_str(new char[strlen(s._str) + 1])
		{
			strcpy(_str, s._str);
		}

		string& operator=(const string &s)
		{
			if (this != &s)
			{
				char*temp = new char[strlen(s._str) + 1];
				strcpy(temp, s._str);
				delete[] _str;
				_str = temp;
			}
			return *this;
		}

		~string()
		{
			if (_str)
			{
				delete[] _str;
				_str = nullptr;
			}
		}

	private:

		char *_str;

	};
}

 现代版:

//现代版
namespace bite
{
	class string

	{

	public:

		string(const char* str = "")
		{
			if (str == nullptr)
			{
				assert(false);
			}

			int len = strlen(str);
			_str = new char[len + 1];
			strcpy(_str, str);
			_str[len] = '\0';
			_size = len;
			_capacity = len;
		}

		string(const string &s)
			:_str(nullptr)
		{
			string strTemp(s._str);
			this->swap(strTemp);
		}

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

		~string()
		{
			if (_str)
			{
				delete[] _str;
				_str = nullptr;
				_size = 0;
				_capacity = 0;
			}
		}
		void swap(string &s)
		{
			std::swap(_str, s._str);
			std::swap(_size, s._size);
			std::swap(_capacity, s._capacity);
		}

	private:

		char *_str;
		size_t _size;
		size_t _capacity;

	};
}

测试结果:

  

>>2.写时拷贝

原理:写时拷贝就是一种拖延症,是在浅拷贝的基础之上增加了引用计数的方式来实现的

引用计数:用来记录资源使用者的个数。在构造时,将资源的计数给成1,每增加一个对象使用该资源,就给计数增加1,当某个对象被销毁时,先给该计数减1,然后再检查是否需要释放资源,如果计数为1,说明该对象时资源的最后一个使用者,将该资源释放;否则就不能释放,因为还有其他对象在使用该资源。

写时拷贝的实现,调用拷贝构造函数创建出来的s2和s1拥有同样的资源:_str + 引用计数模块 

问题:当s2需要对它的内部元素做修改的时候,比如s1[1] = 'H';

 

 这个问题的解决:

当s1要对共享资源中的内容修改时,如果s1与其他对象共享资源了,必须将s1从共享的资源中分离出来,即给s1单独开辟空间

 

string类的模拟实现:

vs2013下模拟实现:

#include<iostream>
#include<assert.h>
#pragma warning(disable:4996)
using namespace std;
#define  iterator char*
#define npos -1
namespace bite
{
	class string
	{
	public:
		string(char* str="")
		{
			if (str == nullptr)
			{
				assert(false);
			}

			_size = strlen(str);
			_str = new char[_size+1];
			strcpy(_str, str);
			_capacity = _size;
		}
		string(const string &s)
			:_str(nullptr)
		{
			string strtemp(s._str);
			this->swap(strtemp);
		}
		string(size_t n, char ch)
		{
			_str = new char[n + 1];
			memset(_str, ch, n);
			_size = n;
			_str[_size] = '\0';
			_capacity = n;
		}
		string& operator=(string s)
		{
			swap(s);
			return *this;
		}
		~string()
		{
			if (_str)
			{
				delete[] _str;
				_str = nullptr;
				_size = 0;
				_capacity = 0;
			}
		}
//
// iterator
		iterator begin()
		{
			return _str;
		}
		iterator end()
		{
			return _str + _size;
		}
/
// capacity
		size_t size()
		{
			return _size;
		}
		size_t size()const
		{
			return _size;
		}
		size_t capacity()
		{
			return _capacity;
		}
		size_t capacity()const
		{
			return _capacity;
		}
		bool empty()
		{
			return 0 == _size;
		}
		void clear()
		{
			_str[0] = '\0';
			_size = 0;
			_capacity = 0;
		}
		void resize(size_t newsize,char ch)
		{
			size_t oldsize = size();
			if (newsize<=newsize)
			{
				_str[newsize] = '\0';
			}
			else
			{
				if (newsize > _capacity)
				{
					reserve(newsize);
				}
				append(newsize - oldsize, ch);
			}
			_size = newsize;
		}
		void resize(size_t newsize)
		{
			resize(newsize, char());
		}
		void reserve(size_t n)
		{
			if (n > _capacity)
			{
				//1.申请新空间
				char* temp = new char[n+1];
				//2.元素拷贝
				strcpy(temp,_str);
				//3.释放旧空间
				delete[] _str;
				_str = temp;
				_capacity = n;
			}
		}
		/
		// access
		char& operator[](size_t index)
		{
			return _str[index];
		}
		const char& operator[](size_t index)const
		{
			return _str[index];
		}
		/
		// modify
		void push_back(char ch)
		{
			append(1,ch);
		}
		string& operator+=(char ch)
		{
			push_back(ch);
			return *this;
		}
		string& operator+=(const char*str)
		{
			append(str);
			return *this;
		}
		string& operator+=(const string&s)
		{
			append(s._str);
			return *this;
		}
		string& append(size_t n, char ch)
		{
			if (_size + n > _capacity)
			{
				reserve(_size + n);
			}
			memset(_str + _size, ch, n);
			_size += n;
			_str[_size] = '\0';
			return *this;
		}
		string& append(const char*str)
		{
			size_t len = strlen(str);
			if (_size + len > _capacity)
			{
				reserve(_size + len);
			}

			strcat(_str, str);
			_size += len;
			return *this;
		}
		void swap(string& s)
		{
			std::swap(_str, s._str);
			std::swap(_size, s._size);
			std::swap(_capacity, s._capacity);
		}
		const char* c_str()const
		{
			return _str;
		}
/
//relational operators
		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 strcmp(_str, s._str)>0;
		}

		bool operator>=(const string& s)
		{
			return (*this > s || *this == s);
		}

		bool operator==(const string& s)
		{
			return 0 == strcmp(_str, s._str);
		}

		bool operator!=(const string& s)
		{
			return !(*this == s);
		}

		// 返回c在string中第一次出现的位置

		size_t find(char c, size_t pos = 0) const
		{
			assert(pos<_size);
			for (size_t i = pos; i < _size; i++)
			{
				if (_str[i] == c)
				{
					return i;
				}
			}
			return npos;
		}

		// 返回子串s在string中第一次出现的位置

		size_t find(const char* s, size_t pos = 0) const
		{
			assert(s);
			assert(pos < _size);
			size_t len = strlen(s);
			assert(len < (_size - pos));

			size_t j = 0;
			for (size_t i = pos; i < _size; i++)
			{
				size_t k = i;
				for (; j < len; j++)
				{
					if (_str[k] != s[j])
					{
						j = 0;
						break;
					}
					else
					{
						k++;
					}
				}
				if (j == len)
				{
					return i+1;
				}
			}
			return -1;
		}

		// 在pos位置上插入字符c/字符串str,并返回该字符的位置

		string& insert(size_t pos, char c)
		{
			size_t newsize = _size + 1;
			if (newsize > _capacity)
			{
				reserve(newsize);
			}
			size_t i = _size+1;
			for (; i > pos; i--)
			{
				_str[i] = _str[i - 1];
			}
			_str[i] = c;
			_size = newsize;
			return *this;
		}

		string& insert(size_t pos, const char* str)
		{
			int len = strlen(str);
			for (int i = 0; i < len; i++)
			{
				insert(pos+i,str[i]);
			}
			return *this;
		}



		// 删除pos位置上的元素,并返回该元素的下一个位置

		string& erase(size_t pos, size_t len)
		{
			for (size_t i = pos + len; i < _size; i++)
			{
				_str[pos++] = _str[i];
			}
			return *this;
		}


	private:
		char* _str;
		size_t _size;
		size_t _capacity;
	};
}
 

 

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值