Ludzie pragną czasami się rozstawać, żeby móc tęsknić, czekać i cieszyć się z powrotem.
A test
Here’s the old test program for Stash rewritten for the PStash:
//: C13:PStashTest.cpp
//{L} PStash
// Test of pointer Stash
#include "PStash.h"
#include "../require.h"
#include <iostream>
#include <fstream>
Chapter 11: Dynamic Object Creation
395
#include <string>
using namespace std;
int main() {
PStash intStash;
// 'new' works with built-in types, too. Note
// the "pseudo-constructor" syntax:
for(int i = 0; i < 25; i++)
intStash.add(new int(i));
for(int u = 0; u < intStash.count(); u++)
cout << "intStash[" << u << "] = "
<< *(int*)intStash[u] << endl;
ifstream infile("PStashTest.cpp");
assure(infile, "PStashTest.cpp");
PStash stringStash;
string line;
while(getline(infile, line))
stringStash.add(new string(line));
// Print out the strings:
for(int v = 0; stringStash[v]; v++)
cout << "stringStash[" << v << "] = "
<< *(string*)stringStash[v] << endl;
} ///:~
As before, Stashes are created and filled with information, but this time the information is the pointers resulting from new-expressions. In the first case, note the line:
intStash.add(new int(i));
The expression new int(i) uses the pseudoconstructor form, so storage for a new int object is created on the heap, and the int is initialized to the value i.
Note that during printing, the value returned by PStash::operator[ ] must be cast to the proper type; this is repeated for the rest of the PStash objects in the program. It’s an undesirable effect of using void pointers as the underlying representation and will be fixed in later chapters.
The second test opens the source code file and reads it one line at a time into another PStash.
Each line is read into a string using getline( ), then a new string is created from line to make an independent copy of that line. If we just passed in the address of line each time, we’d get a whole bunch of pointers pointing to line, which itself would only contain the last line that was read from the file.
When fetching the pointers back out, you see the expression:
*(string*)stringStash[v]
Chapter 11: Dynamic Object Creation
396
The pointer returned from operator[ ] must be cast to a string* to give it the proper type.
Then the string* is dereferenced so the expression evaluates to an object, at which point the compiler sees a string object to send to cout.
In this example, the objects created on the heap are never destroyed. This is not harmful here because the storage is released when the program ends, but it’s not something you want to do
in practice. It will be fixed in later chapters.
The stack
The Stack benefits greatly from all the features introduced since Chapter XX. [[ I think at this point only inlines have been added??]] Here’s the new header file:
//: C13:Stack4.h
// New version of Stack
#ifndef STACK4_H
#define STACK4_H
class Stack {
struct Link {
void* data;
Link* next;
Link(void* dat, Link* nxt) {
data = dat;
next = nxt;
}
}* head;
public:
Stack() { head = 0; }
~Stack();
void push(void* dat) {
head = new Link(dat,head);
}
void* peek() const { return head->data; }
void* pop();
};
#endif // STACK4_H ///:~
The rest of the logic is virtually identical to what it was in Chapter XX. Here is the
implementation of the two remaining (non-inline) functions:
//: C13:Stack4.cpp {O}
// New version of Stack
#include "Stack4.h"
Chapter 11: Dynamic Object Creation
397
void* Stack::pop() {
if(head == 0) return 0;
void* result = head->data;
Link* oldHead = head;
head = head->next;
delete oldHead;
return result;
}
Stack::~Stack() {
Link* cursor = head;
while(head) {
cursor = cursor->next;
delete head;
head = cursor;
}
} ///:~
The only difference is the use of delete instead of free( ) in the destructor.
As with the Stash, the use of void pointers means that the objects created on the heap cannot be destroyed by the Stack4, so again there is the possibility of an undesirable memory leak if the user doesn’t take responsibility for the pointers in the Stack4. You can see this in the test program:
//: C13:Stack4Test.cpp