1、传统的实现string类的方法

优点:程序简单易懂

缺点:

1)在实现其拷贝构造和赋值的操作时多次的调用new动态的开辟空间,因此也需要多次的通过delete来释放空间。如果处理不当还容易造成内存泄漏。

2)程序的一致性比较差

#include <iostream>
#include <string>
using namespace std;

class String 
{
public:
	// 构造函数
	String(char *str = "")
		:_str(new char [strlen(str)+1])
	{
		if (_str != NULL)
		{
			strcpy(_str, str);
		}
	}
	//拷贝构造
	String(const String& str)
		:_str(new char[strlen(str._str) + 1])
	{
		if (_str != NULL)
		{
			strcpy(_str, str._str);
		}
	}
	//赋值
	String& operator = (const String& str)
	{
		if (this != &str)//自赋值问题
		{
			delete[]_str;//释放之前开辟的空间
		_str = new char[strlen(str._str) + 1];//开辟新的空间
			strcpy(_str, str._str);//拷贝
		}
		return *this;
	}
	//析构函数
	~String()
	{
		delete [] _str;
	}

private:
	char * _str;
};

2、现代的实现string类的方法写实

拷贝构造实现思路:

a)创建一个临时对象,并通过cconst String._str来构造。

b)构造完成之后将临时对象的_str和this._str进行交换进而使得对象的内容交换来完成拷贝构造

赋值实现思路:

通过参数的值传递来构造对象,并将对象的内容交换

优点:

1)程序的一致性比较好

2)只是在构造函数中出现了开辟空间的new,只在析构函数中出现了delete结构清晰,不容易造成内存泄漏

class String 
{
public:
	// 构造函数
	String(char *str = "")
		:_str(new char [strlen(str)+1])
	{
		if (_str != NULL)
		{
			strcpy(_str, str);
		}
	}
	
	//拷贝构造
	String(const String& str)
		:_str(NULL)//防止其指向一份不合法的空间
	{
		String tmp(str._str);
		swap(_str, tmp._str);
	}

	//赋值
	String& operator =(String str)
	{
		swap(_str, str._str);
		return *this;
	}

	//析构函数
	~String()
	{
		delete [] _str;
	}
private:
	char * _str;
};


3、写实函数提高string类的效率

在以上的两种string类的实现方法中,都存在一个效率的问题。有的时候字符串比较长,而且对象建立之后也不一定更改。但是以上两种方法无论何时都需要构建,其效率比较低而且会带来内存的浪费。

我们可以通过让所有指向相同字符串内容的对象指向同一份空间,通过计数器来记录对象的个数,避免析构出现错误。

所以可以通过在开辟对象的时候多开辟4个字节的空间,来存放计数器。如果修改的时候再开辟空间。

#define _CRT_SECURE_NO_WARNINGS

#include<iostream>
#include <string>
using namespace std;

class String 
{
public:
	//构造函数
	String(char *str="")
		:_Pstr(FindStartRef(new char[strlen(str)+1+sizeof(int)]))
	{
		*Count(_Pstr) = 1;//计数器
		strcpy(_Pstr, str);//将字符串拷贝
	}

	//拷贝构造函数
	//如果不更改对象的内容则让多个对象的字符指针指向同一份空间
	String(const String& s)
	{
		_Pstr = s._Pstr;
		(*Count(_Pstr))++;//计数器加1
	}

	//赋值语句
	//如果不更改对象的内容则让多个对象的字符指针指向同一份空间
	String& operator =(const String& s)
	{
		if (this != &s)//避免自赋值
		{
			if (--*(Count(_Pstr)) == 0)
			{
				delete[]FindDel(_Pstr);
			}//只有一个对象使用的一份空间则释放
			else
			{
				_Pstr = s._Pstr;
				(*Count(_Pstr))++;
			}
		}
		return *this;
	}

	//析构函数
	~String()
	{
		if (--*(Count(_Pstr)) == 0)
		{
			delete[]FindDel(_Pstr);
		}
	}
public:
	//找到开辟空间时的存放字符串的首地址
	char * FindStartRef(char* str)
	{
		return (str + 4);
	}

	//找到释放空间时的首地址
	char * FindDel(char* del)
	{
		return (del - 4);
	}

	//找到计数器的首地址
	int *Count(char* str)
	{
		return (int *)(str - 4);
	}
public:
	//修改写实对象的内容函数
	char & operator[](int index)
	{
		if (--*(Count(_Pstr)) != 0)
		{
			char * tmp = _Pstr;
			_Pstr = FindStartRef(new char[strlen(_Pstr) + 1 + sizeof(int)]);
			*Count(_Pstr) = 1;//计数器置1
			strcpy(_Pstr, tmp);
		}//如果该对象和其他对象公用一份空间
		else
		{
			//单独使用一份空间可以随意更改
		}
		return _Pstr[index];
	}
private:
	char * _Pstr;
};