473,725 Members | 2,053 Online
Bytes | Software Development & Data Engineering Community
+ Post

Home Posts Topics Members FAQ

Handle Classes: The Smart Pointer

weaknessforcats
9,208 Recognized Expert Moderator Expert
Handle Classes

Handle classes, also called Envelope or Cheshire Cat classes, are part of the Bridge design pattern. The objective of the Bridge pattern is to separate the abstraction from the implementation so the two can vary independently.

Handle classes usually contain a pointer to the object implementation. The Handle object is used rather than the implemented object. This leaves the implemented object free to change without affecting the Handle object. This is exactly what happens with pointers. The object changes but the address in the pointer does not.

One of the problems with passing pointers is that you never know if it's safe to delete the pointer. If the pointer is a stack pointer and you delete, you crash. If the pointer is to a heap object and you delete, the object pointed at is deleted and this is just fine unless there is another pointer somewhere in the program that still points to the object you just deleted. If there is and you use that pointer, you crash. If you play it safe and never delete you die a death by a thousand memory leaks.

Objects of Handle classes are used like pointers even though they are objects. This is accomplished by overloading the dereference operator (*) and the indirection operator (->). Because these are objects, they can contain data beyond the pointer to the implementation. Like maybe, a count of how may other handles also point to the same object.

An internal count of the number of handles containing a pointer to the same object is called a reference count. A handle with an internal reference count is called a reference counted handle.

The rule on reference counting is that when you are to make a function call that requires a pointer, you increment the count for the number of copies of that pointer. When then function you call is about to return, it decrements the count. If the count is now zero, the function has the last copy of the pointer and it is now safe to delete the object pointed at by the pointer.

Here is an example of a handle:

Expand|Select|Wrap|Line Numbers
  1. #include <iostream>
  2. using namespace std;
  3.  
  4. //Handle classes or "Cheshire Cat" classes
  5. //separate interface from implementation
  6. //by using an abstract type.
  7.  
  8. //The implementation
  9. class MyClass
  10. {
  11.  
  12.     private:
  13.         int adata;
  14.         int bdata;
  15.     public:
  16.         MyClass(int a, int b) : adata(a), bdata(b) { }
  17.         void Methoda() {cout << "Methoda() " << adata << endl;}    
  18.         void Methodb() {cout << "Methodb() " << bdata << endl;}
  19. };
  20.  
  21. //The interface
  22. class HandleToMyClass
  23. {
  24.     private:
  25.         MyClass* imp;
  26.  
  27.  
  28.     public:
  29.  
  30.         HandleToMyClass(int x, int y) : imp(new MyClass(x,y)) {  }
  31.  
  32.         MyClass* operator->() {return imp;}
  33.  
  34.  
  35. };
  36.  
  37. int main()
  38. {    
  39.     /////////////////////////////////////////////////////////////////////////
  40.     //The Handle class manages its own instnace of the Hidden class
  41.     //
  42.     HandleToMyClass hobj(10,20);
  43.  
  44.     hobj->Methoda();    //Use hobj instead of passing copies of MyClass around
  45.     hobj->Methodb();
  46.  
  47.     ////////////////////////////////////////////////////////////////////////
  48.  
  49.  
  50.     return 0;
  51. }
  52.  
You can easily see that hobj is an object but it is used in main() as if it were a pointer.

Reference Counted Handles

A reference count keeps track of the number of other handle objects pointing at the same implementation object. This is done by increasing the count in the constructor and decreasing the count in the destructor. When the count goes to zero inside the handle destructor, then it is safe to delete the implementation object. The count itself is also on the heap so it can travel from handle to handle.

In the example below you can see how the count is managed. Note in the handle assignment operator how the count of the LVAL object is decreased and the count of the RVAL object is increased.

Also added to this example is the use of a create function to create the handle. You want to avoid creating an object and then creating a handle and then putting the object pointer inside the handle. The reason to avoid this is: It's not safe. You, at some point, will omit one of the steps or decide to just use the implementation object without bothering about the handle. The entire security net provided by handle fails at this point.

