On to C++

Table of Contents

UP | HOME

1 How This Book Teaches You The Language.

Why you should learn C++. Object-Oriented programming languages. Procedure-oriented programming languages. Programming cliches.

2 How To Compile And Run A Simple Program.

29 Conventions on file extensions for C++ source code vary: some programmers prefer -and some compilers require- C, cc, or cpp.

30…e.g., the name of the program that compiles and links may be CC, g++, or cxx, just to mention a three popular examples. So, for a ~/fooDir/foo.cpp we compile:

$ g++ foo.cpp -o foo
$ ./foo
            

2.1 Edit.

I run into issues when compiling the script which appears on the handbook (pg. 23 section 84).

[txarly@ntxarly c++lab]$ g++ -o volume volume.cpp  
volume.cpp:1:22: fatal error: iostream.h: No such file or directory
              

Some goggling by and reading this post I found out I had to correct the script as shown below, note '<iostream>' and 'using namespace std;'

#include <iostream>
using namespace std;
main() {
int height, width, length;
cout << "Please, type three integers. " <<endl;
cin >> height;
cin >> width;
cin >> length;
cout << "The volume of a "
<< height << " by " << width << " by " << length
<< " box car is " << height * width * length << endl;
}
              

3 How To Declare Variable

4 How To Write Arithmetic Expressions.

5 How To Write Statements that Read Information From Your Keyboard.

The >> when used with the cin 'input-place' specification, the input operator picks up a value for a variable by watching what you type on your keyboard.

#include <iostream.h>
main() {
int height, width, length;
cout << "Please, type three integers. " <<endl;
cin >> height;
cin >> width;
cin >> length;
cout << "The volume of a "
<< height << " by " << width << " by " << length
<< " box car is " << height * width * length << endl;
}
            

6 How-to Define Simple Functions.

7 How-to Benefit From Procedure Abstraction.

8 How-to Work with Local and Global Variables.

9 How-to Create Classes and Objects.

10 How-to Define Member Functions.

11 How-to Define Constructor Member Functions.

12 How-to Define Reader and Writer Member Functions.

13 How-to Benefit From Data Abstraction.

14 How-to Protect Member Variables From Harmful Reference.

15 How-to Define Classes That Inherit Variables And Functions.

16 How-to Design Classes And Class Hierarchies.

17 How-to Perform Tests Using Numerical Predicates.

18 How-to Write One-Way And Two-Way Conditional Statements.

19 How-to Combine Boolean Expressions.

20 How-to Write Iteration Statements.

21 How-to Process Data In Files.

The following program computes box-car volumes as long as you type numbers; it stops when you type the control-d key-chord.

#include <iostream.h>
double box_volume(double h, double w, double l){return h * w * l;}
main(){
double height, width, depth;
while (cin >> height >> width >> depth)
cout << "The volume of a "
<< height << " by " << width << " by " << depth
<< " box car is " << box_volume (height, width, depth)
<< endl;
cout << "You appear to have finished." << endl;
}
            

Also, you can redirect input from your keyboard to a file. e.g. a file named test-data:

10.5 9.5 40.0
12.6 8.6 50.0
            

So, to call the function you need only to type, assuming you have called you program box_car and you have prepared a test file named test-data:

$ box_car < test-data
            

22 How-to Write Recursive Functions.

An example better than words..

#include <iostream>
using namespace std;

// Define recursive..

int recursive_power_of_2 (int n) {
  if (n == 0)
    return 1;
  else
    return 2 * recursive_power_of_2 (n - 1);
  }

//test

main () {
  cout << "2 to the 0th power is " << recursive_power_of_2 (0) << endl
    << "2 to the 1st power is " << recursive_power_of_2 (1) << endl
    << "2 to the 2nd power is " << recursive_power_of_2 (2) << endl
    << "2 to the 3rd power is " << recursive_power_of_2 (3) << endl;
  }
            

23 How-to Solve Definition Ordering Problems With Function Prototypes.

As learned in seg 349 the number of rabbits after more than 1 month is the sum of the number at the end of the previous month and the month before that, as shown next:

int rabbits (int n) {
if (n == 0 || n == 1)
return 1;
else return previous_month (n) + penultimate_month (n);
}
            
#include <iostream>
using namespace std;
//Function prototype for rabbits function
int rabbits (int);
//Function definitions requiring rabbits function prototype:
int previous_month (int n) {return rabbits (n - 1);}
int penultimate_month (int n) {return rabbits (n -2);}
//Function definition for rabbits function:
int rabbits (int n) {
if (n == 0 || n == 1)
return 1;
 else return previous_month (n) + penultimate_month (n);
}
//Test rabbits function
main () {
cout << "At the end of the month 1, there is " << rabbits (1) << endl
<< "At the end of the month 2, there are " << rabbits (2) << endl
<< "At the end of the month 3, there are " << rabbits (3) << endl
<< "At the end of the month 4, there are " << rabbits (4) << endl
<< "At the end of the month 5, there are " << rabbits (5) << endl;
}
            

Each of the three cooperating functions can initiate a chain of calls that ends in a call to itself. Thus, the cooperating functions exhibit indirect, rather than direct, recursion.

24 How-to Work With Arrays Of Numbers.

To define a global array

int distances[5];
            

Then the following expressions, for example, inserts an integer into the place indexed by the value of counter.

distances[counter] = 57;
            

