string类模拟之引用计数和写时拷贝

在 string类的模拟之深浅拷贝  中,我们可以看到,程序浅拷贝导致的崩溃,是因为多次释放内存引起的多次析构,那么我们可以添加一个计数器,这里称之为引用计数,来表示当前的空间有多少指针指向它,如果大于一的话,那么在调析构函数的时候,就只对引用计数减一,当减到只剩一的时候就可以安心的释放内存空间了。

引用计数的本质就是一个整形变量,那么我们到底应该怎么给出引用计数的形式呢?

1.整形成员变量(不可以,每个对象都会单独存一份,就会导致计数器并不统一(数值只在相邻的对象发生传递),在析构的时候内存无法释放,导致内存泄漏)


2.静态成员变量 (不可以,静态成员变量为所有对象共享,任何对象都可以对它进行修改,每创建一个对象我们都对计数器加1,却忽略了创建的新对象是否与已存在的对象占同一块空间


3.指针变量(可以)

class String
{
public:
	String(const char* pStr = "")//构造函数  
		:_pCount(new int(0))
		, _pStr(new char[strlen(pStr) + 1])
	{
			strcpy(_pStr, pStr);
			*_pCount = 1;
	}
	String(const String& s) 
		:_pCount(s._pCount)
	{
		_pStr = (char*)s._pStr;
		_pCount = s._pCount;
		(*_pCount)++;
	}
	~String()//析构 
	{
		if (NULL != _pStr)
		{
			if (--(*_pCount) == 0)
			{
				delete[]_pCount;//释放计数器指针  !!!
				delete[]_pStr;
				_pStr = NULL;
				_pCount = NULL;
			}
		}
	}
	String& operator=(String& s)//赋值运算符重载  
	{
		if (_pStr != s._pStr)
		{
			_pStr = s._pStr;
			_pCount = s._pCount;
			(*_pCount)++;
		}
		return *this;
	}
private:
	int* _pCount;
	char* _pStr;
};


void Funtest()
{
	String s1("abcd");
	String s2(s1);
	String s3 = "1111";//调用拷贝构造函数(编译器会s2直接初始化s3)  
	String s4;//s4对象已经存在了  
	s4 = s3;//编译器会调用赋值运算符重载将s3的值赋给s4  
}

至此,好像已经解决了多次析构的问题,但是还不够好(开辟了两块较小的内存空间,造成内存的碎片化问题)。我们采取下面这样的方法来避免。


再说说每次拷贝都开辟内存空间,这样的做法虽然有效的避免了内存的多次释放的问题,但它带来的问题确是,需要不停的开辟内存,尽管可能只是读一下内存中的字符而已,这就造成的系统资源的浪费。由此,我们可以采用写时拷贝的方式来避免这样的问题。

只要S2的操作只是读取内存时,那么并不会开辟空间,而发生写(增删改)的操作时,我们就新开辟空间。改变S2的指向。


String.cpp

#define _CRT_SECURE_NO_WARNINGS 1
#include "String.h"

int& String::GetRefCount()
{
	return *((int *)(_str - 4));
}


String::~String()
{
	if (--GetRefCount() == 0)
	{
		delete[](_str - 4);
	}
}

String::String(const String& s)
		:_str(s._str)
{
	GetRefCount()++;
}

String& String::operator=(const String& s)
{
	if (_str != s._str)	//自赋值
	{
		if (--GetRefCount() == 0)	//只有一个引用的时候
		{
			delete[](_str - 4);
		}

		_str = s._str;		//指向位置改变,引用计数增加
		GetRefCount()++;	
	}
	return *this;
}


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

void String::CopyOnWrte()
{
	if (GetRefCount() > 1)	//引用计数大于一	?  在使用的函数中判断减少压栈过程
	{
		GetRefCount()--;

		char* newstr = new char[strlen(_str) + 5];

		strcpy(newstr + 4, _str);
		_str = newstr + 4;
		GetRefCount() = 1;
	}
}

char& String::operator[](size_t pos)
{
	CopyOnWrte();
	return _str[pos];
}


void TestClass()
{
	String s1("hello world!");
	String s2(s1);
	String s3("this is czf");
	s1 = s3;
	String s4(s3);

	s3[3] = 'r';
}


int main()
{
	TestClass();
	return 0;
}
String.h
#pragma	once
#include <string.h>

#define NULL 0

class String
{
public:
	String(char * str = "")
		:_str(new char[strlen(str) + 5])
	{
		_str += 4;
		strcpy(_str, str);
		*((int *)(_str - 4)) = 1;
	}

	String(const String& s);
	String& operator=(const String& s); 
	~String();
	const char *c_str();
	void  CopyOnWrte();
	int& GetRefCount();
	char& operator[](size_t pos);

private:
	char *_str;
};



猜你喜欢

转载自blog.csdn.net/bian_cheng_ru_men/article/details/79958917
今日推荐