The
examples from previous chapters have obvious functions that map to constructors
and destructors:
initialize( )
and
cleanup( ).
Here’s the
Stash
header using constructors and destructors:
//: C06:Stash2.h
// With constructors & destructors
#ifndef STASH2_H
#define STASH2_H
class Stash {
int size; // Size of each space
int quantity; // Number of storage spaces
int next; // Next empty space
// Dynamically allocated array of bytes:
unsigned char* storage;
void inflate(int increase);
public:
Stash(int size);
~Stash();
int add(void* element);
void* fetch(int index);
int count();
};
#endif
// STASH2_H ///:~
The
only member function definitions that are changed are
initialize( )
and
cleanup( ),
which have been replaced with a constructor and destructor:
//: C06:Stash2.cpp {O}
// Constructors & destructors
#include "Stash2.h"
#include <iostream>
#include <cassert>
using namespace std;
const int increment = 100;
Stash::Stash(int sz) {
size = sz;
quantity = 0;
storage = 0;
next = 0;
}
int Stash::add(void* element) {
if(next >= quantity) // Enough space left?
inflate(increment);
// Copy element into storage,
// starting at next empty space:
int startBytes = next * size;
unsigned char* e = (unsigned char*)element;
for(int i = 0; i < size; i++)
storage[startBytes + i] = e[i];
next++;
return(next - 1); // Index number
}
void* Stash::fetch(int index) {
assert(0 <= index && index < next);
// Produce pointer to desired element:
return &(storage[index * size]);
}
int Stash::count() {
return next; // Number of elements in CStash
}
void Stash::inflate(int increase) {
assert(increase > 0);
int newQuantity = quantity + increase;
int newBytes = newQuantity * size;
int oldBytes = quantity * size;
unsigned char* b = new unsigned char[newBytes];
for(int i = 0; i < oldBytes; i++)
b[i] = storage[i]; // Copy old to new
delete [](storage); // Old storage
storage = b; // Point to new memory
quantity = newQuantity;
}
Stash::~Stash() {
if(storage != 0) {
cout << "freeing storage" << endl;
delete []storage;
}
}
///:~
Looking
at
inflate( ),
you might ask why the “primitive”
assert( )
is still being used after the
require.h
functions have already been introduced. The distinction is important: in this
book,
assert( )
will be used to watch for programmer errors. This makes sense because the
output of a failed
assert( )
is not particularly end-user friendly and should only be seen by programmers,
while the
require.h
functions (which will be shown later in the book) are specifically designed to
be reasonably useful for end-users.
Because
inflate( )
is private, the only way an
assert( )
could occur is if one of the other member functions accidentally passed an
incorrect value to
inflate( ).
If you are certain this can’t happen, you could consider removing the
assert( ),
but you might keep in mind that until the class is stable, there’s always
the possibility that new code might be added to the class which could cause
errors. The cost of the
assert( )
is low (and can be removed by defining
NDEBUG)
and the value of code robustness is high.
Notice,
in the following test program, how the definitions for
Stash
objects appear right before they are needed, and how the initialization appears
as part of the definition, in the constructor argument list:
Also
notice how the
cleanup( )
calls have been eliminated, but the destructors are still automatically called
when
intStash
and
stringStash
go out of scope.