Home arrow C++ arrow Page 11 - A Simple Garbage Collector for C++
C++

A Simple Garbage Collector for C++


The use of dynamically allocated memory must be managed, because it has a tremendous effect on the performance of your programs. The current trend in handling dynamic memory seems to be shifting toward an automated approach. While C++ uses the manual approach for managing dynamic memory, this does not mean that it can't be automated in that language -- thus giving the C++ programmer the best of both worlds. This article explains how to do it. It is excerpted from chapter two of The Art of C++, written by Herbert Schildt (McGraw-Hill/Osborne, 2004; ISBN: 0072255129).

Author Info:
By: McGraw-Hill/Osborne
Rating: 4 stars4 stars4 stars4 stars4 stars / 73
June 21, 2005
TABLE OF CONTENTS:
  1. · A Simple Garbage Collector for C++
  2. · Comparing the Two Approaches to Memory Management
  3. · Choosing a Garbage Collection Algorithm
  4. · What About auto_ptr?
  5. · An Overview of the Garbage Collector Classes
  6. · GCPtr In Detail
  7. · The Overloaded Assignment Operators
  8. · GCInfo
  9. · How to Use GCPtr
  10. · Allocating Arrays
  11. · A Larger Demonstration Program
  12. · Load Testing

print this article
SEARCH DEVARTICLES

A Simple Garbage Collector for C++ - A Larger Demonstration Program
(Page 11 of 12 )

The following program shows a larger example that exercises all of the features of GCPtr:

// Demonstrating GCPtr.
#include <iostream>
#include <new>
#include "gc.h"
using namespace std;
// A simple class for testing GCPtr with class types.
class MyClass {
  int a, b;
public:
  double val;
 
MyClass() { a = b = 0; }
 
MyClass(int x, int y) {
    a = x;
    b = y;
    val = 0.0;
 
}
 
~MyClass() {
    cout << "Destructing MyClass(" <<
         a << ", " << b << ")\n";
  }
 
int sum() {
    return a + b;
  }
 
friend ostream &operator<<(ostream &strm, MyClass &obj);
};
// Create an inserter for MyClass.
ostream &operator<<(ostream &strm, MyClass &obj) {
  strm << "(" << obj.a << " " << obj.b << ")";
  return strm;
}
// Pass a normal pointer to a function.
void passPtr(int *p) {
  cout << "Inside passPtr(): "
       << *p << endl;
}
// Pass a GCPtr to a function.
void passGCPtr(GCPtr<int, 0> p) {
  cout << "Inside passGCPtr(): "
       << *p << endl;
}
int main() {
 
try {
    // Declare an int GCPtr.
    GCPtr<int> ip;
   
// Allocate an int and assign its address to ip.
    ip = new int(22);
   
// Display its value.
    cout << "Value at *ip: " << *ip << "\n\n";
   
// Pass ip to a function
    passGCPtr(ip);
   
// ip2 is created and then goes out of scope 
    {
     GCPtr<int> ip2 = ip;
    }
   
int *p = ip; // convert to int * pointer'
   
passPtr(p); // pass int * to passPtr()
   
*ip = 100; // Assign new value to ip
   
// Now, use implicit conversion to int *
    passPtr(ip);
    cout << endl;
   
// Create a GCPtr to an array of ints
    GCPtr<int, 5> iap = new int[5];
   
// Initialize dynamic array.
    for(int i=0; i < 5; i++)
      iap[i] = i;
   
// Display contents of array.
    cout << "Contents of iap via array indexing.\n";
    for(int i=0; i < 5; i++)
     
cout << iap[i] << " ";
    cout << "\n\n";
   
// Create an int GCiterator.
    GCPtr<int>::GCiterator itr;
   
// Now, use iterator to access dynamic array.
    cout << "Contents of iap via iterator.\n";
    for(itr = iap.begin(); itr != iap.end(); itr++)
     
cout << *itr << " ";
    cout << "\n\n";
   
// Generate and discard many objects
    for(int i=0; i < 10; i++)
      ip = new int(i+10);
   
// Now, manually garbage collect GCPtr<int> list.
    // Keep in mind that GCPtr<int, 5> pointers
    // will not be collected by this call.
    cout << "Requesting collection on GCPtr<int> list.\n";
    GCPtr<int>::collect();
   
// Now, use GCPtr with class type.
    GCPtr<MyClass> ob = new MyClass(10, 20);
   
// Show value via overloaded insertor.
    cout << "ob points to " << *ob << endl;
   
// Change object pointed to by ob.
    ob = new MyClass(11, 21);
    cout << "ob now points to " << *ob << endl;
   
// Call a member function through a GCPtr.
    cout << "Sum is : " << ob->sum() << endl;
   
// Assign a value to a class member through a GCPtr. 
    ob->val = 19.21;
    cout << "ob->val: " << ob->val << "\n\n";
   
cout << "Now work with pointers to class objects.\n";
   
// Declare a GCPtr to a 5-element array
    // of MyClass objects.
    GCPtr<MyClass, 5> v;
    // Allocate the array.
    v = new MyClass[5];
   
// Get a MyClass GCiterator.
    GCPtr<MyClass>::GCiterator mcItr;
   
// Initialize the MyClass array.
    for(int i=0; i<5; i++) {
      v[i] = MyClass(i, 2*i);
    }
   
// Display contents of MyClass array using indexing.
    cout << "Cycle through array via array indexing.\n"; 
    for(int i=0; i<5; i++) {
     
cout << v[i] << " ";
    }
    cout << "\n\n";
   
// Display contents of MyClass array using iterator. 
    cout << "Cycle through array through an iterator.\n"; 
    for(mcItr = v.begin(); mcItr != v.end(); mcItr++) {
     
cout << *mcItr << " ";
    }
    cout << "\n\n";
   
// Here is another way to write the preceding loop. 
    cout << "Cycle through array using a while loop.\n"; 
    mcItr = v.begin();
    while(mcItr != v.end()) {
     
cout << *mcItr << " ";
     
mcItr++;
    }
    cout << "\n\n";
   
cout << "mcItr points to an array that is "
         << mcItr.size() << " objects long.\n";
   
// Find number of elements between two iterators. 
    GCPtr<MyClass>::GCiterator mcItr2 = v.end()-2;
    mcItr = v.begin();
    cout << "The difference between mcItr2 and mcItr is "
        
<< mcItr2 - mcItr;
    cout << "\n\n";
   
// Can also cycle through loop like this.
    cout << "Dynamically compute length of array.\n";
    mcItr = v.begin();
    mcItr2 = v.end();
    for(int i=0; i < mcItr2 - mcItr; i++) {
     
cout << v[i] << " ";
    }
   
cout << "\n\n";
   
// Now, display the array backwards.
    cout << "Cycle through array backwards.\n";
    for(mcItr = v.end()-1; mcItr >= v.begin(); mcItr--)
     
cout << *mcItr << " ";
    cout << "\n\n";
   
// Of course, can use "normal" pointer to
    // cycle through array.
    cout << "Cycle through array using 'normal' pointer\n";
    MyClass *ptr = v;
    for(int i=0; i < 5; i++)
     
cout << *ptr++ << " ";
    cout << "\n\n";
   
// Can access members through a GCiterator.
    cout << "Access class members through an iterator.\n";
    for(mcItr = v.begin(); mcItr != v.end(); mcItr++) {
     
cout << mcItr->sum() << " ";
    }
    cout << "\n\n";
   
// Can allocate and delete a pointer to a GCPtr
    // normally, just like any other pointer.
    cout << "Use a pointer to a GCPtr.\n";
    GCPtr<int> *pp = new GCPtr<int>();
    *pp = new int(100);
    cout << "Value at **pp is: " << **pp;
    cout << "\n\n";
   
// Because pp is not a garbage collected pointer,
    // it must be deleted manually.
    delete pp;
 
} catch(bad_alloc exc) {
    // A real application could attempt to free
    // memory by collect() when an allocation
    // error occurs.
    cout << "Allocation error.\n";
 
}
 
return 0;
}

