目录
2.2 要在引用计数减为0后才能释放内存,但又出现了新的问题
1.引用计数与写时复制
1.1 引用计数
引用计数是计算机编程语言中的一种内存管理技术,是指将资源(可以是对象、内存或磁盘空间等等)的被引用次数保存起来,当被引用次数变为零时就将其释放的过程。使用引用计数技术可以实现自动资源管理的目的。同时引用计数还可以指使用引用计数技术回收未使用资源的垃圾回收算法。
最直观的垃圾收集策略是引用计数。引用计数很简单,但是需要编译器的重要配合,并且增加了赋值函数 (mutator) 的开销(这个术语是针对用户程序的,是从垃圾收集器的角度来看的)。每一个对象都有一个关联的引用计数 —— 对该对象的活跃引用的数量。如果对象的引用计数是零,那么它就是垃圾(用户程序不可到达它),并可以回收。每次修改指针引用时(比如通过赋值语句),或者当引用超出范围时,编译器必须生成代码以更新引用的对象的引用计数。如果对象的引用计数变为零,那么运行时就可以立即收回这个块(并且减少被回收的块所引用的所有块的引用计数),或者将它放到迟延收集队列中。
1.2 写时复制
写入时复制(Copy-on-write)是一个被使用在程序设计领域的最佳化策略。其基础的观念是,如果有多个呼叫者(callers)同时要求相同资源,他们会共同取得相同的指标指向相同的资源,直到某个呼叫者(caller)尝试修改资源时,系统才会真正复制一个副本(private copy)给该呼叫者,以避免被修改的资源被直接察觉到,这过程对其他的呼叫只都是通透的(transparently)。此作法主要的优点是如果呼叫者并没有修改该资源,就不会有副本(private copy)被建立。
2.写出一个比较好的用引用计数解决浅拷贝问题的代码
2.1 这个代码为什么会引发奔溃呢?
#include <string.h>
#include <iostream>
using namespace std;
class MyString
{
public:
MyString(const char *str = "")
{
if(str == NULL)
{
data = new char[1];
data[0] = '\0';
}
else
{
data = new char[strlen(str)+1];
strcpy(data,str);
}
useCount++;
}
MyString(const MyString& s)
{
data = s.data;
++useCount;
}
MyString& operator=(const MyString& s)
{
if(this!= &s)
{
data = s.data;
useCount++;
}
return *this;
}
~MyString()
{
cout << "~MyString " << data << endl;
delete []data;
data = NULL;
}
public:
char* data;
static int useCount;
};
int MyString::useCount = 0;
int main()
{
MyString s1("hello");
MyString s2 = s1;
cout << "s1.data's address is " << (void*)(s1.data) <<", s1.useCount = " << s1.useCount << endl;
cout << "s2.data's address is " << (void*)(s2.data) <<", s2.useCount = " << s2.useCount << endl;
}
2.2 要在引用计数减为0后才能释放内存,但又出现了新的问题
#include <string.h>
#include <iostream>
using namespace std;
class MyString
{
public:
MyString(const char *str = "")
{
if(str == NULL)
{
data = new char[1];
data[0] = '\0';
}
else
{
data = new char[strlen(str)+1];
strcpy(data,str);
}
useCount++;
}
MyString(const MyString& s)
{
data = s.data;
++useCount;
}
MyString& operator=(const MyString& s)
{
if(this!= &s)
{
data = s.data;
useCount++;
}
return *this;
}
~MyString()
{
if(--useCount == 0)
{
cout << "~MyString " << data << endl;
delete []data;
data = NULL;
}
}
public:
char* data;
static int useCount;
};
int MyString::useCount = 0;
int main()
{
MyString s1("hello");
MyString s2 = s1;
cout << "s1.data's address is " << (void*)(s1.data) <<", s1.useCount = " << s1.useCount << endl;
cout << "s2.data's address is " << (void*)(s2.data) <<", s2.useCount = " << s2.useCount << endl << endl;
MyString s3 = s1;
s3 = s2;
cout << "s1.data's address is " << (void*)(s1.data) <<", s1.useCount = " << s1.useCount << endl;
cout << "s2.data's address is " << (void*)(s2.data) <<", s2.useCount = " << s2.useCount << endl;
cout << "s3.data's address is " << (void*)(s3.data) <<", s3.useCount = " << s3.useCount << endl << endl;
MyString s4("hi");
cout << "s1.data's address is " << (void*)(s1.data) <<", s1.useCount = " << s1.useCount << endl;
cout << "s2.data's address is " << (void*)(s2.data) <<", s2.useCount = " << s2.useCount << endl;
cout << "s3.data's address is " << (void*)(s3.data) <<", s3.useCount = " << s3.useCount << endl;
cout << "s4.data's address is " << (void*)(s4.data) <<", s4.useCount = " << s4.useCount << endl;
}
2.3 引用计数的正确设计以及写时复制技术的使用
#include <string.h>
#include <iostream>
#include <stdio.h>
#include <unistd.h>
using namespace std;
class MyStringRef
{
public:
MyStringRef(const char *str = ""):useCount(0)
{
if(str == NULL)
{
data = new char[1];
data[0] = '\0';
}
else
{
data = new char[strlen(str)+1];
strcpy(data,str);
}
}
MyStringRef(const MyStringRef & ref):useCount(0)
{
data = new char[strlen(ref.data)+1];
strcpy(data,ref.data);
}
MyStringRef& operator=(const MyStringRef & ref)
{
if(this != &ref )
{
delete []data;
data = new char[strlen(ref.data)+1];
strcpy(data, ref.data);
}
return *this;
}
~MyStringRef()
{
delete []data;
data = NULL;
}
public:
void increment()
{
++useCount;
}
void decrement()
{
if(--useCount == 0)
{
/*
delete []data;
data = NULL;
*/
delete this;
}
}
void getDataAndAddrAndUseCount()
{
cout << "current data is " << data << "\t, current addr is " << (void*)data<< "\t, current useCount is " << useCount << endl;
}
private:
char * data;
int useCount;
};
/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
class MyString
{
public:
MyString(const char* str ="")
{
ref = new MyStringRef(str);
ref->increment();
ref->getDataAndAddrAndUseCount();
}
MyString(const MyString &s)
{
ref = s.ref;
ref->increment();
ref->getDataAndAddrAndUseCount();
}
MyString& operator=(const MyString & str)
{
if(this != &str)
{
ref->decrement();
ref = str.ref;
ref->increment();
}
return *this;
}
~MyString()
{
ref->decrement();
}
public:
MyStringRef* ref;
};
int main()
{
MyString s1("hello");
MyString s2 = s1;
MyString s3("hi");
MyString s4("say");
s2 = "hy";
s1.ref->getDataAndAddrAndUseCount();
s2.ref->getDataAndAddrAndUseCount();
s3.ref->getDataAndAddrAndUseCount();
s4.ref->getDataAndAddrAndUseCount();
}