c++中string類的基本功能的實現(1)

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;
};


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章