句柄类:智能指针

处理类

句柄类(也称为信封或柴郡猫类)是Bridge设计模式的一部分。 桥接模式的目标是将抽象与实现分开,以便两者可以独立变化。

句柄类通常包含一个指向对象实现的指针。 使用Handle对象而不是已实现的对象。 这使实现的对象可以自由更改,而不会影响Handle对象。 这正是指针发生的情况。 对象更改,但指针中的地址不变。

传递指针的问题之一是,您永远不知道删除指针是否安全。 如果指针是堆栈指针而您删除了,则会崩溃。 如果指针指向堆对象并且您将其删除,则指向的对象将被删除,这很好,除非程序中的某个位置仍然有另一个指针指向您刚刚删除的对象。 如果存在并且使用该指针,则会崩溃。 如果您放心使用它,并且从不删除它,那么您将死于一千次内存泄漏。

尽管Handle类的对象是对象,但它们的使用类似于指针。 这是通过重载取消引用运算符(*)和间接运算符(->)来实现的。 因为这些是对象,所以它们可以包含指向实现的指针之外的数据。 可能像其他计数一样指向其他对象。

包含指向同一对象的指针的句柄数的内部计数称为引用计数。 具有内部引用计数的句柄称为引用计数的句柄。

关于引用计数的规则是,当您要进行需要指针的函数调用时,需要增加该指针副本数的计数。 当您调用的函数即将返回时,它会减少计数。 如果计数现在为零,则该函数具有指针的最后一个副本,现在可以安全删除指针指向的对象。

这是一个句柄的示例:


#include <iostream>
using namespace std; 
//Handle classes or "Cheshire Cat" classes
//separate interface from implementation
//by using an abstract type. 
//The implementation
class MyClass
{ 
    private:
        int adata;
        int bdata;
    public:
        MyClass(int a, int b) : adata(a), bdata(b) { }
        void Methoda() {cout << "Methoda() " << adata << endl;}    
        void Methodb() {cout << "Methodb() " << bdata << endl;}
}; 
//The interface
class HandleToMyClass
{
    private:
        MyClass* imp;  
    public: 
        HandleToMyClass(int x, int y) : imp(new MyClass(x,y)) {  } 
        MyClass* operator->() {return imp;}  
}; 
int main()
{    
    /
    //The Handle class manages its own instnace of the Hidden class
    //
    HandleToMyClass hobj(10,20); 
    hobj->Methoda();    //Use hobj instead of passing copies of MyClass around
    hobj->Methodb(); 
      
    return 0;
} 
您可以很容易地看到hobj是一个对象,但是它在main()中使用,就好像它是一个指针一样。 参考计数手柄

引用计数跟踪指向同一实现对象的其他句柄对象的数量。 这可以通过增加构造函数中的计数并减少析构函数中的计数来完成。 当句柄析构函数中的计数变为零时,可以安全地删除实现对象。 计数本身也在堆上,因此它可以从句柄到句柄传播。

在下面的示例中,您可以看到计数的管理方式。 请注意在句柄分配运算符中,如何减少LVAL对象的计数并增加RVAL对象的计数。