Expand|Select|Wrap|Line Numbers
  1.  
  2.  
  3. #include <iostream>
  4. using namespace std;
  5.  
  6. //A reference counting handle keeps track of the number of existing copies
  7. //of the handle. The object managed by the handle is not destroyed until
  8. //the destructor of the last handle object is called.
  9.  
  10.  
  11. class MyClass
  12. {
  13.  
  14.     private:
  15.         int adata;
  16.         int bdata;
  17.     public:
  18.         MyClass(int a, int b) : adata(a), bdata(b) { }
  19.         ~MyClass() {cout << "Egad! The MyClass object is gone!" << endl;}
  20.         void seta(int in) {adata = in;}
  21.         void setb(int in) {bdata = in;}
  22.  
  23.         //Inserter
  24.         friend ostream& operator<<(ostream& os, const MyClass& rhs);
  25. };
  26. //MyClass Inserter
  27. ostream& operator<<(ostream& os, const MyClass& rhs)
  28. {
  29.     os << "adata: " << rhs.adata << " bdata: " << rhs.bdata;
  30.     return os;
  31. }
  32.  
  33. class HandleToMyClass
  34. {
  35.     private:
  36.         MyClass* imp;
  37.         int* RefCount;
  38.  
  39.     public:
  40.  
  41.         HandleToMyClass() : imp(0), RefCount(new int(0)) { }
  42.  
  43.         HandleToMyClass(int x, int y) : imp(new MyClass(x,y)),
  44.                                         RefCount(new int(1)) {  }
  45.  
  46.         //Destructor deletes managed object when reference count is zero
  47.         ~HandleToMyClass();
  48.  
  49.         //Copy constructor increments the reference count
  50.         HandleToMyClass(const HandleToMyClass& rhs);
  51.  
  52.         //Assignment operator decrements lhs reference count and
  53.         //increments rhs reference count
  54.         HandleToMyClass& operator=(const HandleToMyClass& rhs);
  55.  
  56.                //Support using the handle object as a pointer
  57.         MyClass* operator->() {return imp;}
  58.                 MyClass& operator*() {return *imp;}
  59. };
  60.  
  61.  
  62.  
  63. //Destructor deletes managed object when reference count is zero
  64.     HandleToMyClass::~HandleToMyClass()
  65. {
  66.     //RefCount can be zero if this handle was never assigned an object.
  67.     //In this case subtracting can cause a negative value
  68.  
  69.     if (--(*RefCount) <= 0)    //not thread-safe
  70.     {
  71.             delete imp;
  72.             delete RefCount;
  73.             imp = 0;
  74.             RefCount = 0;
  75.  
  76.     }
  77.  
  78. }
  79.  
  80. //Copy constructor increments the reference count
  81.     HandleToMyClass::HandleToMyClass(const HandleToMyClass& rhs)
  82.     : imp(rhs.imp), RefCount(rhs.RefCount)    //not thread-safe
  83. {
  84.     ++(*RefCount);
  85. }
  86.  
  87.  
  88. //Assignment operator decrements lhs reference count and
  89. //increments rhs reference count
  90. HandleToMyClass& HandleToMyClass::operator=(const HandleToMyClass& rhs)
  91. {
  92.     if (this == &rhs) return *this; //no assignment to self
  93.  
  94.     //Delete our current implementation (LVAL)
  95.     HandleToMyClass::~HandleToMyClass();
  96.  
  97.     //This is our new implementation (RVAL):
  98.     imp = rhs.imp;
  99.     RefCount = rhs.RefCount;    //not thread-safe
  100.     ++(*RefCount);
  101.  
  102.     return *this;
  103. }
  104. //
  105. //Use a create function to create both the MyClass object and its handle
  106. //
  107. HandleToMyClass CreateMyClassHandle(int a, int b)
  108. {
  109.     return HandleToMyClass(10,20);
  110. }
  111.  
  112. void Process(HandleToMyClass in)
  113. {
  114.         //Silly stuff to exercise the handle
  115.     HandleToMyClass x;
  116.  
  117.     x = in;
  118.  
  119.     HandleToMyClass y(x);
  120.  
  121.     y->seta(30);   //Use handle object as a pointer
  122. }
  123.  
  124. int main()
  125. {    
  126.     //Create the MyClass object and the handle
  127.     //
  128.     HandleToMyClass hobj = CreateMyClassHandle(10,20);
  129.  
  130.     cout << *hobj << endl;
  131.  
  132.     Process(hobj);
  133.  
  134.     cout << *hobj << endl;
  135.  
  136.     ////////////////////////////////////////////////////////////////////////
  137.  
  138.  
  139.     return 0;
  140. }
  141.  
