前奏
在刚开始学习C++的时候,我师傅给了我一句话:好好看看《C++primer》中的句柄类。我一直记在心里,但是惭愧的是最近才真正实现这个东西,而且还不是很理解为什么要另外再定义一个句柄类来管理基类指针,所以发这篇博文的目的有两个:总结和解惑。总结自己的理解,解惑是希望牛人解答why。
实现句柄类的准备
在实现句柄类之前,先要理解下智能指针,当我们定义的类有指针成员时,如果两个或多个类对象中的指针成员指向了同一个对象,这个时候如果其中一个对象生命结束了,那他的指针成员所指向的对象会被析构函数删除,而另外几个对象的指针并不知道他们所指对象已经被别人删除了,不知情况的对象在执行析构函数的时候也会删除已经不存在的对象,这对于C++来说是不允许的,编译器报错,为了解决这个问题就引出了智能指针,其中一种解决方案就是使用计数器,另外定义一个计数器类,将类成员指针相互关联的对象绑定到同一个计数器对象,每多绑定一个指针,计数器增一,对象析构的时候只有当计数器减为零时才删除对象,这样
一来就可以确保在删除指针对象之前对象还存在,这就是大致思路了,具体代码如下:
类头文件class.h
#ifndef CLASS_H
#define CLASS_H 1
/*
*假如我们要实现一个字符串类,那这个类中就需要一个指向
*指向动态分配内存基地址的指针成员,这时候就需要使用智能指针
*/
class CCount; //声明一下
class string_str
{
public:
void get() const;
string_str(char *data=0);//在这里
string_str(const string_str &item);
~string_str();
string_str &operator=(const string_str &item);
private:
CCount *CData;//用计数器对象来管理这个指针
};
class CCount
{
friend class string_str;
CCount(char *data);
char *m_data;
unsigned count;
};
#endif
类定义的实现class.cpp
#include "class.h"
string_str::string_str(char *data):CData(new CCount(data))
{
}
string_str::string_str(const string_str &item)
{
CData=item.CData;//两个类对象绑定到同一个计数器
CData->count=item.CData->count;
++CData->count;//计数增一
}
string_str& string_str::operator =(const string_str &item)
{
/*
*赋值操作符的操作有些不同,详细分析下,因为当执行赋值操作时,
*说明两个类已经是分别绑了不同的计数器的,这个时候就要删除其中
*一个,那是删除哪个呢?由于右操作数为const引用,无法修改他的成员,
*所以我们只能删除左边对象绑定的计数器,将他绑定到右计数器上,同样在删除
*之前要检测下计数器是否减为零
*/
if(--CData->count==0)
delete CData;
++item.CData->count;
CData=item.CData;//将左对象计数器绑定到右对象计数器
CData->count=item.CData->count;
return *this;
}
string_str::~string_str()
{
if(--CData->count==0)//当计数器减为时删除计数对象
delete CData;
}
CCount::CCount(char *data):m_data(data),count(1)
{
}
测试代码test.cpp
#include "class.h"
#include <iostream.h>
void main()
{
string_str s1("hello");
string_str s2;
s2=s1;//调用赋值操作符
string_str s3=s2;//调用复制构造函数
}
句柄类建立在智能指针的基础上
以上就是智能指针的一种实现了,在句柄类中将用到另一种实现方式,但是思路是一样的,同样是使用计数,但是除了计数器,句柄类的目的是管理基类指针,C++的多态性发生在使用指向基类的指针或引用调用派生类成员函数时,所以在句柄类中就保存有一个指向基类的指针,这样一来就需要使用智能指针了,我们的目的是通过句柄类对象就可以获得多态性,比如:定义基类computer;派生类mobile,ipad,句柄类handle,通过handle(computer &c)->output();实现动态绑定。
类定义文件CLASS.H
#ifndef CLASS_H
#define CLASS_H 1
class computer
{
public:
virtual void output();
virtual computer* clone() const;//返回调用该函数的对象
};
class mobile:public computer
{
public:
virtual void output();
virtual mobile* clone() const;//返回调用该函数的对象
};
class ipad:public computer
{
public:
virtual void output();
virtual ipad* clone() const;//返回调用该函数的对象
};
class handle //句柄类
{
public:
handle();
handle(const handle &h);
handle(computer &c);
~handle();
handle operator=(const handle &h);
private:
int *count;//保存句柄类实的使用次数的指针
computer *base;
};
#endif
类定义实现CLASS.CPP
#include "CLASS.H"
#include <iostream.h>
void computer::output()
{
cout<<"computer"<<endl;
}
computer* computer::clone() const
{
return this;
}
void mobile::output()
{
cout<<"mobile"<<endl;
}
mobile* mobile::clone() const
{
return this;
}
void ipad::output()
{
cout<<"ipad"<<endl;
}
ipad* ipad::clone() const
{
return this;
}
handle::handle():count(new int(1)),base(NULL)
{
}
handle::handle(const handle &h):count(h.count),base(h.base)
{
++*count;
}
handle::handle(computer &c):count(new int(1)),base(c.clone())//动态返回调用函数的对象
{
}
handle::~handle()
{
if(0==--*count)
delete count;
}
handle handle::operator =(const handle &h)
{
if(0==--*count)
delete count;
++*h.count;
count=h.count;
base=h.base;
return *this;
}
测试代码text.cpp
#include <iostream.h>
#include "CLASS.H"
void main()
{
computer *c=NULL;
mobile m;
ipad i;
handle h1(m);
handle h2=h1;
}
总结
句柄类实现的关键就在那个clone()虚函数,在继承层次的每个类中增加一个clone()函数返回调用该函数的对象,这样句柄类就可以知道传递给handle(computer &c)中的c到底是哪个对象。实际使用句柄类时还需要重载句柄类的-〉操作符 或*操作符。
句柄类代码未测试过,有错莫怪!