// ****************************This is array.h************************
class Array
{
public:
Array(int n); // Constructor:
// Create array of n elements
Array(); // Default constructor:
// Create array of 0 elements
~Array(); // Destructor
Array(const Array &); // Copy constructor
int numElts() const; // Number of elements
Array & operator = (const Array &); // Array assignment
Array & operator = (double); // Scalar assignment
void setSize(int n); // Change size
double &operator[] (int i); // Subscripting
const double &operator[] (int i) const; // Subscripting
friend Array operator + (const Array &, const Array &);
//Overload addition operator
private:
int num_elts; // Number of elements
double *ptr_to_data; // Pointer to data
void copy(const Array & a); // Copy in elements of a
};
void error(const char *s); // For bounds checking
// ***************************This is array.cc********************
#include <iostream.h>
#include "array.h"
Array::Array(int n)
{
num_elts = n;
ptr_to_data = new double[n];
}
Array::Array()
{
num_elts = 0;
ptr_to_data = 0;
}
Array::~Array() // Destructor
{
delete[] ptr_to_data;
}
Array::Array(const Array& a) // Copy constructor
{
num_elts = a.num_elts;
ptr_to_data = new double[num_elts];
copy(a); // Copy a's elements
}
void Array::copy(const Array& a)
{
// Copy a's elements into the elements of *this
double *p = ptr_to_data + num_elts;
double *q = a.ptr_to_data + num_elts;
while (p > ptr_to_data)
*--p = *--q;
}
double& Array::operator[](int i)
{
#ifdef CHECKBOUNDS
if(i<0 || i>num_elts)
error("out of bounds");
#endif
return ptr_to_data[i];
}
const double& Array::operator[](int i) const
{
#ifdef CHECKBOUNDS
if(i<0 || i>num_elts)
error("out of bounds");
#endif
return ptr_to_data[i];
}
int Array::numElts() const
{
return num_elts;
}
Array& Array::operator=(const Array& rhs)
{
if (ptr_to_data != rhs.ptr_to_data)
{
setSize(rhs.num_elts);
copy(rhs);
}
return *this;
}
void Array::setSize(int n)
{
if (n != num_elts)
{
delete[] ptr_to_data; // Delete old elements,
num_elts = n; // set new count,
ptr_to_data = new double[n]; // and allocate new elements
}
}
Array& Array::operator=(double rhs)
{
double *p = ptr_to_data + num_elts;
while (p > ptr_to_data)
*--p = rhs;
return *this;
}
void error(const char *s)
{
cerr << endl << s << endl; //1st "endl" is in case program is printing
cout << endl << s << endl; //something out when the error occurs
cout.flush(); //write the output buffer to the screen
//or wherever the output of cout goes.
abort();
}
Array operator + (const Array &a1, const Array &a2)
{
if(a1.numElts()==a2.numElts())
{
Array sum(a1.numElts());
int i;
for(i=0;i<a1.numElts();i++)
sum[i] = a1[i] + a2[i];
return sum;
}
else
error("can't add arrays of different lengths");
}
// **************************This is testarray.cc*************
#include <iostream.h>
#include "refarray.h"
int main()
{
int max=5;
Array matrixA(max);
Array matrixB(max);
int i;
for(i=0;i<max;i++)
{ matrixA[i]=i; //matrixA={0 1 2 3 4}
cout << matrixA[i] << " ";
}
cout << endl;
for(i=0;i<max;i++)
{ matrixB[i]=i+4.0; //matrixB={4 5 6 7 8}
cout << matrixB[i] << " " ;
}
cout << endl;
Array result = matrixA + matrixB; // invokes copy constructor
for(i=0;i<max;i++)
{ cout << result[i] << " ";}
cout << endl;
result = matrixA; // invokes assignment operator
for(i=0;i<max;i++)
{ cout << result[i] << " ";}
cout << endl;
return 0;
}
The result of the addition is a temporary matrix sum that will disappear
when the addition operator goes out of scope. So we copy the result
into result. This copying is time consuming.
Rather than copying the result of adding 2 matrices into result,
let's give result a pointer to the sum. This saves time
and space in memory. To do this we
use reference counting. This keeps count of the number of
pointers pointing to each array, including result. Reference
counting won't let a matrix be destroyed until we don't need it
anymore, i.e., until there are no more pointers pointing at it.
So let's modify the Array class to include reference counting:
// ***********************This is refarray.h***********************
class Array
{
public:
Array(int n); // Constructor:
// Create array of n elements
Array(); // Default constructor:
// Create array of 0 elements
~Array(); // Destructor
Array(const Array &); // Copy constructor
int numElts() const; // Number of elements
Array & operator = (const Array &); // Array assignment
Array & operator = (double); // Scalar assignment
// void setSize(int n); // Change size; comment out
double &operator[] (int i); // Subscripting
const double &operator[] (int i) const; // Subscripting
friend Array operator + (const Array &, const Array &);
//Overload addition operator
private:
int num_elts; // Number of elements
double *ptr_to_data; // Pointer to data
// void copy(const Array & a); // Copy elements of a; comment out
int *refcnt; // Pointer to integer that counts
// number of pointers pointing to
// object
void decrement(); // decrement refcnt
}; // destroy object if refcnt=0
void error(const char *s); // For bounds checking
//**********************This is refarray.cc************************
#include <iostream.h>
#include "refarray.h"
Array::Array(int n)
{
num_elts = n;
ptr_to_data = new double[n];
refcnt = new int(1); //initialize *refcnt to 1
}
Array::Array()
{
num_elts = 0;
ptr_to_data = 0;
refcnt = new int(1);
}
Array::~Array() // Destructor
{
decrement();
}
void Array::decrement() // decrement refcnt
{ // destroy object if refcnt=0
(*refcnt)--;
if(*refcnt == 0)
{
delete[] ptr_to_data;
delete refcnt;
}
}
Array::Array(const Array& a) // Copy constructor
{
num_elts = a.num_elts;
// ptr_to_data = new double[num_elts];
ptr_to_data = a.ptr_to_data; // Shallow copy of ptr_to_data
// copy(a); // Copy a's elements
refcnt = a.refcnt; // Shallow copy of refcnt
//original and copy point to same refcnt
(*refcnt)++; // increment refcnt
}
/* void Array::copy(const Array& a) //Comment out "copy" function
{
// Copy a's elements into the elements of *this
double *p = ptr_to_data + num_elts;
double *q = a.ptr_to_data + num_elts;
while (p > ptr_to_data)
*--p = *--q;
}
*/
double& Array::operator[](int i)
{
#ifdef CHECKBOUNDS
if(i<0 || i>num_elts)
error("out of bounds");
#endif
return ptr_to_data[i];
}
const double& Array::operator[](int i) const
{
#ifdef CHECKBOUNDS
if(i<0 || i>num_elts)
error("out of bounds");
#endif
return ptr_to_data[i];
}
int Array::numElts() const
{
return num_elts;
}
Array& Array::operator=(const Array& rhs)
{
if (ptr_to_data != rhs.ptr_to_data)
{
(*rhs.refcnt)++; //increment *refcnt for rhs
decrement(); //decrement *refcnt for lhs
refcnt = rhs.refcnt;
ptr_to_data = rhs.ptr_to_data; //shallow copy of ptr_to_data
num_elts=rhs.num_elts;
// setSize(rhs.num_elts); //comment out setSize
// copy(rhs); //comment out copying
}
return *this;
}
/* void Array::setSize(int n) //comment out setSize
{
if (n != num_elts)
{
delete[] ptr_to_data; // Delete old elements,
num_elts = n; // set new count,
ptr_to_data = new double[n]; // and allocate new elements
}
}
*/
Array& Array::operator=(double rhs)
{
double *p = ptr_to_data + num_elts;
while (p > ptr_to_data)
*--p = rhs;
return *this;
}
void error(const char *s)
{
cerr << endl << s << endl; //1st "endl" is in case program is printing
cout << endl << s << endl; //something out when the error occurs
cout.flush(); //write the output buffer to the screen
//or wherever the output of cout goes.
abort();
}
Array operator + (const Array &a1, const Array &a2)
{
if(a1.numElts()==a2.numElts())
{
Array sum(a1.numElts());
int i;
for(i=0;i<a1.numElts();i++)
sum[i] = a1[i] + a2[i];
return sum;
}
else
error("can't add arrays of different lengths");
}
// ***********************This is testarray.cc*********************
// testarray.cc is the same as before.
#include <iostream.h>
#include "refarray.h"
int main()
{
int max=5;
Array matrixA(max);
Array matrixB(max);
int i;
for(i=0;i<max;i++)
{ matrixA[i]=i; //matrixA={0 1 2 3 4}
cout << matrixA[i] << " ";
}
cout << endl;
for(i=0;i<max;i++)
{ matrixB[i]=i+4.0; //matrixB={4 5 6 7 8}
cout << matrixB[i] << " " ;
}
cout << endl;
Array result = matrixA + matrixB; // invokes copy constructor
for(i=0;i<max;i++)
{ cout << result[i] << " ";}
cout << endl;
result = matrixA; // invokes assignment operator
for(i=0;i<max;i++)
{ cout << result[i] << " ";}
cout << endl;
return 0;
}
Class Array now includes reference counting.
Notice that the copy constructor and assignment operators no longer
copy all the array elements. They just handle the pointers pointing
to the objects and make sure that there is a proper count of the
number of references to each object. The destructor decreases
by one the number of pointers pointing at an object and only destroys
an object if there are no pointers pointing at it any longer.
In chapter 14 of Barton and Nackman, there is a class called ReferenceCount that keeps track of the bookkeeping for reference counting.
// ***********************ReferenceCount.h************************
#ifndef ReferenceCountH
#define ReferenceCountH
class ReferenceCount {
public:
ReferenceCount(); // Create with count of 1
ReferenceCount(const ReferenceCount&); // Copy and increment count
ReferenceCount& operator=(const ReferenceCount&); // Assign;
//decrement lhs count, increment rhs
~ReferenceCount(); // Decrement count; delete if 0
bool unique() const; // True if count is 1
private:
unsigned int* p_refcnt; // Pointer to actual count
void decrement(); // Decrement count; delete if 0
};
inline bool ReferenceCount::unique() const
{return *p_refcnt == 1; }
inline ReferenceCount::ReferenceCount() : p_refcnt(new unsigned int(1)) {}
inline ReferenceCount::ReferenceCount(const ReferenceCount& anRC) :
p_refcnt(anRC.p_refcnt) { ++*p_refcnt; }
inline void ReferenceCount::decrement() {
if (unique()) delete p_refcnt;
else --*p_refcnt;
}
inline ReferenceCount::~ReferenceCount() { decrement(); }
inline ReferenceCount&
ReferenceCount::operator=(const ReferenceCount& rhs) {
++*rhs.p_refcnt;
decrement();
p_refcnt = rhs.p_refcnt;
return *this;
}
#endif
ReferenceCount is a private member of a pointer
class CountedBuiltInPtr that keeps track of the
object pointed to. This is an example of a smart pointer.
``Smart pointers'' are pointer classes which help manage objects.
Their star data member is a pointer.
#ifndef CountedObjPtrH
#define CountedObjPtrH
#include "ReferenceCount.h"
template<class T>
class CountedBuiltInPtr {
public:
CountedBuiltInPtr() : the_p(0) {} // Construct as null pointer
CountedBuiltInPtr(T* just_newed) : the_p(just_newed) {}
// Construct pointing at heap object
CountedBuiltInPtr<T>& operator=(const CountedBuiltInPtr<T>&);
// Adjust counts
CountedBuiltInPtr<T>& operator=(T*);
// Decrease lhs count
~CountedBuiltInPtr(); //Decrease count, destroy if 0
bool unique() const; // Is count one ?
T& operator*() const { return *the_p; } // Access object
bool isNull() const { return the_p == 0; }
friend bool operator==(const CountedBuiltInPtr<T>& lhs,
const CountedBuiltInPtr<T>& rhs) {
return lhs.the_p == rhs.the_p;
}
friend bool operator!=(const CountedBuiltInPtr<T>& lhs,
const CountedBuiltInPtr<T>& rhs) {
return lhs.the_p != rhs.the_p;
}
protected:
T* the_p;
private:
ReferenceCount refCount; // Number of pointers to heap object
};
Notice that no copy constructor is needed. The default copy constructor
will do since we just want a shallow copy of the pointers. The
default copy constructor for CountedBuiltInPtr will call the
copy constructor for ReferenceCount. The class CountedBuiltInPtr
is fine for built-in types. For pointing to class objects we need a
member selection operator. This is done in the class CountedObjPtr.
The template class CountedBuiltInPtr is inherited
by the class CountedObjPtr.
template<class T>
class CountedObjPtr :
public CountedBuiltInPtr<T> {
public:
CountedObjPtr() {}
CountedObjPtr(T* just_newed) : CountedBuiltInPtr<T>(just_newed) {}
CountedObjPtr<T>& operator=(T* rhs) {
CountedBuiltInPtr<T>::operator=(rhs);
return *this;
}
CountedObjPtr<T>& operator=(const CountedObjPtr<T>& rhs) {
CountedBuiltInPtr<T>::operator=(rhs);
return *this;
}
T* operator->() const { return the_p; } // member selection operator
};
We have defined constructors and assignment operators for CountedObjPtr
because they are not inherited from CountedBuiltInPtr.
// ******definitions of template member functions from CountedObjPtr.cc
template<class T>
CountedBuiltInPtr<T>&
CountedBuiltInPtr<T>::operator=(const CountedBuiltInPtr<T>& rhs) {
if (the_p != rhs.the_p) {
if (unique()) delete the_p;
the_p = rhs.the_p;
refCount = rhs.refCount;
}
return *this;
}
template<class T>
CountedBuiltInPtr<T>& CountedBuiltInPtr<T>::operator=(T* just_newed) {
if (unique()) delete the_p;
the_p = just_newed;
refCount = ReferenceCount(); //invokes default constructor
return *this;
}
template<class T>
CountedBuiltInPtr<T>::~CountedBuiltInPtr() {
if (refCount.unique()) delete the_p;
}
template<class T>
bool CountedBuiltInPtr<T>::unique() const {
return refCount.unique();
}
#endif
This makes reference counting easy to use. You just put the class
that you want to have reference counting as the type T in
CountedObjPtr<T>. For example, if you have a class Vector
that you want to have reference counting, you would write
int main()
{
Vector* velocity = new Vector;
CountedObjPtr<Vector> ref_velocity(velocity); //calls constructor of
//CountedObjPtr
// other stuff ...
}