Reference Counted Handles as a Template

A Handle is a prime candidate for a template since all Handles behave the same and only the type if the implementation object varies.

Shown below is the Handle as a template as you would see it in a header file. It was prepared from the HandleToMyClass used in the previous example.

You should be able to use this Handle template in your own code.

Expand|Select|Wrap|Line Numbers
  1. #ifndef  HANDLETEMPLATEH
  2. #define HANDLETEMPLATEH
  3.  
  4. //A reference counting handle keeps track of the number of existing copies
  5. //of the handle. The object managed by the handle is not destroyed until
  6. //the destructor of the last handle object is called.
  7.  
  8. //Converting the handle to a template:
  9. template<class T>
  10. class Handle
  11. {
  12.     private:
  13.         T* imp;
  14.         int* RefCount;
  15.  
  16.     public:
  17.  
  18.         Handle() : imp(0), RefCount(new int(0)) { }
  19.  
  20.         Handle(T* in) : imp(in), RefCount(new int(1)) {  }
  21.  
  22.         //Destructor deletes managed object when reference count is zero
  23.         ~Handle();
  24.  
  25.         //Copy constructor increments the reference count
  26.         Handle(const Handle<T>& rhs);
  27.  
  28.         //Assignment operator decrements lhs reference count and
  29.         //increments rhs reference count
  30.         Handle<T> operator=(const Handle<T>& rhs);
  31.  
  32.         //Support using the handle object as a pointer
  33.         T* operator->() {return imp;}
  34.         T& operator*() {return *imp;}
  35. };
  36.  
  37. //Destructor deletes managed object when reference count is zero
  38. template<class T>
  39.     Handle<T>::~Handle()
  40. {
  41.     //RefCount can be zero if this handle was never assigned an object.
  42.     //In this case subtracting can cause a negative value
  43.     if (--(*RefCount) <= 0)    //not thread-safe    {
  44.                 delete imp;
  45.                 delete RefCount;
  46.                 imp = 0;
  47.                 RefCount = 0;
  48.  
  49.     }
  50. }
  51.  
  52. //Copy constructor increments the reference count
  53. template<class T>
  54.     Handle<T>::Handle(const Handle<T>& rhs)
  55.     : imp(rhs.imp), RefCount(rhs.RefCount)    //not thread-safe
  56. {
  57.     ++(*RefCount);
  58. }
  59.  
  60.  
  61. //Assignment operator decrements lhs reference count and
  62. //increments rhs reference count
  63. template<class T>
  64. Handle<T> Handle<T>::operator=(const Handle<T>& rhs)
  65. {
  66.     if (this == &rhs) return *this; //no assignment to self
  67.  
  68.     //Delete our current implementation (LVAL)
  69.     Handle::~Handle();
  70.  
  71.     //This is our new implementation (RVAL):
  72.     imp = rhs.imp;
  73.     RefCount = rhs.RefCount;    //not thread-safe
  74.     ++(*RefCount);
  75.  
  76.     return *this;
  77. }
  78.  
  79.  
  80.  
  81. #endif  //end of #ifndef HANDLEREFCOUNTTEMPLATEH
  82.  
Using a Reference Counted Handle

