写时拷贝(方案三)

以下是其它方案链接地址:

方案一:

http://iynu17.blog.51cto.com/10734157/1755179

方案二:

http://iynu17.blog.51cto.com/10734157/1755185

方案四:(推荐)

http://iynu17.blog.51cto.com/10734157/1755213

方案三

class String
{
    private:
               char* _str;
               size_t  _size;
               size_t  _capacity;
               int* _refCount;       //***
};

方案三设置了一个int型的指针变量用来引用计数,每份内存空间对应一个引用计数,而不是每个对象对应一个引用计数,而且每块内存的引用计数互不影响,不会出现方案一和方案二出现的问题。


1.在实现赋值运算符重载要谨慎,不要遇到下图的情形


2.改变字符串的某个字符时要谨慎,不要遇到类似下图所遇到的问题。

如果多个对象都指向同一块内存,那么只要一个对象改变了这块内存的内容,那所有的对象都被改变了!!

可以用下图的形式改善这种问题:新设置一块内存来存要改变的对象

案例3我画的图较多,方便大家结合代码去理解 

#define _CRT_SECURE_NO_WARNINGS 1

#include<iostream>
using namespace std;
#include<assert.h>

class String
{
public:
	String(char* str = "")    //不能strlen(NULL)
	{
		_refCount = new int(1);     //给_refCount开辟空间,并赋初值1
		_size = strlen(str);
		_capacity = _size + 1;
		_str = new char[strlen(str) + 1];
		strcpy(_str, str);
	}
	String(const String &s)
	{
		_refCount = s._refCount;
		_str = s._str;
		_size = strlen(s._str);
		_capacity = _size + 1;
		(*_refCount)++;      //拷贝一次_refCount都要加1

	}

	//要考虑是s1=s2时,s1原先不为空的情况,要先释放原内存
	//如果要释放原内存时,要考虑它的_refCount减1后是否为0,为零再释放,否则其它对象指针无法再访问这片空间
	String& operator=(String& s)  
	{
		if (_str!= s._str)
		{
			_size = strlen(s._str);
			_capacity = _size + 1;
			if (--(*_refCount) == 0)
			{
				delete[] _str;
				delete _refCount;
			}

			_str = s._str;
			_refCount = s._refCount;
			(*_refCount)++;
		}
		return *this;
	}
	//如果修改了字符串的内容,那所有指向这块内存的对象指针的内容间接被改变
	//如果还有其它指针指向这块内存,我们可以从堆上重新开辟一块内存空间,
	//把原字符串拷贝过来
	//再去改变它的内容,就不会产生链式反应
	//  1.减引用计数  2.拷贝   3.创建新的引用计数
	char& String::operator[](const size_t index) //参考深拷贝      
	{
		if (*_refCount==1)
		{
			return *(_str + index);
		}
		else
		{
			--(*_refCount);
			char* tmp = new char[strlen(_str)+1];
			strcpy(tmp, _str);
			_str = tmp;
			_refCount = new int(1);
			return *(_str+index);
		}
	}
	~String()
	{
		if (--(*_refCount)== 0)  //当_refCount=0的时候就释放内存
		{
			delete[] _str;
			delete _refCount;
			_str = NULL;
			cout << "~String " << endl;
		}
		_size = 0;
		_capacity = 0;
	}
	friend ostream& operator<<(ostream& output, const String &s);
	friend istream& operator>>(istream& input, const String &s);
private:
    char* _str;      //指向字符串的指针
    size_t  _size;      //字符串大小
    size_t  _capacity;   //容量
    int* _refCount;    //计数指针
};

ostream& operator<<(ostream& output, const String &s)
{
	output << s._str;
	return output;
}
istream& operator>>(istream& input, const String &s)
{
	input >> s._str;
	return input;
}

void Test()    //用例测试
{
	String s1("abcdefg");
	String s2(s1);
	String s3;
	s3 = s2;
	cout << s1 << endl;
	cout << s2 << endl;
	cout << s3 << endl;
	s2[3] = ‘0‘;
	cout << s1 << endl;
	cout << s2 << endl;
	cout << s3 << endl;

	//String s4("opqrst");
	//String s5(s4);
	//String s6 (s5);
	//s6 = s4;
	//cout << s4 << endl;
	//cout << s5 << endl;
	//cout << s6 << endl;

}
int main()
{
	Test();
	system("pause");
	return 0;
}
时间: 2024-08-25 18:58:25

写时拷贝(方案三)的相关文章

写时拷贝(方案一)

深拷贝效率低,我们可以应引用计数的方式去解决浅拷贝中析构多次的问题. 首先要清楚写时拷贝是利用浅拷贝来解决问题!! 方案一 class String { private:     char* _str;     int _refCount; }; 方案一最不靠谱,它将用作计数的整形变量_refCount定义为类的私有成员变量,任何一个对象都有它自己的成员变量_refCount,它们互不影响,只要拷贝出了对象,_refCount大于了1,那么每个对象调用自己的析构函数时--_refCount不等于