The following is a program in which an array of five integers is defined, data are wired in via five assignments, and the data are accessed in an output statement:

#include <iostream>
using namespace std;
// Define global integer array:
int distances[5];
main () {
// Wire in sample data
distances[0] = 57; distances[1] = 72;
distances[2] = 94; distances[3] = 22;
distances[4] = 35;
//Display
cout << "The total distance traveled is "
<< distances[0]
+ distances[1]
+ distances[2]
+ distances[3]
+ distances[4]
<< endl;
}
            
//Result
The total distance traveled is 280
            

24.1 PS.

Here I had to take an interesting break reading up on certain Rich Hickey's tracked performing curve which I do look against as the learning route me myself am trough (which this very notepad is proof of). Actually afterwards some LISP literacy (followed a quite hard EMACS learning curve) I picked up some of Python, R, HTML, CSS, & am into a rebuilt coming back to C++ though so better understood now as it's under a LISP-building-view approach.

25 How-to Work With Arrays Of Class Objects.

As in the following example:

#include <iostream>
use namespace std;
const double pi = 3.14159;
//Define the cylinder class
class cylinder {
public: double radious, length;
        double volume () {return pi * radious * radious * length;}
};
//Define cylinder array:
cylinder oil_tanks[100];
main() {
//Declare various variables:
int limit, counter;
double sum = 0.0;
//Read numbers and write them into array:
 for (limit = 0;
      cin >> oil_tanks[limit].radious
          >> oil_tanks[limit].length;
       ++limit)
;
//Compute volume:
for (counter = 0; counter < limit; ++counter)
  sum = sum + oil_tanks[counter].volume ();
//Display sum:
cout << "The total volume in the "
     << limit << " storage tanks is "
     << sum << endl;
}
            

26 How-to Create File Streams For Input And Output.

In this section we learn how to access information in files directly, so that we don't have to depend on input-output redirection to get information in and out of files.

A stream is a sequence of data objects. In the context of file input and output, cout is the name of a stream that flows from your program to your screen, and cin is the name of a stream that flows from your keyboard to your program.

#include <fstream>
ifstream cilinder_stream ("test.data");

//of course, the file specification may include a file directory path:
ifstream cilinder_stream ("/usr/phw/cpp/test.data");

//the following fileopening statement write down on to a file, rather:
ofstream volume_stream ("test.result")
            

The following program, fills an array with cylinder radio and lengths from a file named test.data, and then writes the sum of the cylinders' volumes into a file named test.result.

#include <iostream>
#include <fstream>
cons double pi = 3.14159;

//Define the cylinder class:
class cylinder {
public: double radius, length;
  double volume () {return pi * radius * radius * length;}
};

//Define cylinder array:
cylinder oil_tanks[100];
main () {

//Declare variables:
double sum = 0.0;

//Connect an input stream to the file named test.data:
ifstream cylinder_stream ("test.data");

//Connect an output stream to the file named test.result:
ofstream volume_stream ("test.result");

//Read numbers from the test.data file:
for (limit = 0;
     cylinder_stream >> oil_tanks[limit].radius
                     >> oil_tanks[limit].length;
      ++limit);

//Compute volume:
for (counter = 0; counter < limit; ++counter)
   sum = sum + oil_tanks[counter].volume ();

//Write sum into the test result file:
volume_stream << "The total volume in the "
              << limit << " storage tanks is "
              << sum << endl;
}
            

27 How-to create New Class Objects At Run Time.

28 How-to Store Pointers To Class Objects.

You may find yourself creating ridiculously large global arrays just to be sure that you don't run out of space. Such arrays can waste a lot of memory, particularly if they hold large class of objects. Fortunately, you now know that you can arrange for the C++ compiler to allocate only pointers at compile time, deferring the allocation of memory for objects until run time.

// recall you define an array of cylinder objects with a definition
// that looks like a var definition with a bracketed number added:

cylinder oil_tanks[100];

// And you can define an array of pointers to cylinders objects by
// adding an asterisk before the array name:

cylinder *oil_tank_pointers[100];
            

29 How-to Write Programs That Find Member Functions At Run Time.

456 .. you must somehow arrange for your compiled program to determine which [..] function to use, rather than having the C++ compiler decide in advance, at compile time.

[…] to mark a member function for selection at run time, rather than at compile time, you preface its definition with virtual. Such function is said to be a virtual member function (it won be available, hence, only virtual, at compile time)

class railroad_car {
  public: //Constructor:
         railroad_car () {}
         // Virtual display function:
        virtual void display_short_name () {}; }
            

458 Until you are completely comfortable with virtual functions, you may find the phrase multiversion function for which the version to be called is determined by object class at run time whenever you see the phrase 'virtual function'

30 How-to Write Multiway Conditional Statements.

You know how to use if statements when you want a program to decide which of two alternative statements to execute. In this section, you learn how to use switch statements when you want your program to decide which of many alternative statements to execute.

switch (integer-producing expression) {
  case integer constant 1: statement for integer 1 break;
  case integer constant 2: statement for integer 2 break;
  ...
  default: default statements
}
            

The version of the analyze_train shown in segment 460 contains a sequence of if-else statements that determines what to do for any given value of the type_code variable:

if (type_code == 0)        train[n] = new engine; 
else if (type_code == 1)   train[n] = new box_car; 
else if (type_code == 2)   train[n] = new tank_car; 
else if (type_code == 3)   train[n] = new caboose; 

