模拟实现string类深浅拷贝

从构造,析构以及拷贝构造的方面理解string类的底层实现。

源代码获取:
https://github.com/akh5/C-/blob/master/STL/Mystring.cpp

string的底层通过字符指针char*通过构造函数申请对应大小的空间,并将指针指向其空间。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

namespace my
{
	class string
	{
	public:
		string(char* str = "")
		{
			if (nullptr == str)
				str = "";	
			//申请空间
			_str = new char[strlen(str)+1]; //“\0”
			strcpy(_str, str);
		}
		
		~string()
		{
			if (_str)
			{
				delete[] _str;
				_str = nullptr;
			}
		}
	private:
		char* _str;
	};
}

    构造时需要先给一个默认为空的字符粗,对应string str没=没有初始化的情况。构造函数中,需要人为申请一段比str多1的空间,可以放置str中的内容和一个’\0’。将_str指向申请的空间,用strcpy函数将str的内容赋值给_str。
    析构函数将开始申请的空间释放掉,并将_str指向空指针就完成析构

拷贝构造

深拷贝和浅拷贝概念的引入:https://blog.csdn.net/MPF1230/article/details/104023364

浅拷贝的实现相对简单,编译器默认的拷贝构造也是浅拷贝,但是浅拷贝的危害是,拷贝的对象结束调用析构函数时,会导致空间的二次释放

namespace my
{
	class string
	{
	public:
		string(char* str = "")
		{
			if (nullptr == str)
				str = "";	
			//申请空间
			_str = new char[strlen(str)+1]; //“\0”
			strcpy(_str, str);
		}
		
		//浅拷贝
		string(const string& s)
			:_str(s._str)
		{ }

		string &operator=(const string& s)
		{
			_str = s._str;
			return *this;
		}
		
		~string()
		{
			if (_str)
			{
				delete[] _str;
				_str = nullptr;
			}
		}
	private:
		char* _str;
	};
}

浅拷贝的拷贝构造,只需要将当前_str的指针指向需要拷贝的s._str的指针。
对应的=运算符的重载,也是一样将当前_str的指针指向需要拷贝的s._str的指针,并返回this指针。

深拷贝
深拷贝大致分为4个步骤

  1. 申请新空间
  2. 在新空间中拷贝需要的内容
  3. 将指针指向新空间
  4. 释放旧空间

在这里插入图片描述

namespace my
{
	class string
	{
	public:
		string(char* str = "")
		{
			if (nullptr == str)
				str = "";
			
			//申请空间
			_str = new char[strlen(str)+1]; //“\0”
			strcpy(_str, str);
		}

		//深拷贝
		string(const string& s)
			:_str(new char[strlen(s._str)])
		{
			strcpy(_str, s._str);

		}
		string& operator=(const string& s)
		{
			if (this != &s)
			{
				char* temp = new char[strlen(s._str)];
				strcpy(temp, s._str);
				delete[] _str;
				_str = temp;
			}
		}
		~string()
		{
			if (_str)
			{
				delete[] _str;
				_str = nullptr;
			}
		}
	private:
		char* _str;
	};
}

通过swap函数改进深拷贝

		string(const string& s)
			:_str(nullptr)
		{
			string strtemp(s._str);
			swap(_str, strtemp._str);
		}

		string& operator=(const string& s)
		{
			if (this != &s)
			{
				string strtemp(s);
				swap(_str, strtemp);
			}
		}

先将原指针置为空,编译器就会自动释放空的旧空间,并且在构造临时空间时,不能直接构造s,会导致无线递归构造,最后在用swap函数与临时空间交换。



改进浅拷贝多次释放同一资源的问题

最简单的方法就是在类总添加一个,当前资源使用情况的计数器,每当有指向当前资源的对象时,计数器加一,有对象释放资源时,计数器减一。只有当最后一个释放资源时,才能将资源释放掉

class string
	{
	public:
		string(char* str = "")
			:_pCount(new int(1))//初始化计数
		{
			if (nullptr == str)
				str = "";
			
			//申请空间
			_str = new char[strlen(str)+1]; //“\0”
			strcpy(_str, str);
		}
		//浅拷贝
		string(const string& s)
			:_str(s._str) 
			,_pCount(s._pCount) //拷贝资源的同时,也拷贝计数
		{
			++_count;
		}
		//在拷贝前,当前对象可能有旧资源,所以需要先将旧资源释放,指向新资源
		string &operator=(const string& s)
		{
			if (this != &s)
			{
				//需要将当前对象的旧资源释放掉
				if (0 == --*_pCount)
				{
					delete[] _str;
					delete _pCount;
				}
				_str = s._str;
				_pCount = s._pCount;
				(*_pCount)++;
			}
			return *this;
		}

		~string()
		{
			if (_str && --*_pCount==0 )
			{
				delete[] _str;
				_str = nullptr;

				delete _pCount;
				_pCount = nullptr;
			}
		}

	private:
		char* _str;
		int* _pCount;
	};

需要新引入一个指向计数器的指针,每一个对象指向同一资源的同时,同样也指向同一个计数器,每当发生拷贝构造时 _pCount加1,释放时_pCount减1.只有当_pCount等于0时,才能释放该资源。

最后是[]重载,可以访问固定位置的内容

	[]运算符重载,访问某一位置元素
		char& operator[](size_t index)
		{
			return _str[index];
		}
发布了52 篇原创文章 · 获赞 13 · 访问量 5443

猜你喜欢

转载自blog.csdn.net/MPF1230/article/details/104059562