此示例还添加了使用create函数创建句柄的功能。 您要避免创建对象,然后创建手柄,然后将对象指针放在手柄内。 避免这种情况的原因是:这是不安全的。 在某些时候,您将忽略其中一个步骤,或者决定只使用实现对象,而不必理会句柄。 此时,handle提供的整个安全网络将失败。

  
#include <iostream>
using namespace std; 
//A reference counting handle keeps track of the number of existing copies
//of the handle. The object managed by the handle is not destroyed until
//the destructor of the last handle object is called.  
class MyClass
{ 
    private:
        int adata;
        int bdata;
    public:
        MyClass(int a, int b) : adata(a), bdata(b) { }
        ~MyClass() {cout << "Egad! The MyClass object is gone!" << endl;}
        void seta(int in) {adata = in;}
        void setb(int in) {bdata = in;} 
        //Inserter
        friend ostream& operator<<(ostream& os, const MyClass& rhs);
};
//MyClass Inserter
ostream& operator<<(ostream& os, const MyClass& rhs)
{
    os << "adata: " << rhs.adata << " bdata: " << rhs.bdata;
    return os;
} 
class HandleToMyClass
{
    private:
        MyClass* imp;
        int* RefCount; 
    public: 
        HandleToMyClass() : imp(0), RefCount(new int(0)) { } 
        HandleToMyClass(int x, int y) : imp(new MyClass(x,y)),
                                        RefCount(new int(1)) {  } 
        //Destructor deletes managed object when reference count is zero
        ~HandleToMyClass(); 
        //Copy constructor increments the reference count
        HandleToMyClass(const HandleToMyClass& rhs); 
        //Assignment operator decrements lhs reference count and
        //increments rhs reference count
        HandleToMyClass& operator=(const HandleToMyClass& rhs); 
               //Support using the handle object as a pointer
        MyClass* operator->() {return imp;}
                MyClass& operator*() {return *imp;}
};   
//Destructor deletes managed object when reference count is zero
    HandleToMyClass::~HandleToMyClass()
{
    //RefCount can be zero if this handle was never assigned an object.
    //In this case subtracting can cause a negative value 
    if (--(*RefCount) <= 0)    //not thread-safe
    {
            delete imp;
            delete RefCount;
            imp = 0;
            RefCount = 0; 
    } 
} 
//Copy constructor increments the reference count
    HandleToMyClass::HandleToMyClass(const HandleToMyClass& rhs)
    : imp(rhs.imp), RefCount(rhs.RefCount)    //not thread-safe
{
    ++(*RefCount);
}  
//Assignment operator decrements lhs reference count and
//increments rhs reference count
HandleToMyClass& HandleToMyClass::operator=(const HandleToMyClass& rhs)
{
    if (this == &rhs) return *this; //no assignment to self 
    //Delete our current implementation (LVAL)
    HandleToMyClass::~HandleToMyClass(); 
    //This is our new implementation (RVAL):
    imp = rhs.imp;
    RefCount = rhs.RefCount;    //not thread-safe
    ++(*RefCount); 
    return *this;
}
//
//Use a create function to create both the MyClass object and its handle
//
HandleToMyClass CreateMyClassHandle(int a, int b)
{
    return HandleToMyClass(10,20);
} 
void Process(HandleToMyClass in)
{
        //Silly stuff to exercise the handle
    HandleToMyClass x; 
    x = in; 
    HandleToMyClass y(x); 
    y->seta(30);   //Use handle object as a pointer
} 
int main()
{    
    //Create the MyClass object and the handle
    //
    HandleToMyClass hobj = CreateMyClassHandle(10,20); 
    cout << *hobj << endl; 
    Process(hobj); 
    cout << *hobj << endl; 
      
    return 0;
} 
引用计数的句柄作为模板

一个Handle是模板的主要候选对象,因为所有Handle的行为相同,并且如果实现对象有所不同,则其类型也相同。

下面显示的是Handle作为模板,就像您在头文件中看到的那样。 它是从上一个示例中使用的HandleToMyClass编写的。

您应该能够在自己的代码中使用此Handle模板。


#ifndef  HANDLETEMPLATEH
#define HANDLETEMPLATEH 
//A reference counting handle keeps track of the number of existing copies
//of the handle. The object managed by the handle is not destroyed until
//the destructor of the last handle object is called. 
//Converting the handle to a template:
template<class T>
class Handle
{
    private:
        T* imp;
        int* RefCount; 
    public: 
        Handle() : imp(0), RefCount(new int(0)) { } 
        Handle(T* in) : imp(in), RefCount(new int(1)) {  } 
        //Destructor deletes managed object when reference count is zero
        ~Handle(); 
        //Copy constructor increments the reference count
        Handle(const Handle<T>& rhs); 
        //Assignment operator decrements lhs reference count and
        //increments rhs reference count
        Handle<T> operator=(const Handle<T>& rhs); 
        //Support using the handle object as a pointer
        T* operator->() {return imp;}
        T& operator*() {return *imp;}
}; 
//Destructor deletes managed object when reference count is zero
template<class T>
    Handle<T>::~Handle()
{
    //RefCount can be zero if this handle was never assigned an object.
    //In this case subtracting can cause a negative value
    if (--(*RefCount) <= 0)    //not thread-safe    {
                delete imp;
                delete RefCount;
                imp = 0;
                RefCount = 0; 
    }
} 
//Copy constructor increments the reference count
template<class T>
    Handle<T>::Handle(const Handle<T>& rhs)
    : imp(rhs.imp), RefCount(rhs.RefCount)    //not thread-safe
{
    ++(*RefCount);
}  
//Assignment operator decrements lhs reference count and
//increments rhs reference count
template<class T>
Handle<T> Handle<T>::operator=(const Handle<T>& rhs)
{
    if (this == &rhs) return *this; //no assignment to self 
    //Delete our current implementation (LVAL)
    Handle::~Handle(); 
    //This is our new implementation (RVAL):
    imp = rhs.imp;
    RefCount = rhs.RefCount;    //not thread-safe
    ++(*RefCount); 
    return *this;
}   
#endif  //end of #ifndef HANDLEREFCOUNTTEMPLATEH 
使用引用计数的句柄