// Most C++ programmers use a switch statement instead:

switch (type_code) { 
  case 0: train[n] = new engine;   break; 
  case 1: train[n] = new box_car;  break; 
  case 2: train[n] = new tank_car; break; 
  case 3: train[n] = new caboose;  break; 
}
            

31 How-to Use Enumerations To Improve Readability.

..making programs easier to understand by replacing integer codes by mnemonic symbols.

For example, you could define integer constants as follows near the beginning of your program:

const int eng_code = 0; 
const int box_code = 1; 
const int tnk_code = 2; 
const int cab_code = 3;

// Although more experienced c++ programmers would be more likely to use
// an enumeration statement:

enum {eng_code, box_code, tnk_code, cab_code}; 

// so the switch statement could gain clarity by being writen this way:

switch (type_code) { 
  case eng_code: train[n] = new engine;   break; 
  case box_code: train[n] = new box_car;  break; 
  case tnk_code: train[n] = new tank_car; break; 
  case cab_code: train[n] = new caboose;  break; 
}
            

32 How-to Write Constructors That Call Other Constructors.

We should back to secc 11 where we learned that constructors are member functions that you use to construct class objects. At this point So that we could build construct the function as in the following

box_car() : box (10.5, 9.5, 40.0){}
            

492-cons-that-call-cons.png

Now let's compare this with the previous member-variable-assigning version

box_car () {
     height =  10.5; width = 9.5; length = 40.0;
     }
            

Also, if wish, we could arrange for explicit calls to more than one constructor. We simply separate the calls by commas, as in the following example:

box_car () : box (10.5, 9.5, 40.0),
             railroad_car (arguments)
             {}
            

And, the way of doing this is (e.g) as follows:

class box : public container {
  public: double height, width, length;

          // Default constructor:
          box () {}

          // Argument-bearing constructor:
          box (double h, double w, double l) {
             height = h; width = w; length = l;
          }
          // Other member function:
          double volume () {return height * width * length;}
};
            

33 How-to Write Member Functions That Call Other Members Functions.

As said in the previous case, this issue is faced by recurring to a kind of 'template', a built-in one abstraction..

..to understand this (I think) better starting by the end.. (segment 509) (Though it doesn't work for me (I've had to give it up for now))


          

34 How-to Use Protected And Private Variables And Functions.

To improve your understanding of the public, protected, and private parts of a class definition, contemplate the following diagram.

519-seg.png

I have no included syntax details because it's quite close the case and would become a some tricky chapter. I find this will come out easily when on the writing task.

35 How-to Use Protected And Private Class Derivations.

36 How-To Write Functions That Return Character Strings.

#include <iostream>
using namespace std;

const double pi = 3.14159; 

class box { 
  public: double height, width, length; 
    // Default constructor: 
    box ( ) { } 
    // Argument-bearing constructor: 
    box (double h, double w, double l) { 
      height = h; width = w; length = l; 
    } 
    // Volume member function: 
    double volume ( ) {return height * width * length;} 
}; 

// ...Cylinder definition goes here 
class cylinder {
  public: double radius, length;
  // Default constructor:
  cylinder ( ) { }
  // Other member functions
  // Argument-bearing constructor:
  double volume ( ) {return pi * radius * radius * length;}
  };

class railroad_car { 
  public: railroad_car ( ) { } 
          virtual char* short_name ( ) {return "rrc";} 
          virtual double capacity ( ) {return 0.0;} 
}; 

class box_car : public railroad_car, public box { 
  public: box_car ( ) : box (10.5, 9.5, 40.0) { } 
          virtual char* short_name ( ) {return "box";} 
          virtual double capacity ( ) {return volume ( );} 
}; 

// Tank car definition goes here 
class tank_car : public railroad_car, public cylinder {
  public: tank_car ( ) : cylinder (3.5, 40.0) { }
  virtual char* short_name ( ) {return "tnk";}
  virtual double capacity ( ) {return volume ( );}
  };

//..Engine &
class engine : public railroad_car { 
  public: engine ( ) { } 
  virtual char* short_name ( ) {return "eng";} 
}; 

// ..Caboose definition goes here
class caboose : public railroad_car {
  public: caboose ( ) { }
  virtual char* short_name ( ) {return "cab";}
  };

// Define railroad car pointer array: 
railroad_car *train[100]; 

// Declare enumeration constants, needed in switch statement: 
enum {eng_code, box_code, tnk_code, cab_code}; 

main ( ) { 
  int n, car_count, type_code; 
  for (car_count = 0; cin >> type_code; ++car_count) 
    switch (type_code) { 
      case eng_code: train[car_count] = new engine;   break; 
      case box_code: train[car_count] = new box_car;  break; 
      case tnk_code: train[car_count] = new tank_car; break; 
      case cab_code: train[car_count] = new caboose;  break; 
    } 

//Display report.
  for (n = 0; n < car_count; ++n)  

// Display shortname & capacity & terminate the line:
cout << train[n] -> short_name ( ) 
     << "     "
     << train[n] -> capacity ( )
     << endl; 

}

// Sample Data
0 1 1 2 3

// Result
                          eng 0
                          box 3990
                          box 3990
                          tnk 1539.38
                          cab 0
            

I cannot see where it's wrong.. so, I decided post for help on the c++ g+ community and include the entire script as following:

well, unfolding out some context (as I think I got it) as whenever a string appears in an
expression the value of that string is a pointer to the first element in the correspondent
character array (and a final null character, which the array is filled with, 
as end-of-string marker) the idea is playing out with the potential of writing functions
that return character strings. Or, as we could e.g. declare a variable whose value is a
string by: 
              'char *variable name = character string;'
& using
              'char* function name' 
if we wanted a function to return a string. Lets say e.g. we wanted control to in an
only (main) place a calling for returns which would be provided by local methods
(although there would be a lot uses for this functionality (apparently)).

e.g. make of this 2 following:
virtual void display_capacity ( ) { }
virtual void display_capacity ( ) {cout << volume ( );}

more versatile by writing them this way:

virtual double capacity( ){return 0.0;}
virtual double capacity( ){return volume( );}

The whole exercise as I could manage to complete summarizing with this chap some other
previous can be seen here:

[1] http://pastebin.com/Q362CVpy
            

37 How-To Use Call-By-Reference Parameters.

There are occasions when you have to hand a class object to a member function as an ordinary argument, rather than via the class-pointer operator or the class-member operator.

e.g. you might think that this function would work properly in the analyze_train program if called as follows:

// DEFECTIVE attempt to define ordinary_capacity_function! 
double ordinary_capacity_function (railroad_car r) { 
  return r.capacity ( ); 
} 

for (n = 0; n < car_count; ++n) { 
 // Display short name and capacity and terminate the line:  
 cout << train[n] -> short_name ( ) 
      << "     " 
      << ordinary_capacity_function (*train[n]) 
      << endl;                       --------- 
}                                        ^ 
                                         | 
Dereferenced array pointer identifies  --* 
a chunk of memory that holds a box car, 
tank car, engine, or caboose
            

Perhaps unexpectedly, incorporating this version of ordinary_capacity_function into the analyze_train program leads to the following result:

eng     0 
box     0 
box     0 
tnk     0 
cab     0
            

To tell C++ that the parameter is to be a call-by-reference parameter, we add an ampersand & to the parameter's type indicator:

                                               | 
                                               v 
double ordinary_capacity_function (railroad_car& r) { 
  return r.capacity ( ); 
}
            

With this one-character amendment, the ordinary_capacity_function works fine. The analyze_train program displays the following report, as it should:

eng     0 
box     2880 
box     2880 
tnk     1130.97 
cab     0
            

38 How-To Overload The Output Operator.

So figure out by target this chap is about the following statement:

Supose that you decide that you want to decorate the capacity report —the one displayed by the analyze_train program— with a simple iconic train, such as the following:

[eng]-[box]-[box]-[tnk]-[cab]
            

39 How-To Produce Tabular Displays

Basically is about using the printf function rather than the output operator.

40 How-To Write Statements That Read Character Strings.

As learned in seg. 36 now is about obtaining character strings from a file.

  • char input_buffer[100];
  • getline(name of character array, maximum characters to be read)

41 How-To Test String Characters.

It's about hoe to extract characters from strings, and how to use such characters to determine what to do next.

Obtaining e.g. the type-indicating character from the input_buffer character array is easy:

input_buffer[4]

//If data were stored in file e.g. as 'TPW-E-783' with the above statement we'd obtain 'E'
            

So:

char extract_car_code(char *input_buffer){...}
            

Could evolve to:

char extract_car_code(char *input_buffer){
                      return input_buffer[4];
                      }
            

So I could alter the switch function to read from file by:

switch (extract_car_code (input_buffer)) {
  case 'E': train[n] = new engine; break;
  ...
  }
//and still obtain de code and reporting e.g. for 'TPW-E-783' 'E' and so on..
            

42 How-To Deposit Character Strings Into Class Objects.

In the previous two sections, we learned how to obtain character strings from a file, how to extract characters from character strings, and how to test characters. In this section, we learn how to move a character string from a temporary buffer to a permanent location inside a class object using functions provided by c++ string handling library.

  • As content of input_buffer is being continuously used, first we should e.g. create a character array to store the value (the char string) we want to work with, as in e.g new char[10]
  • Then, by using #include <string> & call e.g. for strlen(input_buffer) function, we could:
    • create a new array making sure to add 1, to account for the null character as in:

      new char[strlen(input_buffer) + 1]

  • Next, using strcpy (for string copy) from string library function we must arrange to copy that content into the new character just been created: strcpy (e-g-serial_number, input_buffer);

43 How-To Reclaim Memory With Delete And Destructors.

After reading the chapter I indeed wondered why C++ does not reclaim memory automatically whenever a pointer is reassigned to a new object created by the new operator. The rationale is that chunks of memory accessed trough a pointer are often accessible through more than one pointer. Accordingly, C++ cannot assume that reassignment of a single pointer makes a chunk of memory inaccessible.

So, as this seems to have features of being a routine:

  • We can use a while loop, e.g.: while (1) {...}
  • As seen below we make the railroad_car destructor virtual, by which we tell C++ to treat all the lower-level destructors as virtual. Thus, a virtual destructor exposes destructors in derived classes (see segment 649).

Another way to prevent leaks is to keep track of the number of objects that you have created by having constructors and destructors increment and decrement an object counter. You could arrange for a global variable to be the object counter, better using what is called a static member variable.

Normal member variables are duplicated once for each class object. Static member variables are not —there is just one static member variable for the entire class in which the static member variable is defined.

