C++句柄类

来源:互联网 发布:混血 知乎 编辑:程序博客网 时间:2024/05/07 17:01

一直不能理解句柄类究竟有什么作用,尽管看到很多C++书籍中对句柄的使用进行了介绍。直到最近碰到一个问题才让我对句柄类的使用有了较为深入地了解。

这个问题简单来说是这样:设想一个类封装了一种数据结构,当我们声明该类的一个指针对象来指向这种数据结构的一块数据时,有两种情况:

1. 对数据块进行读取操作,并不改变数据的值

2. 对数据块进行改写操作,但要保留原数据块(因为其他地方要使用)


对于第一种情况,我们只需要构造一个类的指针对象,将他指向这个数据块。

对于第二种情况,问题有些不同。首先,需要在类的构造函数中重新申请内存将数据块拷贝过来,然后进行改写。但问题在于什么时候去申请新的数据块我们并不清楚。根据“写时复制”的原则,只有在我们需要改写数据块的时候才应该去重新申请内存。倘若一直没有对数据块进行改写,所有类的指针对象可以共享这个数据块中的内容。

这种内存管理的思想被许多程序框架使用,例如Qt中的隐式共享,在内容有变动的情况下才对的数据结构做复制,否则仅做共享。看一个例子:

QString str1 = "one"; //str1指向"one"QString str2 = str1; //str2没有改变"one",此时"one"被共享两次
此时,指向"one"的计数器的值为2。现在,倘若我们改变str2指向的内容

str2 = "two";  //改变str2的内容
str2指向了新的数据块,因此"one"的计数减为1,"two"计数变为1。此时在改变str1的值
str1 = str2;
此时,"two"的计数增加到2,"one"计数变为0。这种对内存管理的方式可以用一个新的类来表示,这个类就是句柄。

简单来说,句柄就将指向数据的指针和该数据共享次数封装起来,我们直接看一个例子,假设有一个坐标类Point,建立一个Handle类来管理Point

Point类很简单,有两个成员(x,y)

class Handle;class Point{public:friend Handle;Point(int a=0, int b=0):x(a), y(b){};~Point();Point& operator =(const Point &rhs);public:int x;int y;};Point::~Point(){}Point& Point::operator =(const Point &rhs){    x = rhs.x+1;    y = rhs.y+1;    return *this;}
句柄类

class Handle{public:Handle();Handle(int x, int y);Handle(const Point &Data);~Handle();Handle &operator =(const Handle &rhs);private:Point *data;int *num;             //表示*data被共享的次数void destroy();};
Handle::Handle(){num = new int(1);   data = new Point(); //缺省复制构造函数将Point初始化为远点坐标 }Handle::Handle(int x, int y){num = new int(1);data = new Point(x,y);}Handle::Handle(const Point &Data){num = new int(1);data = new Point(Data);}Handle::~Handle(){destroy();}void Handle::destroy(){if(--*num == 0)             //如果指向数据的指针只有一个,则直接delete{                           //因为如果改变data指向的数据,则指向这块数据的指针个数为0delete data;data = 0;delete num;num = 0;}}Handle &Handle::operator =(const Handle &rhs){(*rhs.num)++;              //首先将右操作数的计数器加1,destroy();               data = rhs.data;num = rhs.num;return *this;}

int main(void){Handle h(1,1);Handle h1;Handle h2;std::cout<<"h.num = "<<*h.num<<"    h1.num = "<<*h1.num<<"    h2.num = "<<*h2.num<<std::endl; //三个对象引用计数都为1h1 = h;      //指向h.data的引用计数变为2std::cout<<"h.num = "<<*h.num<<"    h1.num = "<<*h1.num<<"    h2.num = "<<*h2.num<<std::endl; h1 = h2;    //指向h2.data的引用计数变为2,h.num减少为1std::cout<<"h.num = "<<*h.num<<"    h1.num = "<<*h1.num<<"    h2.num = "<<*h2.num<<std::endl;return 0; }
打印结果

h.num = 1    h1.num = 1    h2.num = 1

h.num = 2    h1.num = 2    h2.num = 1

h.num = 1    h1.num = 2    h2.num = 2

这样就实现了写时复制和对指针的动态管理,节省了复制数据时的开销






0 0
原创粉丝点击