本示例显示本文第一个示例中与MyClass一起使用的引用计数句柄。


void Process(Handle<MyClass> in)
{
        //Silly stuff to exercise the handle
    Handle<MyClass> x; 
    x = in; 
    Handle<MyClass> y(x); 
    y->seta(30);   //Use handle object as a pointer
} 
int main()
{    
    //Create the MyClass object and the handle
    //
    Handle<MyClass> hobj = CreateMyClassHandle(10,20); 
    cout << *hobj << endl; 
    Process(hobj); 
    cout << *hobj << endl; 
    //
    //Unused Handle
    //
    Handle<MyClass> unused; 
      
    return 0;
} 
使用句柄模板创建函数

您使用创建函数创建一个句柄。 假设您有一个Person类:


class Person
{
   private:
       string name;
       string address;
   public:
        Person(string name, string address);
        etc...
}; 
您将编写一个CreatePersonH​​andle函数,如下所示:

Handle<Person> hp = CreatePersonHandle("John Smith", "123 Fox St  Anytown USA"); 
创建函数在哪里:

Handle<Person> CreatePersonHandle(string name, string address)
{
     Person* temp = new Person(name, address);
     Handle<Person> rval(temp);
     return rval;
} 
将引用计数的句柄与STL容器一起使用

从这里开始,您将Handle <Person>用作Person *。 如果需要vector <Person>,现在可以创建vector <Handle <Person>>:


int main()
{
    Handle<Person? h1 = CreatePersonHandle("John Smith", "123 Fox St  Anytown USA");
    Handle<Person? h2 = CreatePersonHandle("Sue Collins", "James Boulevard  ACity USA"); 
    vector<Handle<Person> > database;
    database.push_back(h1);
    database.push_back(h2); 
    cout << *database[0] << endl;
    cout << *database[1] << endl; 
} 
这里的优点是向量是句柄的向量。 每个句柄只有两个指针:一个用于实现对象,一个用于计数。 制作副本只需要制作指针的副本,而不是实现对象本身。 这将使向量更小,并且当删除元素时,仅删除两个指针,除非那是最后一次删除实现对象的句柄。

容器往往会移动其内容物。 复制后,将调用容器中对象的复制构造函数。 几乎可以肯定,与复制句柄相比,这将花费更多时间。 同样,容器(如矢量)也不会轻易释放内存。 他们倾向于ho积它,以避免将来增加项目时分配更多的内存。 一个句柄只会使容器ard积两个指针的大小。

将句柄用作重载运算符

通常需要涉及比较对象的排序或其他过程。 在这些情况下,如果您有一个句柄容器,则将比较两个句柄。 这不是您想要的。 您需要比较手柄所指向的对象。

首先,不要将运算符添加到Handle模板中。

相反,您编写了一个具有Handle参数并返回正确的bool值的比较函数。 以上面的Person类为例,您可以:


bool operator<(Handle<Person> left, Handle<Person> right)
{
     if (*left < *right) return true;      //calls Person::operator<
     return false;
} 
如果Person类没有operator <,那么您将使用其他Person函数来完成比较:

bool operator<(Handle<Person> left, Handle<Person> right)
{
     if (left->GetName() < right->GetName()) return true;    
     return false;
} 

版权所有2007 Buchmiller Technical Associates美国北华盛顿本德

修订记录:

2008年2月6日:更正了句柄析构函数中的引用计数错误。

From: https://bytes.com/topic/c/insights/651599-handle-classes-smart-pointer

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值