#include <iostream>
#include <fstream>
#include <string>
const double pi = 3.14159;
// Box and cylinder class definitions go here
class railroad_car {
  public: char *serial_number;
          // Constructors:
          railroad_car ( ) {++counter;}
          railroad_car (char *input_buffer) {
            ++counter;
            serial_number = new char[strlen(input_buffer) + 1]; 
            strcpy (serial_number, input_buffer);
          }
          // Destructor:
          virtual ~railroad_car ( ) {
             cout << "Destroying a railroad car; "
                  << --counter << " remain." << endl;
             delete [ ] serial_number;                          
          }
          // Other:
          virtual char* short_name ( ) {return "rrc";}
          virtual double capacity ( ) {return 0.0;}
  private: static int counter;
};
int railroad_car::counter = 0;
// Other railroad-car class definitions go here
railroad_car *train[100];
char input_buffer[100];
enum {eng_code = 'E', box_code = 'B', tnk_code = 'T', cab_code = 'C'};
char extract_car_code (char *input_buffer) {return input_buffer[4];}
void do_analysis (ifstream& car_stream) {
  int n, car_count;
  for (car_count = 0; car_stream >> input_buffer; ++car_count) {
    delete train[car_count];                                    
    switch (extract_car_code (input_buffer)) {
      case eng_code:
        train[car_count] = new engine (input_buffer); break;    
      case box_code:
        train[car_count] = new box_car (input_buffer); break;   
      // Entries for tank cars and cabooses 
    } 
  } 
  car_stream.close ( ); 
  for (n = 0; n < car_count; ++n)  
    cout << train[n] -> serial_number << "     " 
         << train[n] -> short_name ( ) << "     " 
         << train[n] -> capacity ( ) << endl; 
} 
main ( ) { 
  // Read file name and call analysis function repeatedly: 
  while (1) { 
    cout << "Please type the name of a train file." << endl << ": "; 
    cin >> input_buffer; 
    // Open the input file: 
    ifstream car_stream (input_buffer); 
    // Give up if the input file does not exist: 
    if (!car_stream) {cout << "No such file." << endl; return 0;} 
    // Analyze: 
    do_analysis (car_stream); 
    // Close: 
    car_stream.close ( ); 
  } 
}
            

44 How-To Prevent Object Copying.

Here more about memory management in general, and about copy constructors in particular.

45 How-To Organize A Multiple-File Program.

In section 16 you learned about the organisation of class hierarchies. In this section, you learn about the organisation of files.

Suppose, for the sake of illustration, that you decide to divide the analize_train program into three files: trains.cxx, containers & cars; then, we can use #include declarations to incorporate the text in the containers file and the cars file back into the trains.cxx file:

#include "containers"
#include "cars"
            

Note that, when a file specification is surrounded by double-quotation marks, the C++ compiler attempts to find the file in the current directory —which is presumably the same one that contains the source code.

With the header files properly included, we can use the C++ compiler to compile each source-code file separately, producing object code. The object code files, by convention, have o extensions: trains.o, containers.o, cars.o. …When you make changes in a big, multiple-file program, you need to recompile only the altered files; & leave unaltered files alone.