This example shows the reference counted handle in use with MyClass from the first example in this article.
Expand|Select|Wrap|Line Numbers
  1. void Process(Handle<MyClass> in)
  2. {
  3.         //Silly stuff to exercise the handle
  4.     Handle<MyClass> x;
  5.  
  6.     x = in;
  7.  
  8.     Handle<MyClass> y(x);
  9.  
  10.     y->seta(30);   //Use handle object as a pointer
  11. }
  12.  
  13. int main()
  14. {    
  15.     //Create the MyClass object and the handle
  16.     //
  17.     Handle<MyClass> hobj = CreateMyClassHandle(10,20);
  18.  
  19.     cout << *hobj << endl;
  20.  
  21.     Process(hobj);
  22.  
  23.     cout << *hobj << endl;
  24.  
  25.     //
  26.     //Unused Handle
  27.     //
  28.     Handle<MyClass> unused;
  29.  
  30.     ////////////////////////////////////////////////////////////////////////
  31.  
  32.  
  33.     return 0;
  34. }
  35.  
Create Functions Using the Handle Template

You create a handle by using a create function. Let's assume you have a Person class:

Expand|Select|Wrap|Line Numbers
  1. class Person
  2. {
  3.    private:
  4.        string name;
  5.        string address;
  6.    public:
  7.         Person(string name, string address);
  8.         etc...
  9. };
  10.  
You would write a CreatePersonHan dle function as follows:

Expand|Select|Wrap|Line Numbers
  1. Handle<Person> hp = CreatePersonHandle("John Smith", "123 Fox St  Anytown USA");
  2.  
where the create function is:

Expand|Select|Wrap|Line Numbers
  1. Handle<Person> CreatePersonHandle(string name, string address)
  2. {
  3.      Person* temp = new Person(name, address);
  4.      Handle<Person> rval(temp);
  5.      return rval;
  6. }
  7.  
Using the Reference Counted Handle with an STL Container

From here on you use the Handle<Person> as a Person*. If you needed a vector<Person> you would now create a vector<Handle<P erson> >:

Expand|Select|Wrap|Line Numbers
  1. int main()
  2. {
  3.     Handle<Person? h1 = CreatePersonHandle("John Smith", "123 Fox St  Anytown USA");
  4.     Handle<Person? h2 = CreatePersonHandle("Sue Collins", "James Boulevard  ACity USA");
  5.  
  6.     vector<Handle<Person> > database;
  7.     database.push_back(h1);
  8.     database.push_back(h2);
  9.  
  10.     cout << *database[0] << endl;
  11.     cout << *database[1] << endl;
  12.  
  13. }
  14.  
The advantage here is the vector is a vector of handles. Each handle has only two pointers: One for the implementation object and one for the count. Making a copy just requires making copies of the pointers rather than the implementation object itself. This will make for a much smaller vector and when elements are deleted, only the two pointers are deleted unless that was the last handle at which time the implementation object is also deleted.

Containers tend to move their contents around. When copies are made, the copy constructor of the objects in the container is called. Almost certainly this will take more time than to copy a Handle. Also, containers, like vector, do not readily release memory. They tend to hoard it to avoid more memory allocations if items are added in the future. A Handle just causes the container to hoard the sizeof two pointers.

Using Handles as Overloaded Operators

Often a sort or some other process is required that involves comparing objects. In these cases, if you have a container of handles, you will be comparing two handles. This is not what you want. You need to compare the objects pointed at by the handles.

First, you do not add operator functions to the Handle template.

Instead, you write a compare function that has Handle arguments and returns the correct bool value. Using the Person class, above, as an example you could:

Expand|Select|Wrap|Line Numbers
  1. bool operator<(Handle<Person> left, Handle<Person> right)
  2. {
  3.      if (*left < *right) return true;      //calls Person::operator<
  4.      return false;
  5. }
  6.  
If the Person class does not have an operator<, then you would use other Person functions to accomplish the comparison:

Expand|Select|Wrap|Line Numbers
  1. bool operator<(Handle<Person> left, Handle<Person> right)
  2. {
  3.      if (left->GetName() < right->GetName()) return true;    
  4.      return false;
  5. }
  6.  

