Let's go,Garbage Collector(十)

来源:互联网 发布:网络4路录像机安装视频 编辑:程序博客网 时间:2024/06/16 17:03

If you are allocating an array using new, then you must tell GCPtr this fact by specifying its size when the GCPtr pointer to that array is declared. For example, here is the way to allocate an array of five doubles:

GCPtr<double, 5> pda = new double[5];

The size must be specified for two reasons. First, it tells the GCPtr constructor that this object will point to an allocated array, which causes the isArray field to be set to true. When isArray is true, the collect( ) function frees memory by using delete[ ], which releases a dynamically allocated array, rather than delete, which releases only a single object. Therefore, in this example, when pda goes out of scope, delete[ ] is used and all five elements of pda are freed. Ensuring that the correct number of objects are freed is especially important when arrays of class objects are allocated. Only by using delete[ ] can you know that the destructor for each object will be called.

The second reason that the size must be specified is to prevent an out-of-bounds element from being accessed when an Iter is used to cycle through an allocated array. Recall that the size of the array (stored in arraySize) is passed by GCPtr to Iter’s constructor whenever an Iter is needed.

Be aware that nothing enforces the rule that an allocated array be operated on only through a GCPtr that has been specified as pointing to an array. This is solely your responsibility.

Once you have allocated an array, there are two ways you can access its elements. First, you can index the GCPtr that points to it. Second, you can use an iterator. Both methods are shown here.

Using Array Indexing

 

// Demonstrate indexing a GCPtr.
#include <iostream>
#include <new>
#include "gc.h"
using namespace std;
int main() {
 
try {
    // Create a GCPtr to an allocated array of 10 ints.
   
GCPtr<int, 10> ap = new int[10];
   
// Give the array some values using array indexing.
    for(int i=0; i < 10; i++)
      ap[i] = i;
   
// Now, show the contents of the array.
    for(int i=0; i < 10; i++)
      cout << ap[i] << " ";
   
cout << endl;
 
} catch(bad_alloc exc) {
    cout << "Allocation failure!/n";
    return 1;
 
}
 
return 0;
}
The output, with the display option off, is shown here:

0 1 2 3 4 5 6 7 8 9

Because a GCPtr emulates a normal C++ pointer, no array bounds checking is performed, and it is possible to overrun or under run the dynamically allocated array. So, use the same care when accessing an array through a GCPtr as you do when accessing an array through a normal C++ pointer.

Using Iterators

 

Here is the previous program reworked to use an iterator. Recall that the easiest way to obtain an iterator to a GCPtr is to use GCiterator, which is a typedef inside GCPtr that is automatically bound to the generic type T.

// Demonstrate an iterator.
#include <iostream>
#include <new>
#include "gc.h"
using namespace std;
int main() {
 
try {
    // Create a GCPtr to an allocated array of 10 ints.  
    GCPtr<int, 10> ap = new int[10];
   
// Declare an int iterator.
   
GCPtr<int>::GCiterator itr;
   
// Assign itr a pointer to the start of the array.
   
itr = ap.begin();
   
// Give the array some values using array indexing.
    for(unsigned i=0; i < itr.size(); i++)
      itr[i] = i;
   
// Now, cycle through array using the iterator.
    for(itr = ap.begin(); itr != ap.end(); itr++)
      cout << *itr << " ";
   
cout << endl;
 
} catch(bad_alloc exc) {
   
cout << "Allocation failure!/n";
   
return 1;
  } catch(OutOfRangeExc exc) {
    cout << "Out of range access!/n";
    return 1;
 
}
  return 0;
}

On your own, you might want to try incrementing itr so that it points beyond the boundary of the allocated array. Then try accessing the value at that location. As you will see, an OutOfRangeExc is thrown. In general, you can increment or decrement an iterator any way you like without causing an exception. However, if it is not pointing within the underlying array, attempting to obtain or set the value at that location will cause a boundary error.

Using GCPtr with Class Types

GCPtr is used with class types in just the same way it is used with built-in types. For example, here is a short program that allocates objects of MyClass:

// Use GCPtr with a class type.
#include <iostream>
#include <new>
#include "gc.h"
using namespace std;
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);
};
// An overloaded inserter to display MyClass.
ostream &operator<<(ostream &strm, MyClass &obj) {
  strm << "(" << obj.a << " " << obj.b << ")";
  return strm;
}
int main() {
  try {
    GCPtr<MyClass> ob = new MyClass(10, 20);
   
// Show value via overloaded inserter.
    cout << *ob << endl;
   
// Change object pointed to by ob.
    ob = new MyClass(11, 21);
   
cout << *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 = 98.6;
    cout << "ob->val: " << ob->val << endl;
   
cout << "ob is now " << *ob << endl;
  } catch(bad_alloc exc) {
   
cout << "Allocation error!/n";
   
return 1;
  }
 
return 0;
}

Notice how the members of MyClass are accessed through the use of the –> operator. Remember, GCPtr defines a pointer type. Thus, operations through a GCPtr are performed in exactly the same fashion that they are with any other pointer.

The output from the program, with the display option turned off, is shown here:

(10 20)
(11 21)
Sum is : 32
ob->val: 98.6
ob is now (11 21)
Destructing MyClass(11, 21)
Destructing MyClass(10, 20)

Pay special attention to the last two lines. These are output by ~MyClass( ) when garbage is collected. Even though only one GCPtr pointer was created, two MyClass objects were allocated. Both of these objects are represented by entries in the garbage collection list. When ob is destroyed, gclist is scanned for entries having a reference count of zero. In this case, two such entries are found, and the memory to which they point is deleted.

 
Although array indexing is certainly a convenient method of cycling through an allocated array, it is not the only method at your disposal. For many applications, the use of an iterator will be a better choice because it has the advantage of preventing boundary errors. Recall that for GCPtr, iterators are objects of type Iter. Iter supports the full complement of pointer operations, such as ++. It also allows an iterator to be indexed like an array.
The following program creates a GCPtr to a 10-element array of ints. It then allocates that array and initializes it to the values 0 through 9. Finally, it displays those values. It performs these actions by indexing the GCPtr.