C++ String类写时拷贝

    维基百科:

    写入时复制(英语:Copy-on-write,简称COW)是一种计算机程序设计领域的优化策略。其核心思想是,如果有多个调用者(callers)同时要求相同资源(如内存或磁盘上的数据存储),他们会共同获取相同的指针指向相同的资源,直到某个调用者试图修改资源的内容时,系统才会真正复制一份专用副本(private copy)给该调用者,而其他调用者所见到的最初的资源仍然保持不变。这过程对其他的调用者都是透明的(transparently)。此作法主要的优点是如果调用者没有修改该资源,就不会有副本(private copy)被创建,因此多个调用者只是读取操作时可以共享同一份资源。

String类中的写时拷贝技术是指用浅拷贝的方法拷贝其他对象,多个指针指向同一块空间,只有当对其中一个对象修改时,才会开辟一个新的空间给这个对象,和它原来指向同一空间的对象不会受到影响。

可以通过增加一个成员变量count来实现写时拷贝,这个变量叫做引用计数,统计这块空间被多少个对象的_str同时指向。当用指向这块空间的对象拷贝一个新的对象出来时count+1,当指向这块空间的一个对象指向别的空间或析构时count-1。只有当count等于0时才可以释放这块空间,否则说明还有其他对象指向这块空间,不能释放。

count应该是什么类型呢?如果是int类型。

class String
{
	public:
	String(const char* str)
		:_str(new char[strlen(str)+1])
		,_count(1)
	{
		strcpy(_str, str);
	}

	String(String& s)
		:_str(s._str)
	{
		++s._count;
		_count = s._count;
	}

	~String()
	{
		if (--_count == 0)
		{
			delete[] _str;
		}
	}

private:
	char* _str;
	int _count;
};

void Test()
{
	String s1("aaaaaaaaa");
	String s2(s1);
}

虽然s1._count和s2._count都等于2,但是当s2执行析构函数后

现在只剩下s1一个对象指向这块空间,s1._count和s2._count应该都变为1,但是s1._count没有改变,查看s1._count和s2._count的地址发现它们并不是同一个地址,改变count只对当前对象有效,其他对象不会受到影响,无法实现引用计数。

这说明count是公共的,可以被多个对象同时访问的。如果是static int类型

class String
{
	public:
	String(const char* str)
		:_str(new char[strlen(str)+1])
	{
		_count = 1;
		strcpy(_str, str);
	}

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

	~String()
	{
		if (--_count == 0)
		{
			delete[] _str;
		}
	}

private:
	char* _str;
	static int _count;
};

int String::_count = 0;

void Test()
{
	String s1("aaaaaaaaa");
	String s2(s1);
	String s3(s2);

	String s4("bbbbbbbbb");
	String s5(s4);
}

现在s1、s2、s3的引用计数应该是3,s4、s5的引用计数应该是2。

但是结果不正确。原因是s1、s2、s3指向同一块空间后count增加到3,构造s4时又把count设置为1,s4拷贝构造s5后count增加到2。说明这5个对象共用一个count,不能实现引用计数。

如果一个对象第一次开辟空间存放字符串时再开辟一块新的空间存放引用计数,当它拷贝构造其他对象时让其他对象的引用计数都指向存放引用计数的同一块空间,count设置为int*类型,就可以实现引用计数了。