Copyright 2007 Buchmiller Technical Associates North Bend WA USA

Revision History:
Feb 6, 2008: Corrected reference count error in Handle destructor.
May 23 '07 #1
2 35596
Peterwkc
55 New Member
Nice article.

By the way, why u create function create handle rather than inside the constructor.

Another problem is
Expand|Select|Wrap|Line Numbers
  1. template<class T>
  2. Handle<T> Handle<T>::operator=(const Handle<T>& rhs)
  3. {
  4.     if (this == &rhs) return *this; //no assignment to self
  5.  
  6.     //Delete our current implementation (LVAL)
  7.     Handle::~Handle();
  8.  
  9.     //This is our new implementation (RVAL):
  10.     imp = rhs.imp;
  11.     RefCount = rhs.RefCount;    //not thread-safe
  12.     ++(*RefCount);
  13.  
  14.     return *this;
  15. }
Why inside assignment operator, why we need to always delete first before assign ?

a = b;

Do we delete a or b ?

Sorry for my stupidity.

Thanks for your help.
Mar 2 '08 #2
weaknessforcats
9,208 Recognized Expert Moderator Expert
I use a create function to create, initialize and return a handle to your object. I did this to be sure the Handle was created on the heap.

As to deleting before assigning, think about it. If you are going to replace the current contents of an object with new contents, then you need to delete the current contents to avoid a memory leak. In your example of a = b, it is the contents of a that need to be deleted. Deleting the contents of b would delete the data you want to assign to a.
Mar 4 '08 #3

Sign in to post your reply or Sign up for a free account.

Similar topics