分析 写时拷贝 的四个方案(Copy On Write)

深拷贝效率低,我们可以应引用计数的方式去解决浅拷贝中析构多次的问题. 首先要清楚写时拷贝是利用浅拷贝来解决问题!! 方案一 class String { private: char* _str; int _refCount; }; 方案一最不靠谱,它将用作计数的整形变量_refCount定义为类的私有成员变量,任何一个对象都有它自己的成员变量_refCount,它们互不影响,只要拷贝出了对象,_refCount大于了1,那么每个对象调用自己的析构函数时--_refCount不等于0,那么它们指向

写时拷贝技术

Copy On Write(COW):写时拷贝技术 一.什么是写时拷贝技术: 写时拷贝技术可以理解为"写的时候才去分配空间",这实际上是一种拖延战术. 举个栗子: 二.写时拷贝技术原理: 写时拷贝技术是通过"引用计数"实现的,在分配空间的时候多分配4个字节,用来记录有多少个指针指向块空间,当有新的指针指向这块空间时,引用计数加一,当要释放这块空间时,引用计数减一(假装释放),直到引用计数减为0时才真的释放掉这块空间.当有的指针要改变这块空间的值时,再为这个指针分配自

写时拷贝(copy-on-write) COW技术

时间:2014.05.06 地点:基地二楼 ---------------------------------------------------------------------------------- 一.写时拷贝的概念--COW技术在Linux进程上的应用 Linux在使用fork()函数进程创建时,传统fork()的做法是系统把所有的资源复制给新创建的进程,这种方式不仅单一,而且效率低下.因为所拷贝的数据或别的资源可能是可以共享的.现在Linux的fork()使用写时拷贝页来实现新进

难道是“写时拷贝”?

前言: 1 #if 0 2 3 其实,现在我要做的这件事情,是有个前提的, 4 有一天晚上,我和一个朋友讨论一个相关技术的问题, 5 (因为我也不是很懂,我不确定我的观点是正确的,所以才是讨论), 6 我们聊到了,Windows的映射机制, 7 我们模拟的场景是这样的: 8 (简单场景,x86环境下,非x64的复杂场景) 9 系统中有个进程A,有个进程B,进程A加载了一个系统DLL,B进程也加载了一个系统DLL(如ntdll,kernel32等等), 10 这时,系统里面的这个DLL的内存是怎样

2.由深拷贝和浅拷贝引发的写时拷贝技术

一.相关问题 我们知道,浅拷贝会引发重复释放,导致程序崩溃.而深拷贝虽不会出现上述问题,但是内存空间开销大.所以,有没有一种方法, 既节省空间,又不会造成程序崩溃呢? 二.写时拷贝技术 写时拷贝(Copy-on-write):如果有多个呼叫者同时要求相同资源,他们会共同取得相同的指标指向相同的资源,直到某个呼叫者                                        尝试修改时,才会复制一个副本给该呼叫者. 三.例子

深拷贝&amp;浅拷贝&amp;引用计数&amp;写时拷贝

(1).浅拷贝: class String { public: String(const char* str="") :_str(new char[strlen(str)+1]) { strcpy(_str,str); } ~String() { if(NULL!=_str) { delete[] _str; _str=NULL; } } private: char* _str; }; int main() { String s1("hello"); String

Linux写时拷贝技术(copy-on-write)

1.传统的fork()函数创建一个子进程,子进程和父进程共享正文段,复制数据段,堆,栈到子进程示意图如下: 2.Linux的fork()函数-写时复制(copy-on-write)创建一个子进程,内核只为子进程创建虚拟空间,不分配物理内存,和父进程共享物理空间,当父进程中有更改相应段的行为发生时,才为子进程分配物理空间.示意图如下: 3.vfork()函数创建一个子进程,共享父进程的一切.示意图如下: 4.传统fork与copy-on-write区别 传统的fork函数直接把所有资源复制给新的进

写时拷贝 引用计数器模型

1.深浅拷贝的使用时机: 浅拷贝:对只读数据共用一份空间,且只释放一次空间: 深拷贝:数据的修改,的不同空间: 2.引用计数器模型 使用变量use_count,来记载初始化对象个数: (1).static模型(此处只用浅拷贝与浅赋值) #include<iostream> #include<string.h> #include<malloc.h> using namespace std; class String{ public:     String(const ch

Linux写时拷贝技术【转】

本文转载自:http://www.cnblogs.com/biyeymyhjob/archive/2012/07/20/2601655.html COW技术初窥: 在Linux程序中,fork()会产生一个和父进程完全相同的子进程,但子进程在此后多会exec系统调用,出于效率考虑,linux中引入了“写时复制“技术,也就是只有进程空间的各段的内容要发生变化时,才会将父进程的内容复制一份给子进程. 那么子进程的物理空间没有代码,怎么去取指令执行exec系统调用呢? 在fork之后exec之前两个进