PS. I should skip going further into this section (though I got the idea) because of the info I have about the *.h files (or libraries) (I still don't know what updated is this standard..)

46 How-To Compile A Multiple-File Program.

When you build big programs, with many header and source-code files, you eventually lose track of what depends on what. Accordingly, most operating systems that support C++ provide what is called the make utility.

When you compile a multiple-file program using the make utility, the file describing dependencies is called a makefile. Following section 45 dependency would be reflected e.g. as:

analyze_train: trains.o containers.o cars.o

trains.o: trains.cxx cointainers.h cars.h

containers.o: containers.cxx containers.h
cars.o: cars.cxx cars.h containers.h
            

47 How-To Implement Lists.

In this section, we learn about storing objects in lists. Why?

  • The implementation of lists demonstrates that we can use C++ classes to implement programming-language features, as well as to represent domain-specific concepts.
  • List manipulation is a useful tool: When we use list, instead of an ordinary array, we do not need to anticipate how many objects we will need to store, so we can conserve the space that would be wasted by safe worst-case estimates.
  • Lists manipulation also provides a compelling context for the introduction of other C++ concepts as class friends and template functions.

To do it we basically could include in the class definition itself a pointer to the next class object, excepting for the final one which always will carry a pointer to 0 (known as the NULL pointer). This is what we can call a list but, made of internal pointers. However this design has at least two issues:

  • We must add a new member variable to the class definition that should define the objects we want to tie together & which accessing to may have not get included or implementation could be so buggy.
  • We may want to include particular class objects in more than one list, which would make so complicated when adding internal pointers for each such list.

So, we also can try a completely new approach. We can create a new class, the link class. So that objects in this new class have two pointers; one pointing to the next link object, and the other pointing to a class member object. Advantages?.

  • We don't have to alter the class definition of the objects we want to tie together.
  • We can include class member objects in as many lists as we like without further class definition.
  • We can include objects that belong to the class member we are on sub-classes.

Now, so that designing to the header & link classes, we need to answer the following questions:

  • What member variables we do need for the pointers involved?
  • What member functions we do need to create lists, to add new lists elements, and to accessing elements?
  • Which member variables & member functions should be protected, and which public (though for now we will defer this question and make member variables & member functions all public)

Next, we need a way to create an empty list-one that has not link objects. Or a header constructor that explicitly assigns first_link_pointer to the NULL pointer, e.g.:

class header {
   public: link *first_link_pointer;
           header () {
             first_link_pointer = 0;
           }
           ...
 };

// or 

class header {
   public: link *first_link_pointer;
           header () {
             first_link_pointer = NULL;
           }
           ...
 };
            

We also need a function for adding an element to a list (which in its basic mode makes all additions to the front of the list & using the add member function) as in:

A header variable
    |
    |  Name of the element-adding function
    |    |
  train.add (pointer to a class member object);
            

being add a member function of the header class it does add a pointer to a new class member object provided as an ordinary argument, & must have a parameter that is declared to be a pointer to that given class member object.

class header {
   public: link *first_link_pointer;
           header () {
             first_link_pointer = NULL;
           }

 // e.g. with railroad_car class seen this book troughout
          void add (railroad_car *new_element) {
           ...
           }
           ...
 };
            

Note, then, that part of the work done by add is actually done by the two-argument constructor for the link class.

class link {
  public:
    link *next_link_pointer;

    // e.g. railroad_car class

    railroad_car *element_pointer;
    link (railroad_car *e, link *l) {
      element_pointer = e;
      next_link_pointer = l;
    }
};
            

So, the link constructor is called when a new link object is created inside of the add member function.

class header {
   public: link *first_link_pointer;
           header () {
             first_link_pointer = NULL;
          }

 // e.g. with railroad_car class seen this book troughout
          void add (railroad_car *new_element) {
            first_link_pointer = new link (new_element, first_link_pointer);
            ...
          }
           ...
 };
            

Note that add reroutes the pointer in the header object to point to the new link.

At this point we can construct an empty list and add elements to an existing list, but we cannot get at a list's elements. We would need the following:

  • A pointer, current_link_pointer
class header { 
  public: 
    link *first_link_pointer; 
    link *current_link_pointer; 
    header ( ) { 
      first_link_pointer = NULL; 
      current_link_pointer = first_link_pointer; 
    }
            
  • A function, advance, that advances current_link_pointer from one link to the next.
void advance ( ) { 
    current_link_pointer = current_link_pointer -> next_link_pointer; 
  }
            
  • A function access, that obtains a pointer to a, e.g., railroad_car object from the link pointed at by current_link_pointer
railroad_car* access ( ) { 
  return current_link_pointer -> element_pointer; 
}
            
  • A function endp, for end predicate, that determines whether the current_link_pointer's value is NULL

This endp predicate is easy to define, because, when we work on a pointer with the negation operation, the result is 0 of type int, if the pointer is the NULL pointer; otherwise, the result is 1, of type int, so:

int endp ( ) { 
  return ! current_link_pointer; 
}
            
  • A function, reset, that assigns the value of current_link_pointer to the value of first_link_pointer
void reset ( ) {                               
  current_link_pointer = first_link_pointer;   
}
            

An overview to which is done so far:

class header { 
  public: 
    link *first_link_pointer; 
    link *current_link_pointer; 
    header ( ) { 
      first_link_pointer = NULL; 
      current_link_pointer = first_link_pointer; 
    } 
    void add (railroad_car *new_element) { 
      first_link_pointer = new link (new_element, first_link_pointer); 
      current_link_pointer = first_link_pointer; 
    } 
    void advance ( ) { 
      current_link_pointer = current_link_pointer -> next_link_pointer; 
    } 
    railroad_car* access ( ) { 
      return current_link_pointer -> element_pointer; 
    } 
    int endp ( ) { 
      return ! current_link_pointer; 
    } 
    void reset ( ) {                               
      current_link_pointer = first_link_pointer;   
    }                                              
};
            

And the whole exercise to:

#include   
#include   
const double pi = 3.14159; 
// Box, cylinder, and railroad-car class definitions go here 
// Define list classes: 
class link { 
  public: 
    link *next_link_pointer; 
    railroad_car *element_pointer; 
    link (railroad_car *e, link *l) { 
      element_pointer = e; 
      next_link_pointer = l; 
    } 
}; 
class header { 
  public: 
    link *first_link_pointer; 
    link *current_link_pointer; 
    header ( ) { 
      first_link_pointer = NULL; 
      current_link_pointer = first_link_pointer; 
      } 
    void add (railroad_car *new_element) { 
      first_link_pointer = new link (new_element, first_link_pointer); 
      current_link_pointer = first_link_pointer; 
    } 
    void advance ( ) { 
      current_link_pointer = current_link_pointer -> next_link_pointer; 
    } 
    railroad_car* access ( ) { 
      return current_link_pointer -> element_pointer; 
    } 
    int endp ( ) { 
      return ! current_link_pointer; 
    } 
    void reset ( ) { 
      current_link_pointer = first_link_pointer; 
    } 
}; 
// Define list header: 
header train; 
char input_buffer[100]; 
enum {eng_code = 'E', box_code = 'B', tnk_code = 'T', cab_code = 'C'}; 
char extract_car_code (char *input_buffer) {return input_buffer[4];} 
main ( ) { 
  // No initialization or increment expressions: 
  for (; cin >> input_buffer;) 
    switch (extract_car_code (input_buffer)) { 
      case eng_code: train.add (new engine (input_buffer));   break; 
      case box_code: train.add (new box_car (input_buffer));  break; 
      case tnk_code: train.add (new tank_car (input_buffer)); break; 
      case cab_code: train.add (new caboose (input_buffer));  break; 
    } 
  train.reset ( ); 
  // No initialization; increment expression advances list: 
  for (; !train.endp ( ) ; train.advance ( ))  
    // Display number, short name, and capacity and terminate the line:  
    cout << train.access ( ) -> serial_number 
         << "     " 
         << train.access ( ) -> short_name ( ) 
         << "     " 
         << train.access ( ) -> capacity ( ) 
         << endl; 
} 
--- Data ---
TPW-E-783 
PPU-B-422 
NYC-B-988 
NYC-T-988 
TPW-C-271 
--- Result --- 
TPW-C-271     cab     0 
NYC-T-988     tnk     1539.38 
NYC-B-988     box     3990 
PPU-B-422     box     3990 
TPW-E-783     eng     0
            

48 How-To Make One Class A Friend Of Another Class.

In section 47 we learned about the definition of two classes, header and link & that all member variables and member functions in those two classes were in the public portion of the class definition, where they are too freely accessible.

Of course, add, advance, access, endp, reset and the header constructor are used in the train program and therefore should be in the public portion of the header class. In this section, we learn how to make all other member variables and member functions inaccessible, except through those six functions, which constitute the public interface. Along the way we learn about friends and why they are needed.

Well, happen that moving member variables & the link class constructor to the private would make them inaccessible & (or put them in the protected portion of the class definition) make it the header class a subclass of the link class would create more problems than help. This later e.g. would make header objects to carry around element_pointer & next_link_pointer member variables as excess baggage.

So, there being a C++ mechanism whereby the link class can grant complete access privileges to the member functions of the header class. We just grab it & We do this by adding a friend declaration to the definition of the link class.

class link {
  friend class header;
 private:
   link *next_link_pointer;
   railroad_car *element_pointer;
   link (railroad_car *e, link *l) {
     element_pointer = e;
     next_link_pointer = l;
    }
};
            

49 How-To Reuse Class Definitions Using Templates.

In the section 47, we learned how to use the header and link classes to make e.g. a list of railroad cars. In this section we will use the template mechanism, a general feature of c++ with wide applicability to package and reuse so, once we have had defined template classes for list, it's easy to create new lists.

As a step forward understanding c++ mechanism we have just extracted a template from the header and link classes shown in segment 763

class link { 
  friend class header; 
  private: 
    link *next_link_pointer; 
    railroad_car *element_pointer; 
    link (railroad_car *e, link *l) { 
      element_pointer = e; 
      next_link_pointer = l; 
    } 
}; 
class header { 
  public: 
    header ( ) { 
      first_link_pointer = NULL; 
      current_link_pointer = first_link_pointer; 
    } 
    void add (railroad_car *new_element) { 
      first_link_pointer = new link (new_element, first_link_pointer); 
      current_link_pointer = first_link_pointer; 
    } 
    void advance ( ) { 
      current_link_pointer = current_link_pointer -> next_link_pointer; 
    } 
    railroad_car* access ( ) { 
      return current_link_pointer -> element_pointer; 
    } 
    int endp ( ) { 
      return ! current_link_pointer; 
    } 
    void reset ( ) { 
      current_link_pointer = first_link_pointer; 
    } 
  private: 
    link *first_link_pointer; 
    link *current_link_pointer; 
}; 

////////
class link { 
  friend class header; 
  private:                     
    link *next_link_pointer;   
    / *element_pointer;    
    link (/ *e, link *l) { 
      element_pointer = e;     
      next_link_pointer = l;   
    }                          
}; 
class header { 
  public: 
    header ( ) { 
      first_link_pointer = NULL; 
      current_link_pointer = first_link_pointer; 
    } 
    void add (/ *new_element) { 
      first_link_pointer = new link (new_element, first_link_pointer); 
      current_link_pointer = first_link_pointer; 
    } 
    void advance ( ) { 
      current_link_pointer = current_link_pointer -> next_link_pointer; 
    } 
    /* access ( ) { 
      return current_link_pointer -> element_pointer; 
    } 
    int endp ( ) { 
      return ! current_link_pointer; 
    } 
    void reset ( ) { 
      current_link_pointer = first_link_pointer; 
    } 
  private:                       
    link *first_link_pointer;    
    link *current_link_pointer;  
};
            

49.1 Template, simple syntax…

template <class link_parameter>
class link {
  ...
};

template <class header_parameter>
class header {
  ...
};
              

Here, then, is the analyze_train program, newly revised to make use of the generic template class:

#include   
#include   
const double pi = 3.14159; 
// Box, cylinder, and railroad-car class definitions go here 
// Define list classes: 
template  
class link { 
  friend class header; 
  private: 
    link *next_link_pointer; 
    link_parameter *element_pointer; 
    link (link_parameter *e, link *l) { 
      element_pointer = e; 
      next_link_pointer = l; 
    } 
}; 
template  
class header { 
  public: 
    header ( ) { 
      first_link_pointer = NULL; 
      current_link_pointer = first_link_pointer; 
    } 
    void add (header_parameter *new_element) { 
      first_link_pointer = 
        new link (new_element, first_link_pointer); 
      current_link_pointer = first_link_pointer; 
    } 
    void advance ( ) { 
      current_link_pointer = current_link_pointer -> next_link_pointer; 
    } 
    header_parameter* access ( ) { 
      return current_link_pointer -> element_pointer; 
    } 
    int endp ( ) { 
      return ! current_link_pointer; 
    } 
    void reset ( ) { 
      current_link_pointer = first_link_pointer; 
    } 
  private: 
    link *first_link_pointer; 
    link *current_link_pointer; 
}; 
// Define list header: 
header train; 
char input_buffer[100]; 
enum {eng_code = 'E', box_code = 'B', tnk_code = 'T', cab_code = 'C'}; 
char extract_car_code (char *input_buffer) {return input_buffer[4];} 
main ( ) { 
  // Train list is constructed here 
  train.reset ( ); 
  // No initialization; increment expression advances list: 
  for (; !train.endp ( ) ; train.advance ( ))  
    // Display number, short name, and capacity and terminate the line:  
    cout << train.access ( ) -> serial_number 
         << "     " 
         << train.access ( ) -> short_name ( ) 
         << "     " 
         << train.access ( ) -> capacity ( ) 
         << endl; 
} 
--- Data ---
TPW-E-783 
PPU-B-422 
NYC-B-988 
NYC-T-988 
TPW-C-271 
--- Result --- 
TPW-C-271     cab     0 
NYC-T-988     tnk     1539.38 
NYC-B-988     box     3990 
PPU-B-422     box     3990 
TPW-E-783     eng     0
              

50 How-To Iterate Over Lists Using Iteration Class Object.

Now, what if we ever need to have more than one traversal going simultaneously (the reset function and changes in lists (as defined traversing a list alters that list because of the changes made to the current_link_pointer) concurrency is not necessarily stated).

So, in this section, we learn about iteration classes, which allow us to separate list construction from list traversal, thereby enabling multiple simultaneous traversals.

So we will need:

  • Firstly, define to the iterator class to be a template class
template 
class iterator {
  ...
  private: link* current__link_pointer;  
           link* first_link_pointer;     
};
            
  • Next, we need member functions to access the elements, then an advance function so going from current_link_pointer to the next link object, then, test if current_link_pointer is the NULL pointer, and reset the current_link_pointer. So basically we could borrow all these member functions from the definition of the header template class.
template  
class iterator { 
  public:  ... 
    iterator_parameter* access ( ) { 
      return current_link_pointer -> element_pointer; 
    } 
    void advance ( ) { 
      current_link_pointer = current_link_pointer -> next_link_pointer; 
    } 
    int endp ( ) { 
      return ! current_link_pointer; 
    } 
    void reset ( ) { 
      current_link_pointer = first_link_pointer; 
    } 
  private: link* current_link_pointer; 
           link* first_link_pointer; 
};
            
  • Then, on defining the iterator constructor:
iterator (header<iterator_parameter>& header) {
  first_link_pointer = header.first_link_pointer;
  current_link_pointer = first_link_pointer;
 }
            
  • Next, we must ensure that the iterator member function have access to the header and link member variables by declaring the iterator class to be a friend of both the header class and the link class.
template <class link_parameter>
class link {
  friend class iterator<link_parameter>;
  friend class header<link_parameter>;
  ...
};

template <class parameter>
class header {
  friend class iterator<parameter>;
  ....
};
            

Now, in the following program,

#include   
#include   
const double pi = 3.14159; 
// Container, railroad-car, and list definitions go here 
template  
class iterator { 
  public: 
    iterator (header& header) { 
      first_link_pointer = header.first_link_pointer; 
      current_link_pointer = first_link_pointer; 
    } 
    iterator_parameter* access ( ) { 
      return current_link_pointer -> element_pointer; 
    } 
    void advance ( ) { 
      current_link_pointer = current_link_pointer -> next_link_pointer; 
    } 
    int endp ( ) { 
      return ! current_link_pointer; 
    } 
    void reset ( ) { 
      current_link_pointer = first_link_pointer; 
    } 
  private: link* current_link_pointer; 
           link* first_link_pointer; 
}; 
// Define list header: 
header train; 
char input_buffer[100]; 
enum {eng_code = 'E', box_code = 'B', tnk_code = 'T', cab_code = 'C'}; 
char extract_car_code (char *input_buffer) {return input_buffer[4];} 
main ( ) { 
  // Collect train elements as usual 
  // Define and initialize iterator class object:

//Seg; 785: the expression 'railroad_car', provided as template argument in the expression
// 'iterator<railroad_car> train_iterator(train)'
//causes the iterator class to be instantiated as to deal with 
//railroad_cars. The expression 'train' provides the new iterator with
//access to a particular list of railroad cars.

  iterator <railroad_car>  train_iterator (train); 
  // Iterate: 
  train_iterator.reset ( ); 
  for (; !train_iterator.endp ( ) ; train_iterator.advance ( ))  
    // Display number, short name, and capacity and terminate the line:  
    cout << train_iterator.access ( ) -> serial_number << "     " 
         << train_iterator.access ( ) -> short_name ( ) << "     " 
         << train_iterator.access ( ) -> capacity ( ) << endl; 
} 
--- Data ---
TPW-E-783 
PPU-B-422 
NYC-B-988 
NYC-T-988 
TPW-C-271 
--- Result --- 
TPW-C-271     cab     0 
NYC-T-988     tnk     1539.38 
NYC-B-988     box     3990 
PPU-B-422     box     3990 
TPW-E-783     eng     0
            

Author: Txe LLenne

Created: 2016-05-08 Sun 15:08

Emacs 24.5.1 (Org mode 8.2.10)

Validate