7
3254
by: Tony Johansson | last post by:
Hello!! Assume I have a handle body pattern with classes called Handle and Body. In the Body class I store one int value for example 7 or some other integer value. In the Handle class I have a pointer to the Body class. If a want to create a STL container of List with the following declaration List <Handle <Body> > list
2
3051
by: Indiana Epilepsy and Child Neurology | last post by:
Before asking this questions I've spent literally _years_ reading (Meyer, Stroustrup, Holub), googling, asking more general design questions, and just plain thinking about it. I am truly unable to figure out what would be a "proper" OO design (in C++) for this. There may be alternatives to writing my own ODBC handle classes, and I may be interested in them, but I'd like to pursue this particular problem, if for no other reason than to...
14
2728
by: Howard | last post by:
Hi, I recently had a problem where I decided to store objects in a vector. (Previously, I had always stored pointers in vectors). Well, naturally, when storing an object in a vector, using push_back, the object I had in hand was getting copied (twice, in fact). That led to a problem, in that my object contained a "handle" to another object, and when the object being pushed went out of scope and was destroyed, the referenced object was...
7
4097
by: caxmester | last post by:
Ok this shouldn't be nearly this hard, but i've checked EVERYWHERE from msdn to scouring google & I can't figure it out. I basically have a vector of pointers to "aaWord" objects, each of which contain titles (as strings) and I want to order the vector alphabetically by those strings. I'm using __gc and pointers and all that. Visual C++ .NET vector<gcroot<aaWord __gc*> > *VectorofWords;
8
5149
by: Axter | last post by:
I normally use a program call Doxygen to document my source code.(http://www.stack.nl/~dimitri/doxygen) This method works great for small and medium size projects, and you can get good documentation like the following: http://axter.com/smartptr Now I'm on a client site, and I'm trying to create the same type of documentation on a very large project. I ran the Doxygen program, and it ran for over 16 hours, before I had
5
2335
by: mancomb | last post by:
Hi, I'm curious to the syntax of calling member functions through pointers of classes returned through the -operator. For example (excuse the crude incomplete code); Here are the classes: Class Foo{
33
5065
by: Ney André de Mello Zunino | last post by:
Hello. I have written a simple reference-counting smart pointer class template called RefCountPtr<T>. It works in conjunction with another class, ReferenceCountable, which is responsible for the actual counting. Here is the latter's definition: // --- Begin ReferenceCountable.h ---------- class ReferenceCountable
7
40632
by: Shraddha | last post by:
What is exactly a handle to an object????Is it same as reference....
8
1642
by: Oliver Graeser | last post by:
Hi All, I'm coming from Java to C++ and this is one of the very last problems I have so far... In Java, if I have, say, a class SISNode that extends NetworkNode, I can have a function that returns a NetworkNode but I can assure the compiler that it is in fact a SISNode and therefore call the method getStatus() that only a SISNode has. Like SISnode s,t; NetworkNode n;
0
8888
marktang
by: marktang | last post by:
ONU (Optical Network Unit) is one of the key components for providing high-speed Internet services. Its primary function is to act as an endpoint device located at the user's premises. However, people are often confused as to whether an ONU can Work As a Router. In this blog post, we’ll explore What is ONU, What Is Router, ONU & Router’s main usage, and What is the difference between ONU and Router. Let’s take a closer look ! Part I. Meaning of...
0
8752
by: Hystou | last post by:
Most computers default to English, but sometimes we require a different language, especially when relocating. Forgot to request a specific language before your computer shipped? No problem! You can effortlessly switch the default language on Windows 10 without reinstalling. I'll walk you through it. First, let's disable language synchronization. With a Microsoft account, language settings sync across devices. To prevent any complications,...
0
9401
Oralloy
by: Oralloy | last post by:
Hello folks, I am unable to find appropriate documentation on the type promotion of bit-fields when using the generalised comparison operator "<=>". The problem is that using the GNU compilers, it seems that the internal comparison operator "<=>" tries to promote arguments from unsigned to signed. This is as boiled down as I can make it. Here is my compilation command: g++-12 -std=c++20 -Wnarrowing bit_field.cpp Here is the code in...
0
9111
tracyyun
by: tracyyun | last post by:
Dear forum friends, With the development of smart home technology, a variety of wireless communication protocols have appeared on the market, such as Zigbee, Z-Wave, Wi-Fi, Bluetooth, etc. Each protocol has its own unique characteristics and advantages, but as a user who is planning to build a smart home system, I am a bit confused by the choice of these technologies. I'm particularly interested in Zigbee because I've heard it does some...
0
8096
agi2029
by: agi2029 | last post by:
Let's talk about the concept of autonomous AI software engineers and no-code agents. These AIs are designed to manage the entire lifecycle of a software development project—planning, coding, testing, and deployment—without human intervention. Imagine an AI that can take a project description, break it down, write the code, debug it, and then launch it, all on its own.... Now, this would greatly impact the work of software developers. The idea...
1
6702
isladogs
by: isladogs | last post by:
The next Access Europe User Group meeting will be on Wednesday 1 May 2024 starting at 18:00 UK time (6PM UTC+1) and finishing by 19:30 (7.30PM). In this session, we are pleased to welcome a new presenter, Adolph Dupré who will be discussing some powerful techniques for using class modules. He will explain when you may want to use classes instead of User Defined Types (UDT). For example, to manage the data in unbound forms. Adolph will...
0
6011
by: conductexam | last post by:
I have .net C# application in which I am extracting data from word file and save it in database particularly. To store word all data as it is I am converting the whole word file firstly in HTML and then checking html paragraph one by one. At the time of converting from word file to html my equations which are in the word document file was convert into image. Globals.ThisAddIn.Application.ActiveDocument.Select();...
0
4517
by: TSSRALBI | last post by:
Hello I'm a network technician in training and I need your help. I am currently learning how to create and manage the different types of VPNs and I have a question about LAN-to-LAN VPNs. The last exercise I practiced was to create a LAN-to-LAN VPN between two Pfsense firewalls, by using IPSEC protocols. I succeeded, with both firewalls in the same network. But I'm wondering if it's possible to do the same thing, with 2 Pfsense firewalls...
0
4782
by: adsilva | last post by:
A Windows Forms form does not have the event Unload, like VB6. What one acts like?

By using Bytes.com and it's services, you agree to our Privacy Policy and Terms of Use.

To disable or enable advertisements and analytics tracking please visit the manage ads & tracking page.