Follow Techotopia on Twitter

On-line Guides
All Guides
eBook Store
iOS / Android
Linux for Beginners
Office Productivity
Linux Installation
Linux Security
Linux Utilities
Linux Virtualization
Linux Kernel
System/Network Admin
Programming
Scripting Languages
Development Tools
Web Development
GUI Toolkits/Desktop
Databases
Mail Systems
openSolaris
Eclipse Documentation
Techotopia.com
Virtuatopia.com
Answertopia.com

How To Guides
Virtualization
General System Admin
Linux Security
Linux Filesystems
Web Servers
Graphics & Desktop
PC Hardware
Windows
Problem Solutions
Privacy Policy

  




 

 

Thinking in C++
Prev Contents / Index Next

Stack with constructors & destructors

Reimplementing the linked list (inside Stack) with constructors and destructors shows how neatly constructors and destructors work with new and delete. Here’s the modified header file:

//: C06:Stack3.h
// With constructors/destructors
#ifndef STACK3_H
#define STACK3_H

class Stack {
  struct Link {
    void* data;
    Link* next;
    Link(void* dat, Link* nxt);
    ~Link();
  }* head;
public:
  Stack();
  ~Stack();
  void push(void* dat);
  void* peek();
  void* pop();
};
#endif // STACK3_H ///:~

Not only does Stack have a constructor and destructor, but so does the nested class struct Link:

//: C06:Stack3.cpp {O}
// Constructors/destructors
#include "Stack3.h"
#include "../require.h"
using namespace std;

Stack::Link::Link(void* dat, Link* nxt) {
  data = dat;
  next = nxt;
}

Stack::Link::~Link() { }

Stack::Stack() { head = 0; }

void Stack::push(void* dat) {
  head = new Link(dat,head);
}

void* Stack::peek() { 
  require(head != 0, "Stack empty");
  return head->data; 
}

void* Stack::pop() {
  if(head == 0) return 0;
  void* result = head->data;
  Link* oldHead = head;
  head = head->next;
  delete oldHead;
  return result;
}

Stack::~Stack() {
  require(head == 0, "Stack not empty");
} ///:~

The Link::Link( ) constructor simply initializes the data and next pointers, so in Stack::push( ) the line

head = new Link(dat,head);

not only allocates a new link (using dynamic object creation with the keyword new, introduced in Chapter 4), but it also neatly initializes the pointers for that link.

You may wonder why the destructor for Link doesn’t do anything – in particular, why doesn’t it delete the data pointer? There are two problems. In Chapter 4, where the Stack was introduced, it was pointed out that you cannot properly delete a void pointer if it points to an object (an assertion that will be proven in Chapter 13). But in addition, if the Link destructor deleted the data pointer, pop( ) would end up returning a pointer to a deleted object, which would definitely be a bug. This is sometimes referred to as the issue of ownership: the Link and thus the Stack only holds the pointers, but is not responsible for cleaning them up. This means that you must be very careful that you know who is responsible. For example, if you don’t pop( ) and delete all the pointers on the Stack, they won’t get cleaned up automatically by the Stack’s destructor. This can be a sticky issue and leads to memory leaks, so knowing who is responsible for cleaning up an object can make the difference between a successful program and a buggy one – that’s why Stack::~Stack( ) prints an error message if the Stack object isn’t empty upon destruction.

Because the allocation and cleanup of the Link objects are hidden within Stack – it’s part of the underlying implementation – you don’t see it happening in the test program, although you are responsible for deleting the pointers that come back from pop( ):

//: C06:Stack3Test.cpp
//{L} Stack3
//{T} Stack3Test.cpp
// Constructors/destructors
#include "Stack3.h"
#include "../require.h"
#include <fstream>
#include <iostream>
#include <string>
using namespace std;

int main(int argc, char* argv[]) {
  requireArgs(argc, 1); // File name is argument
  ifstream in(argv[1]);
  assure(in, argv[1]);
  Stack textlines;
  string line;
  // Read file and store lines in the stack:
  while(getline(in, line))
    textlines.push(new string(line));
  // Pop the lines from the stack and print them:
  string* s;
  while((s = (string*)textlines.pop()) != 0) {
    cout << *s << endl;
    delete s; 
  }
} ///:~

In this case, all the lines in textlines are popped and deleted, but if they weren’t, you’d get a require( ) message that would mean there was a memory leak.

Thinking in C++
Prev Contents / Index Next

 
 
   Reproduced courtesy of Bruce Eckel, MindView, Inc. Design by Interspire