Here is the output with the display option turned off:

Value at *ip: 22
Inside passGCPtr(): 22
Inside passPtr(): 22
Inside passPtr(): 100
Contents of iap via array indexing.
0 1 2 3 4
Contents of iap via iterator.
0 1 2 3 4
Requesting collection on GCPtr<int> list.
ob points to (10 20)
ob now points to (11 21)
Sum is : 32
ob->val: 19.21
Now work with pointers to class objects.
Destructing MyClass(0, 0)
Destructing MyClass(1, 2)
Destructing MyClass(2, 4)
Destructing MyClass(3, 6)
Destructing MyClass(4, 8)
Cycle through array via array indexing.
(0 0) (1 2) (2 4) (3 6) (4 8)
Cycle through array through an iterator.
(0 0) (1 2) (2 4) (3 6) (4 8)
Cycle through array using a while loop.
(0 0) (1 2) (2 4) (3 6) (4 8)
mcItr points to an array that is 5 objects long.
The difference between mcItr2 and mcItr is 3
Dynamically compute length of array.
(0 0) (1 2) (2 4) (3 6) (4 8)
Cycle through array backwards.
(4 8) (3 6) (2 4) (1 2) (0 0)
Cycle through array using 'normal' pointer
(0 0) (1 2) (2 4) (3 6) (4 8)
Access class members through an iterator.
0 3 6 9 12
Use a pointer to a GCPtr.
Value at **pp is: 100
Destructing MyClass(4, 8)
Destructing MyClass(3, 6)
Destructing MyClass(2, 4)
Destructing MyClass(1, 2)
Destructing MyClass(0, 0)
Destructing MyClass(11, 21)
Destructing MyClass(10, 20)

On your own, try compiling and running this program with the display option turned on. (That is, define DISPLAY in gc.h.) Next, walk through the program, matching the output against each statement. This will give you a good feel for the way the garbage collector works. Remember, garbage collection occurs whenever a GCPtr goes out of scope. This happens at various points in the program, such as when a function that receives a copy of a GCPtr returns. In this case, the copy goes out of scope and garbage collection takes place. Also remember that each type of GCPtr maintains its own gclist. Thus, collecting garbage from one list does not cause it to be collected from other types of lists.


blog comments powered by Disqus
C++ ARTICLES

- Intel Threading Building Blocks
- Threading Building Blocks with C++
- Video Memory Programming in Text Mode
- More Tricks to Gain Speed in Programming Con...
- Easy and Efficient Programming for Contests
- Preparing For Programming Contests
- Programming Contests: Why Bother?
- Polymorphism in C++
- Overview of Virtual Functions
- Inheritance in C++
- Extending the Basic Streams in C++
- Using Stringstreams in C++
- Custom Stream Manipulation in C++
- General Stream Manipulation in C++
- Serialize Your Class into Streams in C++

Watch our Tech Videos 
Dev Articles Forums 
 RSS  Articles
 RSS  Forums
 RSS  All Feeds
Write For Us 
Weekly Newsletter
 
Developer Updates  
Free Website Content 
Contact Us 
Site Map 
Privacy Policy 
Support 

Developer Shed Affiliates

 




© 2003-2017 by Developer Shed. All rights reserved. DS Cluster - Follow our Sitemap
Popular Web Development Topics
All Web Development Tutorials