class String
	{
	public:
		String(const char* str)
			:_str(new char[strlen(str)+1])
			,_pCount(new int(1))
		{
			strcpy(_str, str);
		}

		String(String& s)
			:_str(s._str)
			,_pCount(s._pCount)
		{
			++(*_pCount);
		}

		String& operator=(const String& s)
		{
			if (/*this != &s ||*/ _str != s._str) //防止自己给自己赋值,或自己拷贝的对象给自己赋值
			{
				//释放原对象
				if (--(*_pCount) == 1)
				{
					delete _pCount;
					delete[] _str;
				}

				//浅拷贝增加引用计数
				_str = s._str;
				_pCount = s._pCount;
				++(*_pCount);
			}

			return *this;
		}

		~String()
		{
			if (--*_pCount == 0)
			{
				delete _pCount;
				delete[] _str;
			}
		}

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

但是这种方法也存在不足:

1、它每次new两块空间,创建多个对象时效率较低于下面这种方法;

2、它多次分配小块空间,容易造成内存碎片化,导致分配不出来大块内存。

时间: 2024-12-19 00:18:04

C++ String类写时拷贝的相关文章

String 类的实现(4)写时拷贝浅析

由于释放内存空间,开辟内存空间时花费时间,因此,在我们在不需要写,只是读的时候就可以不用新开辟内存空间,就用浅拷贝的方式创建对象,当我们需要写的时候才去新开辟内存空间.这种方法就是写时拷贝.这也是一种解决由于浅拷贝使多个对象共用一块内存地址,调用析构函数时导致一块内存被多次释放,导致程序奔溃的问题.这种方法同样需要用到引用计数:使用int *保存引用计数:采用所申请的4个字节空间. 1 #include<iostream> 2 #include<stdlib.h> 3 using

string类的写时拷贝

由于浅拷贝使多个对象共用一块内存地址,调用析构函数时导致一块内存被多次释放,导致程序奔溃. 实现string类的时候通常显示的定义拷贝构造函数和运算符重载函数. 由于释放内存空间,开辟内存空间时花费时间,因此,在我们在不需要写,只是读的时候就可以不用新开辟内存空间,就用浅拷贝的方式创建对象,当我们需要写的时候才去新开辟内存空间.这种方法就是写时拷贝. 在构造函数中开辟新的空间时多开辟4个字节的空间,用来存放引用计数器,记录这快空间的引用次数. [cpp] view plain copy #inc

标准C++类std::string的内存共享和Copy-On-Write(写时拷贝)

标准C++类std::string的内存共享,值得体会: 详见大牛:https://www.douban.com/group/topic/19621165/ 顾名思义,内存共享,就是两个乃至更多的对象,共同使用一块内存: 1.关于string的内存共享问题: 通常,string类中必有一个私有成员,其是一个char*,用户记录从堆上分配内存的地址,其在构造时分配内存,在析构时释放内存. 因为是从堆上分配内存,所以string类在维护这块内存上是格外小心的,string类在返回这块内存地址时,只返

简单的String类实现及写时拷贝

#include<iostream> using namespace std; class String { public: /*String(const char* str=" ") :_str(new char[strlen(str)+1]) { strcpy(_str, str); } */ String(const char* str = " ") { if (str == NULL) { _str = new char; _str[0] = '

string类的深浅拷贝,写时拷贝

浅拷贝:多个指针指向同一块空间,多次析构同一块内存空间,系统会崩溃.(浅拷贝就是值拷贝) 深拷贝:给指针开辟新的空间,把内容拷贝进去,每个指针都指向自己的内存空间,析构时不会内存崩溃. #include <iostream> #include <string> using namespace std; class String { public: String(const char*str) :_str(new char [strlen(str)+1]) { strcpy(_str

C++ String 写时拷贝

当类里面有指针对象时,采用简单的赋值浅拷贝,使得两个指针指向同一块内存,则析构两次,存在内存奔溃的问题,因此浅拷贝中利用引用计数. //引用计数浅拷贝 class String { public:  String(char*str = "")   :_str(new char[strlen(str) + 1])   , _pRefCount(new int(1))  {}  String(const String & s)   :_str(s._str)   , _pRefCo

String写时拷贝实现

头文件部分 1 /* 2 版权信息:狼 3 文件名称:String.h 4 文件标识: 5 摘 要:对于上版本简易的String进行优化跟进. 6 改进 7 1.(将小块内存问题与大块分别对待)小内存块每个对象都有,当内存需求大于定义大小时利用动态分配 8 2.实现大块内存的写时拷贝功能,提高效率,优化空间利用 9 3.类似new[]实现机制:将动态内存块大小信息保存为隐藏“头” 10 11 当前版本:1.2 12 修 改 者:狼 13 完成日期:2015-12-12 14 15 取代版本:1.

写时拷贝技术

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

写时拷贝 引用计数器模型

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