CS291 Selected Lecture Notes

Contents

  • Lecture 1, Introduction and Overview
  • Lecture 2, Available Libraries
  • Lecture 3, Input/Output statements
  • Lecture 4, Exceptions try, throw and catch
  • Lecture 5, Introduction to Classes
  • Lecture 6, Class Inheritance
  • Lecture 7, Multiple Inheritance
  • Lecture 8, Virtual Inheritance
  • Lecture 9, Pure Virtual, Abstract
  • Lecture 10, Review for Quiz 1
  • Lecture 11, Quiz 1 - see Homework page
  • Lecture 12, Friend, cout <<, static
  • Lecture 13, Hierarchies, composition
  • Lecture 14, Templates
  • Lecture 15, STL <vector>
  • Lecture 16, STL <algorithm>
  • Lecture 17, STL <string>
  • Lecture 18, STL <list> and <set>
  • Lecture 19, STL <priority_queue> and file IO
  • Lecture 20, Project Description
  • Lecture 21, Review for Quiz 2
  • Lecture 22, Quiz 2
  • Lecture 23, All about const and parameters
  • Lecture 24, Recursive objects and eight queens
  • Lecture 25, Mixing C and C++
  • Lecture 26, STL <complex> and extensions
  • Lecture 27, Object Oriented Draw Program Design
  • Lecture 28, Traffic Simulation Classes, vehicle.h etc
  • Lecture 29, Review for final Exam
  • Lecture 30, Final Exam
  • Other important links
  • Lecture 1, Introduction and Overview

    
      This is a practical course that will provide you with a skill
      that you can use in may other courses. 
    
      The purpose of the course is to have you be able to write
      programs in the C++ language.
    
      C++ is a large and complex language. It took many people many
      years to develop the language and define the language as
      an international standard. ISO/IEC 14882:1998
    
      This course will teach C++ according to the standard because
      eventually the various C++ compilers will accept the
      standard language rather than the various experimental
      versions of C++.
    
      See the course  WEB page. 
      See the course  syllabus. 
      See the course  homework assignments. 
      See  chaotic state of C++ compilers. 
      See  Compact Summary of C++ 
    
    
    

    Lecture 2, Available Libraries

    
    Technically there is a C++ language and there are C++ libraries.
    
    The ISO/IEC 14882:1998 C++ standard includes the formal definition
    of both the language and the libraries.
    
    A list of the library names is in the  C++ summary 
    
    

    Lecture 3, Input/Output statements

    
      The simplest Input/Output is from the  keyboard/display.
    
      To use this Input/Output a program must include the two lines:
    
           #include <iostream>
           using namespace std;
    
      The "#" line is a preprocessor directive to read the standard library
      file named  iostream  .
      The "using namespace std;" is necessary to make the operators >> and <<
      visible.
    
      The statement   cout << "Hello." << endl;
      would write  Hello.  to the display.
    
      The statements  int amount;
                      cin >> amount;
      would read an integer from the keyboard and place the value in 'amount'
    
      A skeleton program to read and print data is shown below. change.cpp
    
    // change.cpp 
    
    #include <iostream>            // basic console input/output
    #include <cctype>              // handy C functions e.g. isdigit
    using namespace std;           // bring standard include files in scope
    
    int main()
    {
      int amount; // value read by  cin
    
      while(!cin.eof())  // a loop that ends when a end of file is reached
      {
        try  // be ready to catch any exceptions and stay in the loop
        {
          if(!isdigit(cin.peek())) throw "not a number";
    
          cin >> amount;     //read something, hopefully an integer
          cout << "Input: " << amount ;     // start the output line
    
          // more code here that computes and outputs using  cout
    
          cout << endl; // this ends the output line
    
        }
        catch(...){ cout << " no change possible " << endl; }
    
        cin.ignore(80, '\n'); // get rid of any junk after the number
    
      } // end of while loop
    
      cout << "end of change run" << endl;
      return 0;
    } // end main
    
    
    For disk file I/O see  test_file_io.cpp 
    
    

    Lecture 4, Exceptions try, throw and catch

    
      The structure of exceptions is:
    
      try
      {
          ... statements
          throw "ooopse";
          ... statements
      }
      catch(   ) { ... statements }
      catch(...) { ... statements }
    
    Several example programs and their output are: try_try2.cc
    
    // try_try2.cc  demonstrate how the type of 'catch' get various 'throw'
    
    #include <iostream>
    using namespace std;
    
    int main()
    {
      typedef int my_int;
      my_int k;
    
      for(int i=0; i<5; i++)
      {
        cout << i << '\n';
        try
        {
    	cout << "in try block, i= " << i << '\n';
    	if(i==0) throw "first exception";
    	cout << "in try block after first  if \n";
    	if(i==1) throw i;
    	cout << "in try block after second if \n";
    	if(i==2) throw (float)i;
    	cout << "in try block after third if \n";
    	if(i==3) { k=i; throw k;}
    	cout << "in try block after third if \n";
    	throw 12.5; // a double
    	cout << "should not get here \n"; // compiler warning also
        }
        catch(const char * my_string){
    				  cout << "my_string  " << my_string << '\n';
    	 			 }
        catch(const int j){
    	                cout << "caught an integer = " << j << '\n';
    		      }
        catch(const double j){
    	                   cout << "caught a double = " << j << '\n';
    		         }
        catch(...){
    	        cout << "caught something \n" << '\n' << '\n';
    	      }
        cout << "outside the  try  block \n";
      }
      cout << "outside the loop, normal return \n";
      return 0;
    } // end main
    
    
    0
    in try block, i= 0
    my_string  first exception
    outside the  try  block 
    1
    in try block, i= 1
    in try block after first  if 
    caught an integer = 1
    outside the  try  block 
    2
    in try block, i= 2
    in try block after first  if 
    in try block after second if 
    caught something 
    
    
    outside the  try  block 
    3
    in try block, i= 3
    in try block after first  if 
    in try block after second if 
    in try block after third if 
    caught an integer = 3
    outside the  try  block 
    4
    in try block, i= 4
    in try block after first  if 
    in try block after second if 
    in try block after third if 
    in try block after third if 
    caught a double = 12.5
    outside the  try  block 
    outside the loop, normal return 
    
    
    
    And, now you can see how an exception thrown from a nested (possibly
    deeply nested) function comes up the call stack to the first 'catch'
    that can handle the type of the thrown exception. try_nest.cpp
    
    // try_nest.cpp  'throw' from nested function call
    
    #include <iostream>
    using namespace std;
    
    void f1(void);
    void f2(void);
    
    int main(void)
    {
        cout << "Staring try_nest.cpp \n";
        try
        {
            f1();
    	cout << "should not get here in main \n";
        }
        catch(const int j){
    	                cout << "caught an integer = " << j << '\n';
    		      }
        cout << "outside the  try  block \n";
        return 0;
    } // end main
    
    void f1(void)
    {
      cout << "in f1, calling f2 \n";
      f2();
      cout << "in f1, returned from f2 \n";
    }
    
    void f2(void)
    {
      cout << "in f2, about to throw an exception \n";
      throw 7;
    }
    
    
    Staring try_nest.cpp 
    in f1, calling f2 
    in f2, about to throw an exception 
    caught an integer = 7
    outside the  try  block 
    
    

    Lecture 5, Introduction to Classes

     
    
    A class in C++ is a data structure (often called an abstract data type)
    and all the functions needed to operate on that data structure
    (called member functions).
    
    This is a very simple example of a circle class.
    A class is a definition and the class defines a new type.
    
    A typical setup is shown below:
      The class  Circle  is defined in a header file   circle.h
      The class is implemented in a  .cpp .cc .C file  circle.cpp
      because users of the class just need the header.
      The test program  test_circle.cpp  must do a #include "circle.h"
      but definitely does NOT include  circle.cpp
    
    The test program instantiates the class Circle to create the object 'c1'
    The test program uses the constructor  Circle  to initialize the object.
    
    // circle.h 
    #ifndef CIRCLE_H   // be sure file only included once per compilation
    #define CIRCLE_H
    
    //
    // Define a class that can be used to get objects of type Circle.
    // A class defines a data structure and the member functions
    // that operate on the data structure.
    // The name of the class becomes a type name.
    
    class Circle  // the 'public' part should be first, the user interface
                  // the 'private' part should be last, the safe data
    {
      public:
        Circle(double X, double Y, double R);  // a constructor
        void Show();                           // a member function
        void Set(double R);                    // change the radius
        void Move(double X, double Y);         // move the circle
      private:
        double Xcenter;
        double Ycenter;
        double radius;
    };
    
    #endif  // CIRCLE_H      nothing should be added after this line
    
    
    // circle.cpp 
    // implement the member functions of the class Circle
    #include <iostream>
    #include "circle.h" 
    using namespace std;
    
    Circle::Circle(double X, double Y, double R)
    {
      Xcenter = X;
      Ycenter = Y;
      radius  = R;
    }
    
    void Circle::Show()
    {
      cout << "X, Y, R " << Xcenter << "  " << Ycenter << "  "
           << radius << endl;
    }
    
    void Circle::Set(double R)
    {
      radius = R;
    }
    
    void Circle::Move(double X, double Y)
    {
      Xcenter = X;
      Ycenter = Y;
    }
    
    
    // test_circle.cpp  
    #include <iostream>
    #include "circle.h" 
    using namespace std;
    
    int main()
    {
      Circle c1(1.0, 2.0, 0.5);  // construct an object named 'c1' of type 'Circle'
      Circle circle2(2.5, 3.0, 0.1);  // another object named 'circle2'
    
      c1.Show();       // tell the object c1 to execute the member function Show
      circle2.Show();  // circle2 runs its member function Show
    
      c1.Move(1.1, 2.1);  // move center
      c1.Show();
    
      circle2.Set(0.2);   // set a new radius
      circle2.Show();
      return 0;
    }
    
    
    
    The files above can be saved to your directory and compiled then executed with:
    
      on gl SGI   CC -n32 -Iinclude -o test_circle  test_circle.C circle.C
                  test_circle
    
      on Unix     g++ -o test_circle  test_circle.cc circle.cc
                  test_circle
    
      on PC VC++  cl /GX /ML test_circle.cpp circle.cpp
                  test_circle
    
    The result of the execution is:
    X, Y, R 1  2  0.5
    X, Y, R 2.5  3  0.1
    X, Y, R 1.1  2.1  0.5
    X, Y, R 2.5  3  0.2
    
    

    Lecture 6, Class Inheritance

     
    
    A very similar program to Lecture 4 can be created using
    class inheritance.
    
    A useful memory aid to determine when inheritance should be used
    is to say  "inheriting class" is a "base class"
    
    The "is a" applies here:   a Circle is a Shape
    The example below defines a base class (class that does not inherit) Shape2
    and defines a class Circle2 that inherits the base class.
    
    The word "inherits" is to be taken literally.
    The class  Circle2  really has member functions  Move, Xc and Yc .
    The class  Circle2  really has three double precision numbers in
    its data structure (in the private part) Xcenter, Ycenter and radius.
    
    
    The following code show five files: shape2.h   shape2.cpp
                                        circle2.h  circle2.cpp  test_shape.cpp
    
    // shape2.h
    #ifndef SHAPE2_H
    #define SHAPE2_H
    //
    // Demonstrate simple class inheritance and its test program all in one file
    // the Shape2 class provides the center for specific shapes that inherit it
    class Shape2  // demonstrate a base class
    {
      public:
        void Move(double X, double Y);  // move center of shape to new X,Y
        double Xc();                    // return Xcenter
        double Yc();                    // return Ycenter
      private:
        double Xcenter;
        double Ycenter;
    }; // end Shape2
    
    #endif  // SHAPE2_H
    
    
    
    // shape2.cpp
    // implement the member functions of the class Shape2
    #include "shape2.h"
    
    void Shape2::Move(double X, double Y)
    {
      Xcenter = X;
      Ycenter = Y;
    }
    
    double Shape2::Xc()
    {
      return Xcenter;
    }
    
    double Shape2::Yc()
    {
      return Ycenter;
    }
    // end shape2.cpp
    
    
    
    // circle2.h
    #ifndef CIRCLE2_H
    #define CIRCLE2_H
    //
    // Define a class that can be used to get objects of type Circle.
    // A circle is a shape, so it makes sense for Circle2 to inherit Shape2.
    // A class defines a data structure and the member functions
    // that operate on the data structure.
    // The name of the class becomes a type name.
    
    #include "shape2.h" 
    class Circle2 : public Shape2      // <-- the colon ":" means "inherit"
                  // the 'public' part should be first, the user interface
                  // the 'private' part should be last, the safe data
    {
      public:
        Circle2(double X, double Y, double R);  // a constructor
        void Show();                            // a member function
        void Set(double R);                     // set a new radius
                                                // by inheritance  Move  is here
      private:
        double radius;  // by inheritance Xcenter and Ycenter are here
    };  // end Circle2
    
    #endif  // CIRCLE2_H
    
    
    
    // circle2.cpp 
    // implement the member functions of the class Circle2
    #include <iostream>
    #include "circle2.h" 
    using namespace std;
    
    Circle2::Circle2(double X, double Y, double R)
    {
      Move(X, Y);    // puts values into Xcenter, Ycenter in Shape2
      radius  = R;
    }
    
    void Circle2::Set(double R)
    {
      radius = R;
    }
    
    void Circle2::Show()
    {
      cout << "X, Y, R " << Xc() << "  " << Yc() << "  "
           << radius << endl;
    }
    // end circle2.cpp
    
    
    
    
    // test_shape.cpp 
    #include <iostream>
    #include "circle2.h"
    using namespace std;
    
    int main()
    {
      Circle2 c1(1.0, 2.0, 0.5);  // construct an object named 'c1' of type 'Circle'
      Circle2 circle1(2.5, 3.0, 0.1);  // another object named 'circle2'
    
      c1.Show();       // tell the object c1 to execute the member function Show
      circle1.Show();  // circle2 runs its member function Show
    
      c1.Move(1.1, 2.1);
      c1.Show();
    
      circle1.Set(0.2);
      circle1.Show();
      return 0;
    }            // end test_shape
    
    
    
    and the output from executing:
    
    X, Y, R 1  2  0.5
    X, Y, R 2.5  3  0.1
    X, Y, R 1.1  2.1  0.5
    X, Y, R 2.5  3  0.2
    
    

    Lecture 7 Multiple Inheritance,

    
    Before moving on to multiple inheritance, we need to
    understand the use of the three possible sections of a class
    with the following visibility:
    
    public:    member functions and objects defined in this section
               of a class are visible to any class that inherits
               this class as 'public'
               and visible in any object of this class.
    
    protected: member functions and objects defined in this section
               of a class are visible to any class that inherits
               this class as 'public' or 'protected'
               and not visible anywhere else.
    
    private:   member functions and objects defined in this section
               of a class are not visible anywhere.
    
    Then, the way a class is inherited may restrict the visibility further:
    
    inheriting class A  using  : public A;  keeps all visibility as above.
    
    inheriting class B  using  : protected B; makes the public section of B
                                 become restricted to protected visibility.
    
    inheriting class C  using  : private C;  makes all of C become
                                 restricted to private visibility.
    
    The following example, inherit.cpp
    demonstrates the three restrictions of inheritance
    with the three sections.  (All defaults are private.)
    
    // inherit.cpp   public:  protected:  private:
    //
    // define three classes  A, B, C with variables 1, 2, 3 in
    //                       public:  protected: and private: respectively
    // Then class D inherits public A, protected B and private C
    
    class A
    {
      public:
            int a1;
      protected:
            int a2;
      private:
            int a3;
    };
    
    class B
    {
      public:
            int b1;
      protected:
            int b2;
      private:
            int b3;
    };
    
    class C
    {
      public:
            int c1;
      protected:
            int c2;
      private:
            int c3;
    };
    
    
    class D: public A, protected B, private C  // various restricted inheritance
    {
      public:
            int d1;  // also here  a1
            int test();
      protected:
            int d2;  // also here  a2  b1  b2
      private:
            int d3;  // also here  a3  b3  c1  c2  c3
    };
    
    int D::test()
    {
      return d1 + a1 + d2 + a2 + b1 + b2 + d3; // all visible inside D
    
             // not visible  a3  b3  c1  c2  c3  inside D
    }
    
    int main()
    {
      D object_d;  // object_d has 12 values of type int in memory
      return object_d.d1 + object_d.a1;  // only these are visible outside D
    
      // not visible  object_d.  a2  a3  b1  b2  b3  c1  c2  c3  d2  d3
    }
    
    
    Now, Multiple Inheritance
    
    Pictorially, multiple inheritance can be shown by representing
    classes by boxes and inheritance by lines.
    The class at the top, class A, is a base class.
    
    
                +---------+
                |         |
                | class A |              a base class defining two int's
                |         |
                | int p,q |
                |         |
                +---------+
                 /       \
                /         \
               /           \
      +---------+         +---------+ 
      |         |         |         |   C now has two int's inherited
      | class B |         | class C |   from A in addition to the
      |         |         |         |   two int's C defines
      | int q,r |         | int q,s |
      |         |         |         |
      +---------+         +---------+
               \           /
                \         /
                 \       /
                +---------+   The programmer has a design decision with
                |         |   multiple inheritance: Does the programmer
                | class D |   want (or need) two copies of A in class D?
                |         |   Without 'virtual' you get two copies.
                | int q,t |   Using 'virtual' you get one copy.
                |         |
                +---------+
    
    The following program  multinh.cc  shows the design getting two
    copies of class A and thus two sets of A's  int p,q;
    (Further down is  virtinh.cc  that gets one copy of class A)
    
    
    // multinh.cc      demonstrate multiple inheritance
    //                 design requirement is that B and C have their own A
    //                 compare this to virtinh.cc
    
    class A                       // base class for B and C
    {                             // indirect class for D
      public:
        int p, q;
    };
    
    class B : public A            // single inheritance
    {
      public:
        int q, r;                 // now have A::p  A::q  B::q  B::r
        void f(int a){ A::q = a;} // because A::q won't be visible
        int  g(){ return A::q;}   // define functions to set and get
    };
    
    class C : public A            // another single inheritance
    {
      public:
        int q, s;                 // now have A::p  A::q  C::q  C::s
        void f(int a){ A::q = a;} // four integers
        int  g(){ return A::q;}
    };
    
    class D : public B, public C  // multiple inheritance
    {
      public:
        int q, t;                 // now have AB::p  AB::q  B::q  B::r
    };                            //          AC::p  AC::q  C::q  C::s
                                  //          D::q   D::t
                                  // ten integers
    
    #include <iostream>
    using namespace std;
    
    int main()
    {
      D stuff;             // ten (10) integers
    
      stuff.B::p = 1;      // really in A, ambiguous
      stuff.C::p = 2;      // really in A, picked p from A in C
     // stuff.B::A::q = 3; // can not get to this one as object
      stuff.B::f(3);       // set via a function in B
     // stuff.C::A::q = 4; // can not get to this one as object
      stuff.C::f(4);       // set via a function in C
      stuff.B::q = 5;      // the local q in B
      stuff.C::q = 6;      // the local q in C
      stuff.D::q = 7;      // the local q in D  stuff::q  also works
      stuff.r = 8;         // from B unambiguous
      stuff.s = 9;         // from C unambiguous
      stuff.t = 10;        // from D unambiguous
      cout << stuff.B::p << stuff.C::p << stuff.B::g() << stuff.C::g()
           << stuff.B::q << stuff.C::q << stuff.D::q   << stuff.r
           << stuff.s    << stuff.t    << endl;
      return 0; // prints  12345678910 to show ten variables stored
    }
    
    //
    //     functions are needed to get/set some variables
    //     this is due to lack of syntax in present C++  stuff.B::A::q should work
    
    
    Now, with a different design, the programmer may want only one copy
    of class A to be inherited into D.  This design technique is implemented
    by inheriting using the key word 'virtual'. The rule is that any duplicate
    inheritances of a class will not occur if that class is inherited as
    virtual.  If all inheritances are virtual, exactly one copy of the class
    will be inherited.  This is demonstrated by the file  virtinh.cc
    and should be compared to the file above to see the differences.
    
    // virtinh.cc       demonstrate multiple inheritance with 'virtual'
    //                  design requirement needs only one copy of A in D
    //                  compare this to multinh.cc
    
    class A               // base class for B and C
    {                     // indirect class for D
      public:
        int p, q;
    };
    
    class B : virtual public A    // single inheritance, virtual
    {
      public:
        int q, r;           // now have A::p  A::q  B::q  B::r
                            // no special set/get functions needed
    };
    
    class C : virtual public A    // another single inheritance, virtual
    {                             // now have four integers
      public:
        int q, s;           // now have A::p  A::q  C::q  C::s
    };
    
    class D : public B, public C  // multiple inheritance, only one A
    {                             // class inherited because B, C virtuals
      public:
        int q, t;           // now have A::p  A::q  B::q  B::r
    };                      //          C::q  C::s  D::q   D::t
                            // now have eight integers
    
    #include <iostream>
    using namespace std;
    
    int main()
    {
      D stuff;             // eight (8) integers
    
      stuff.p = 1;         // the local p in A
      stuff.A::q = 2;      // the local q in A   note four (4) q's
      stuff.B::q = 3;      // the local q in B
      stuff.C::q = 4;      // the local q in C
      stuff.q = 5;         // the local q in D
      stuff.r = 6;         // from B unambiguous
      stuff.s = 7;         // from C unambiguous
      stuff.t = 8;         // from D unambiguous
      cout << stuff.p    << stuff.A::q << stuff.B::q << stuff.C::q
           << stuff.D::q << stuff.r    << stuff.s    << stuff.t
           << '\n';
      return 0;        // prints   12345678 to show eight variables stored
    }
    
    

    Lecture 8, Virtual Inheritance

    
    The programmer of a class has a design technique to cause member functions
    to be invisible when inherited by any class that uses the same function
    prototype as the member function. This technique is know as defining
    a virtual function. A class inheriting a virtual function gets an actual
    function as long as it is not over ridden by a local definition.
    An example of a virtual member function is:
             virtual double Compute(double X);
    
    
    Given a class hierarchy, a set of classes using inheritance, the user of
    the classes can achieve what is called 'polymorphism'. Polymorphism
    is the result of having pointers to classes in a hierarchy and having
    a given pointer point to different classes at different times during
    the execution of a program. This can result in run time polymorphism
    where the decision of what function to call is made at execution
    time rather than at compilation time. The cases where polymorphism
    occurs are indicated in the comments in the examples below.
    
    
    The first example below demonstrates many cases for virtual member functions.
    The second example below demonstrates some cases of pure virtual member
    functions and some abstract classes.
     
    
    // test_vir.cc
    // show how a virtual function in a base class gets called
    // demonstrate polymorphism
    // compare this with testpvir.cc  using a pure function to make an abstract class
    
    class Base                // terminology: a base class is any class that
    {                         // does not inherit another class
      public:
        void f(void);
        virtual void v(void); 
    };
    
    class D1 : public Base    // note both f and v inherited
    {                         // then defined again
      public:
        void f(void);
        virtual void v(void); 
    };
    
    class D2 : public Base    // note both f and v inherited
    {                         // only v defined again
      public:
        virtual void v(void); 
    };
    
    class D3 : public Base    // note both f and v inherited
    {                         // only f defined again
      public:
        void f(void);
    };
    
    class DD1 : public D1  // a class derived from the a derived class
    {                      // now have potentially three f's and three v's
      public:
        void f(void);
        virtual void v(void); 
    };
    
    
    int main(void)
    {
      Base b,   *bp;    // declare an object and a pointer for each class
      D1   d1,  *d1p;
      D2   d2,  *d2p;
      D3   d3,  *d3p;
      DD1  dd1, *dd1p;
    
      b.f();     // calls  Base::f()
      b.v();     //        Base::v()
      bp=&b;
      bp->f();   //        Base::f()
      bp->v();   //        Base::v()   no difference with virtual yet
    
      d1.f();    //        D1::f()
      d1.v();    //        D1::v()
      d1p=&d1;
      d1p->f();  //        D1::f()
      d1p->v();  //        D1::v()     no difference with virtual yet
      bp=&d1;    //        now, make a pointer to the base type, point to an
                 //        object of a derived type of the base type
      bp->f();   //        Base::f() choose function belonging to pointer type
      bp->v();   //        D1::v()   now difference with virtual!
                 //                  run time polymorphism
    
      d2.f();    //        Base::f()
      d2.v();    //        D2::v()
      d2p=&d2;
      d2p->f();  //        Base::f()    D2 has no f(), get from base type
      d2p->v();  //        D2::v()     no difference with virtual yet
      bp=&d2;    //        now, make a pointer to the base type, point to an
                 //        object of a derived type of the base type
      bp->f();   //        Base::f() choose function belonging to pointer type
      bp->v();   //        D2::v()   now difference with virtual!
                 //                  run time polymorphism
    
      d3.f();    //        D3::f()
      d3.v();    //        Base::v()    D3 has no v(), get from base type
      d3p=&d3;
      d3p->f();  //        D3::f()
      d3p->v();  //        Base::v()   D3 has no v(), get from base type
      bp=&d3;    //        now, make a pointer to the base type, point to an
                 //        object of a derived type of the base type
      bp->f();   //        Base::f() choose function belonging to pointer type
      bp->v();   //        Base::v() no local function, choose from pointer type
    
      dd1.f();   //        DD1::f()
      dd1.v();   //        DD1::v()
      dd1p=&dd1;
      dd1p->f(); //        DD1::f()
      dd1p->v(); //        DD1::v()     no difference with virtual yet
      bp=&dd1;   //        now, make a pointer to the base type, point to an
                 //        object of a derived derived type of the base type
      bp->f();   //        Base::f()  choose function belonging to pointer type
      bp->v();   //        DD1::v()   now difference with virtual!
                 //                   this is run time polymorphism
    
      return 0;
    }
    
    #include <iostream>
    using namespace std;
    
    void Base::f(void)             // implementation of each function
    {                              // each function just outputs its name
      cout << "Base::f()" << endl;
    }
    void Base::v(void)
    {
      cout << "Base::v()" << endl;
    }
    void D1::f(void)
    {
      cout << "D1::f()" << endl;
    }
    void D1::v(void)
    {
      cout << "D1::v()" << endl;
    }
    void D2::v(void)
    {
      cout << "D2::v()" << endl;
    }
    void D3::f(void)
    {
      cout << "D3::f()" << endl;
    }
    void DD1::f(void)
    {
      cout << "DD1::f()" << endl;
    }
    void DD1::v(void)
    {
      cout << "DD1::v()" << endl;
    }
    
    /* result of running above file:
    Base::f() 
    Base::v() 
    Base::f() 
    Base::v() 
    D1::f() 
    D1::v() 
    D1::f() 
    D1::v() 
    Base::f() 
    D1::v() 
    Base::f() 
    D2::v() 
    Base::f() 
    D2::v() 
    Base::f() 
    D2::v() 
    D3::f() 
    Base::v() 
    D3::f() 
    Base::v() 
    Base::f() 
    Base::v() 
    DD1::f() 
    DD1::v() 
    DD1::f() 
    DD1::v() 
    Base::f() 
    DD1::v() 
    */
    
    
    

    Lecture 9, Pure Virtual, Abstract

    
    The programmer of a class has a design technique to force any class
    inheriting class to define a member function with a specific
    function prototype. An example of such a function is:
             virtual void Draw(void) = 0;
    The combination of 'virtual' and the syntax  = 0;
    causes this class to be an abstract class and causes any class that
    inherits this class to define a function  void Draw(void) if that class
    is to be other than an abstract class.
    
    An abstract class may define a pointer object but may not define an object.
    
    
    Now we use   virtual ... = 0;  to make a class an abstract class.
    No objects can be created from an abstract class.
    
    // testpvir.cc
    // show how a virtual function in a base class gets called
    // demonstrate polymorphism
    // compare this file to test_vir.cc that does not use 'virtual'
    
    class Base                     // a base class
    {
      public:
        void f(void);
        virtual void v(void) = 0;  // makes  v  a pure virtual function
    };                             // makes  Base  an abstract class
    
    class D1 : public Base         // a derived class
    {                              // inherits both f and v
      public:                      // then defines both
        void f(void);
        virtual void v(void); 
    };
    
    class D2 : public Base         // inherits f and v
    {                              // defines only v
      public:
        virtual void v(void); 
    };
    
    class D3 : public Base
    {
      public:
        void f(void);               // no  v  defined, thus D3 abstract also
    };
    
    class DD1 : public D1  // a class derived from the a derived class
    {
      public:
        void f(void);
        virtual void v(void); 
    };
    
    class D4 : public D3 // now make non virtual so we can get instance (object)
    {
      public:
        void v(void);
    };
    
    int main(void)
    {
      Base      *bp;    // no object but a pointer for each  pure virtual class
      D1   d1,  *d1p;
      D2   d2,  *d2p;
      D3        *d3p;   // no object because v() not defined
      DD1  dd1, *dd1p;
      D4   d4;          // now v() defined and can get object
    
      // b.f();     // no longer can get an object for the pure virtual Base class
      // b.v();
      // bp=&b;     // can get pointer, but can not use until later class instance
      // bp->f();   
      // bp->v();   
    
      d1.f();    //        D1::f()
      d1.v();    //        D1::v()
      d1p=&d1;
      d1p->f();  //        D1::f()
      d1p->v();  //        D1::v()     no difference with virtual yet
      bp=&d1;    //        now, make a pointer to the base type, point to an
                 //        object of a derived type of the base type
      bp->f();   //        Base::f() choose function belonging to pointer type
      bp->v();   //        D1::v()   now difference with virtual!
                 //                  run time polymorphism
    
      d2.f();    //        Base::f()
      d2.v();    //        D2::v()
      d2p=&d2;
      d2p->f();  //        Base::f()
      d2p->v();  //        D2::v()     no difference with virtual yet
      bp=&d2;    //        now, make a pointer to the base type, point to an
                 //        object of a derived type of the base type
      bp->f();   //        Base::f() choose function belonging to pointer type
      bp->v();   //        D2::v()   now difference with virtual!
                 //                  run time polymorphism
    
      // d3.f();    //        D3::f() // not with pure virtual
      // d3.v();    //        D3::v()
      d3p=&d4;
      d3p->f();  //        D3::f()
      d3p->v();  //        D4::v()     only choice
      bp=&d4;    //        now, make a pointer to the base type, point to an
                 //        object of a derived type of the base type
      bp->f();   //        Base::f() choose function belonging to pointer type
      bp->v();   //        D4::v()     only choice
    
      dd1.f();   //        DD1::f()
      dd1.v();   //        DD1::v()
      dd1p=&dd1;
      dd1p->f(); //        DD1::f()
      dd1p->v(); //        DD1::v()     no difference with virtual yet
      bp=&dd1;   //        now, make a pointer to the base type, point to an
                 //        object of a derived derived type of the base type
      bp->f();   //        Base::f()  choose function belonging to pointer type
      bp->v();   //        DD1::v()   now difference with virtual!
                 //                   run time polymorphism
      return 0;
    }
    
    #include <iostream>
    using namespace std;
    
    void Base::f(void)             // code for all the function prototypes above
    {                              // each function just prints its full name
      cout << "Base::f()" << endl;
    }
    void Base::v(void)
    {
      cout << "Base::v()" << endl;
    }
    
    void D1::f(void)
    {
      cout << "D1::f()" << endl;
    }
    void D1::v(void)
    {
      cout << "D1::v()" << endl;
    }
    
    void D2::v(void)
    {
      cout << "D2::v()" << endl;
    }
    
    void D3::f(void)
    {
      cout << "D3::f()" << endl;
    }
    
    void DD1::f(void)
    {
      cout << "DD1::f()" << endl;
    }
    void DD1::v(void)
    {
      cout << "DD1::v()" << endl;
    }
    
    void D4::v(void)
    {
      cout << "D4::v()" << endl;
    }
    
    /* results of running above program:
    D1::f() 
    D1::v() 
    D1::f() 
    D1::v() 
    Base::f() 
    D1::v() 
    Base::f() 
    D2::v() 
    Base::f() 
    D2::v() 
    Base::f() 
    D2::v() 
    D3::f() 
    D4::v() 
    Base::f() 
    D4::v() 
    DD1::f() 
    DD1::v() 
    DD1::f() 
    DD1::v() 
    Base::f() 
    DD1::v() 
    */
    
    

    Lecture 10, Review for Quiz 1

     
       see homework assignment for details 
    
    

    Lecture 11, Quiz

     
       see homework assignment for details 
    
    

    Lecture 12, Friend, cout <<, static

     
    The key word 'friend' is used to allow visibility to the private
    part of a class. In the following example, class A1 declares that
    class B1 is a friend. Thus, allowing class B1 to have access to
    the private part of class A1.
    
    The 'friend' declaration is used most often when a pair of classes
    are being defined that have close connection and much interaction.
    
    // friends.cpp
    #include <iostream>
    using namespace std;
    
    class A1;  // incomplete type definition needed because B1 refers to A1
               // and A1 refers to B1
    
    class B1
    {
      public:
        B1(int pub, int pri){b1pub=pub; b1pri=pri;}
        void B1_out(A1 aa);
        int b1pub;
      private:
        int b1pri;
    };
    
    class A1
    {
      friend B1;  // says B1 can access this classes private part
      public:
        A1(int pub, int pri){a1pub=pub; a1pri=pri;}
        void A1_out(B1 bb);
        int a1pub;
      private:
        int a1pri;
    };
    
    void A1::A1_out(B1 bb)
    {
      cout << "A1_out " << a1pub << a1pri << bb.b1pub /* << bb.b1pri */ << '\n';
                               // B1 public OK  -----          ----- no private
    }
    
    void B1::B1_out(A1 aa)
    {
      cout << "B1_out " << b1pub << b1pri << aa.a1pub << aa.a1pri << '\n';
                        // both public and private OK because of 'friend'
    }
    
    int main(void)
    {
      A1 a(1,2);
      B1 b(3,4);
    
      cout << "friends.cpp \n";
    
      a.A1_out(b);
      b.B1_out(a);
    
      return 0;
    }
    
    // results: friends.cpp 
    //          A1_out 123
    //          B1_out 3412
    
    
    There is a special case when a operator needs to be defined for another
    class and variables in the private part of a class need to be used by
    that operator.
    
    The most common case is when the  cout  operator  <<  needs to be
    defined for a class. The operator is not a member function of the class
    being defined but need to access private variables.  Thus a special
    use of 'friend' allows only the operator to have access to the private
    part.
    
        friend ostream &operator << (ostream &stream, A_class x);
    
    says that the operator << defined for reference to class ostream
    is allowed visibility to A_class private section.
    
    In general, any C++ predefined operator can be given a definition
    for a class.  This example also shows the comparison operator <
    being defined.  Note the function return type 'bool' is typically
    used for comparison operators.  The first object to compare
    is this object, denoted by the key work 'this'. The second object
    used in the comparison is a reference to another object, 'y'.
    A simple comparison was shown here yet the class may define whatever
    makes sense for 'greater than' for the operator '<'.
    The precedence of operators can not be changed.
    
    
    A new form of constructor initialization is also demonstrated.
    The syntax  : variable(constant or argument) initializes
    the variable in the object to the constant or argument.  Actually
    any valid expression based on arguments and constants may be used
    for the initialization expression in the parenthesis.
    
    
    
    // cout_friend.cpp  define the cout operator  <<  for a class  
    //                  define operator  <  for a class
    //                  demonstrate constructor initialization  :var(arg)
    
    // a_class.h      should be in a file by itself
    
    #include <iostream>            // basic console input/output
    using namespace std;           // bring standard include files in scope
    
    class A_class
    {
      public:
        A_class(int Akey, double Aval): key(Akey), val(Aval) {}
        friend ostream &operator << (ostream &stream, A_class x);
        bool operator < (A_class &y) {return this->key < y.key;}
    
      private: // bug in VC++ means this has to be public in VC++ only
        int key;
        double val;
    };
          
    
    
    // #include "a_class.h"  // would be needed if lines above were in a_class.h
    
    int main()
    {
      A_class a(5,7.5);
      A_class b(6,2.4);
    
      cout << "a= " << a << "  b= " << b << endl; // using A_class  <<
    
      if( a < b)  // using  A_class  <
      {
        cout << "OK a < b  because 5 < 6"  << endl;
      }
      else
      {
        cout << "BAD compare on  a < b  fix operator <  definition" << endl;
      }
    
    
      return 0;
    } // end main
    
    // The lines below should be in a file  a_class.cpp  .cc  .C
    // #include "a_class.h"
    
    // The 'friend' statement in A_class makes key and val visible here
    
    ostream &operator<< (ostream &stream, A_class x)
    {
      stream << x.key << "  ";
      stream << " $" << x.val << " "; // just demonstrating added output
      return stream;
    }
    
    // output of execution is:
    // a= 5   $7.5   b= 6   $2.4 
    // OK a < b  because 5 < 6
    
    
    The next topic is the four uses of the key word 'static'.
    The following file static.cc is well commented to show the uses.
    
    Note the execution results that show the single location B1 is shared by
    all objects of class Base.
    
    
    // static.cc  test various cases  note //! is about 'static'  FOUR USES !!
    //            1  a static member function
    //            2  a static member variable (shared by all objects of this class
    //            3  local to this file
    //            4  static (not on stack) variable in function - holds value
    //               between calls to the function
    
    
    #include <iostream>
    using namespace std;
    
    static int funct(int stuff); //! local to this file, not seen by linker     3
    
    class Base
    {
      public:
        Base(void) { B2=2; B3=3;}
        static void BF1(void);   //! definition can not go here                 1
        void BF2(void) { cout << B1 << "  " << B2 << " BF2 in Base \n";
                         B2=5; }
      private:
        static int B1; //! no =1; here  (shared by all objects of this class)   2
        int B2;
        int B3;
    };
    
    int Base::B1 = 1; //! has to be at file scope. Not in 'main' or in class    2
                      //! only one per total program, not in .h file
    
    void Base::BF1(void) //! can not use word static in front of this
    {
      cout << B1 << " BF1 static in Base \n"; //! can not use B2 or B3 in here
      B1=4;
    }
    
    static int j=3;  //! means local to this file, not visible to linker        3
    static Base c;
    
    int main(void)
    {
      static int  i;   //! means not on stack, value saved from call to call   4
      Base a, b;
    
      a.BF2();
      a.BF1();     //! object can be used, any object, same result
      a.BF2();
      b.BF2();
      Base::BF1(); //! no object needed, but do need  Base:: syntax
      b.BF2();
      return funct(-2);  //! call to function, local to this file
    }
    
    static int funct(int stuff) //! local to this file, not seen by linker     3
    {
      static int i=4;           // hold value between calls to funct           4
      j=i;
      i++;
      return stuff+3;
    }
    
    //  results, note B1 is changed in object 'a' and B1 gets changed also in 'b'
    //          while B2 is changed in object 'a' and 'b' unaffected
    
    //  1  2 BF2 in Base                                B1==1, B2==2, B2=5 in 'a' 
    //  1 BF1 static in Base                            B1==1, B1=4        global
    //  4  5 BF2 in Base                                B1==4, B2==5, B2=5 in 'a'
    //  4  2 BF2 in Base                                B1==4, B2==2, B2=5 in 'b' 
    //  4 BF1 static in Base                            B1==4, B1=4        global
    //  4  5 BF2 in Base                                B1==4, B2==5, B2=5 in 'b'
    
    
    

    Lecture 13, Hierarchies, composition

     
      The English language terminology "is a"
      A is a B  would usually mean class A would inherit class B.
    
      Shown below: a vehicle is a transporter
                   a car is a vehicle
    
      The English language terminology "has a"
      A has a B  would usually mean class A is composed of one or more
      objects of type B.  This is called assembly, construction or
      composition in various books on object oriented programming.
    
      Shown below: a car has a engine
                   a car has a hood
                   a car has wheels
                   a car has doors
    
      
    
      It does not make sense to say: a car is a door or a door is a car.
      It does not make sense to inherit a door class twice to give
      a car two doors.
    
    
      An example is shown below:
    
    
      // composition.cc
      // we have covered inheritance, now we add composition
      // once students learn about inheritance, they tend to over use it.
      // it must be remembered that objects may be composed of other objects
      // composition has a different meaning and use than inheritance
    
      class door { // for a car door  each usually a header file, e.g. door.h
        public:
        private:
          float width, height, mass; // etc.
      };
    
      class engine { // for a car engine
        public:
        private:
          float horse_power, torque, mass; // etc.
      };
    
      class hood { // for a car hood
        public:
        private:
          float width, height, mass; // etc.
      };
    
      class wheel { // for a car wheel
        public:
        private:
          float diameter, width, mass; // etc.
      };
    
      class transporter { // for a physical transporter
        public:
          float get_mass(transporter object);
        private:
          float mass;
      };               // should be abstract
    
      // #include "transporter"  // all one file, so #include  commented out
      class vehicle : public transporter { // for a physical vehicle
                                           // inherits "is a" transporter
        public:
        private:
          float new_variable; // and more
      } vehicle_1, vehicle_2;              // can have objects
    
      //#include "vehicle.h"       // all in this file
      //#include "door.h"
      //#include "engine.h"
      //#include "hood.h"
      //#include "wheel.h"
      class car : public vehicle { // for physical car
                                   // inherits "is a" vehicle
        public:
        private:
          door left_door;              // composition, car "has a" door
          door right_door;
          engine v8;                   // composition, car "has a" engine
          hood my_hood;                // composition, car "has a" hood
          wheel lf,lr,rf,rr;           // composition, car has wheels
                // multiple inheritance will not work to get four wheels!
          // vehicle a_vehicle; WRONG!  by inheritance, car "is a" vehicle
      };
    
      int main()
      {
        car c1, c2; // c1 and c2 are two objects, instances of class car
      } 
    
    
    
    

    Lecture 14, Templates

     
      Templates allow the developer to leave one or more types to be
      defined by the user of the template. 
    
      Functions take values as parameters,
      but templates take types as parameters.
    
      The first example shows the syntax and provides many comments
      about the definition and use of a template of a class:
    
    // test1_template.cc    basic test of templates
    
    #include <iostream>
    #include <string>              // this is definitely NOT  !!
    using namespace std;
    
    template <class T> class foo;  // a declaration that foo is a template class
    
    template <class T>             // declaration or specification of foo
    class foo                      // the user gets to choose a type for 'T'
    {
      public:
        foo();
        void put(T value);
        T get(int &j);
      private:
        int mine(T &a_value);
        T   a;
        int i;
    };                             // remember the ending semicolon
    
    template <class T>             // implementation or body of foo
    foo<T>::foo()
    {
      i = 1;
      // can not initialize 'a' here because we do not know what type 'T' is
    }
    
    template <class T>
    void foo<T>:: put(T value)
    {
      a = value;  // since 'a' and 'value' are both type 'T' this is OK
    }
    
    template <class T>
    T foo<T>::get(int &j)
    {
      j = ++i;   // 'j' was passed by reference and gets incremented 'i'
      return a;  // return 'a' whatever type it is
    }
    
    int main(int argc, char *argv[])
    {
      foo<double> x; // in object 'x', the type 'T' is double
      double y;
      int j;
    
      x.put(1.5);    // put the value 1.5 into 'a' in 'x'
      y = x.get(j);  // increment 'i' in 'x' and return 'a'
    
      cout << "j= " << j << "  y= " << y << endl;
    
      foo<string> glob; // in object 'glob' , the type 'T' is string::string
      string y_string;
    
      glob.put("abc");  // put the string "abc" int 'a' in 'glob'
      y_string = glob.get(j); // increment 'i' in 'glob' and return 'a'
    
      cout << "j= " << j << "  y_string= " << y_string << endl;
      return 0;
    }
    
    
    Now, we define a template function where the type will be chosen by the user.
    The test case uses numeric types because + - * / have to be defined for
    the type 'typ'
    
    Notice the difference in using a template function, there is no < >
    needed because the types of the function parameters select (or determine)
    the type the template will be instantiated with.
    
    Note the differences in the results for the different user types:
    
    // test_template.cpp   define and use a function template
    
    // define a template 
    
    template <class typ >             // user chooses a type for 'typ'
    typ funct( typ x, typ y, typ z)   // the function takes 3 parameters
    {
       typ a, b;
       a = x + y;                     // our template limits the users
       b = y - z;                     // choice for 'typ' to a type
       return a * a / b;              // that has +, -, *, and / defined
    }
    
    #include <iostream>               // 'typ' must also have operator << defined
    using namespace std;
    
    int main()
    {
      int i = funct(3, 5, 7);              // integer for typ
      long int j = funct(3L, 5L, 7L);      // long  int for typ
      unsigned int k = funct(3U, 5U, 7U);  // unsigned int for typ
      float x = funct(3.1F, 5.2F, 7.3F);   // float for typ
      double y = funct(3.1, 5.2, 7.3);     // double for typ
      cout << i << " int, "
           << j << " long, "
           << k << " unsigned, "
           << x << " float, "
           << y << " double.\n";
      return 0;
    } // end main
    
    // output is
    // -32 int, -32 long, 0 unsigned, -32.8047 float, -32.8048 double.
    // note the very different answer for the unsigned type
    // remember a small negative number becomes a very large positive number
    
    
    Now a more complicated template that takes two types are parameters.
    This example has a no parameter constructor for the template class and
    a two parameter constructor. The comments indicate which is being used.
    
    // template_class.cc  a template that needs two classes to instantiate
    
    // this should be in a file  my_class.h
    
    template $lt;class C1, class C2>  // user needs two class types for 'C1' and 'C2'
    class My_class
    {
      public:
        My_class(void){}
        My_class(C1 Adata1, C2 Adata2) { data1 = Adata1; /*data2 = Adata2;*/}
        // other functions using types C1 and C2
        // using statements such as below put requirements on C1 and C2
        int Get_mode(void)      { return data1.i;    }
        int Get_mode(C2 Adata3) { return Adata3.mode; } 
        // ... 
      protected:
        C1 data1;
        // C2 data2(7); // initialization not allowed here
        // ...
    };
    
    
    // in users main program file
    // #include <my_class.h>
    
    class A_class    // users class that will be OK for template type 'C1'
    {
      public:
        A_class(void) { i = 3; }
        int i;
    };
    
    class B_class   // users class that will be OK for template type 'C2'
    {
      public:
        B_class(int Amode) { mode = Amode; stuff = 0.0; }
        int mode;
      private:
        double stuff;
    };
    
    
    #include <iostream>
    using namespace std;
    
    int main(void)
    {
      A_class AA;       // we need an object of type A_class
      B_class B1(7);    // we need an object of type B_class
      B_class B2(15);   // another object
     
      My_class<A_class, B_class> stuff;  // 'stuff' is the instantiated template
    
      cout << "stuff.Get_mode(B1) " << stuff.Get_mode(B1) << endl;
    
      // now instantiate the template again to get the object 'things'
    
      My_class<A_class, B_class> things(AA, B2); // using the constructor to
                                                 // pass objects 'AA' and 'B2"
    
      cout << "things.Get_mode() " << things.Get_mode() << endl;
    
      return 0;
    }
    // output from execution is:
    // stuff.Get_mode(B1) 7
    // things.Get_mode() 3
    
    

    Lecture 15, STL <vector>

     
     test_vector.cpp 
    
     test_vector.out 
    
    

    Lecture 16, STL <algorithm>

     
    A demonstration of using some functions from STL  algorithm
    
    (Note: These are template functions and not template classes
           that define data structures. Thus, the user must create
           data objects for the algorithms to work on.)
    
       test_algorithm1.cc 
    
    Special case that works in VC++  (user template function)
    
       test_algorithms.cpp 
    
    Modified case that works in g++
    
       test_algorithms.cc 
    
    

    Lecture 17, STL <string>

     
    Unfortunately three compilers differ on handling the type 'string'
    
    This is one of the most complex STL header files.
    The typedef for string had to instantiate basic_string<...>
    which in turn has to instantiate  char_traits<...>.
    
    Then there are template functions that must be instantiated.
    
    Visual C++ works most like the standard but may still have bugs:
    
      test_string.cpp 
    
    g++ may be doing 'swap' correctly but does not have all the 'compare'
    
      test_string.cc 
    
    On UMBC node 'retriever'
    SGI CC outputs a bunch of ^E^E^E ...  and a bunch of line feeds on
    some commands.  .capacity() did not work, possibly a problem with
    header files. Seems much better on node 'gl'.
    
      test_string.C 
    
    Be sure you test whatever compiler you use.
    It is better to not use a feature that is non standard.
    Work around problems with as simple code as possible.
    
    Now, consider that the type 'string' is a STL container.
    Thus, many algorithms from 'algorithm' will work, for example:
    
      test_string2.cc 
    
    This works in both g++ and CC on gl machines.
    

    Lecture 18, STL <list> and <set>

     
      Sample programs:   test_list.cpp 
                         test_set.cpp 
                        
    
    

    Lecture 19, STL <priority_queue> and file IO

     
       test_priority_queue.cpp 
       file io example 
    
    

    Lecture 20, Project Description

     
       see project description 
    
    

    Lecture 21, Review for Quiz 2

     
      Go over HW5 - HW8  (fix it if you did not get it right)
    
      For the STL library <vector> <algorithm> <string>
                          <list>   <set>
    
      Know how to instantiate the template to get an object.
      Know how to get initial values into the object.
      Know how to add more values to the object.
      Know how to delete or remove or erase values from the object.
      Know how to print the object.
      Know how to look up member functions and algorithm template functions
      to be able to call the functions.
      
       test_print_template.cpp 
      shows how to instantiate, initialize and print various STL templates.
    
    // test_print_template.cpp   a general template function to print a STL container
    
    #include <iostream>
    #include <algorithm>
    #include <functional>
    #include <string>
    #include <vector>
    #include <list>
    #include <set>
    #include <queue>
    #include <stack>
    using namespace std;
    
    // a template for a general STL container print function
    
    template <class forward_iterator>
    void print(forward_iterator first, forward_iterator last, const char* title)
    {
      cout << title << endl;
      while (first != last)  cout << *first++ << "  ";
      cout << endl << endl;
    }
    
    int main()
    {
      int i;
      int n; // size of container
      int data[4] = {3, 7, 5, 4};
      print(data, data+4, "normal integer array");
      
      vector<int> v1(4);
      v1[0] = 3;
      v1[1] = 7;
      v1[2] = 5;
      v1[3] = 4;
      print(v1.begin(), v1.end(), "vector<int> data");
      
      list<string> l1;
      l1.push_back("3");
      l1.push_back("7");
      l1.push_back("5");
      l1.push_back("4");
      print(l1.begin(), l1.end(), "list<string> data");
      
      set<int> s1;
      s1.insert(3);
      s1.insert(7);
      s1.insert(5);
      s1.insert(4);
      print(s1.begin(), s1.end(), "set<int> data - note sorted");
    
      multiset<int> ms;
      ms.insert(3);
      ms.insert(7);
      ms.insert(5);
      ms.insert(4);
      print(ms.begin(), ms.end(), "multiset<int> data - note sorted");
      
      queue<int> q1;
      q1.push(3);
      q1.push(7);
      q1.push(5);
      q1.push(4);
      // print(q1.begin(), q1.end(), "queue<int> data"); // won't work
      cout << "special print loop for queue<int>, destroys queue" << endl;
      n = q1.size(); // can not be in 'for' statement
      for(i=0; i<n; i++)  {cout << q1.front() << "  "; q1.pop();}
      cout << endl << endl;
      
      priority_queue<int> pq;
      pq.push(4);
      pq.push(5);
      pq.push(7);
      pq.push(3);
      // print(pq.begin(), pq.end(), "priority_queue<int> data"); // won't work
      cout << "special print loop for priority_queue<int> sorted, destroys priority_queue" << endl;
      n = pq.size(); // can not be in 'for' statement
      for(i=0; i<n; i++)  {cout << pq.top() << "  "; pq.pop();}
      cout << endl << endl;
      
      deque<int> dq;
      dq.push_front(4);
      dq.push_front(5);
      dq.push_front(7);
      dq.push_front(3);
      // print(dq.begin(), dq.end(), "deque<int> data"); // won't work
      cout << "special print loop for deque<int>, destroys deque" << endl;
      n = dq.size(); // can not be in 'for' statement
      for(i=0; i<n; i++)  {cout << dq.front() << "  "; dq.pop_front();}
      cout << endl << endl;
      
      stack<int> st;
      st.push(4);
      st.push(5);
      st.push(7);
      st.push(3);
      // print(st.begin(), st.end(), "stack<int> data"); won't work
      cout << "special print loop for stack<int>, destroys stack" << endl;
      n = st.size(); // can not be in 'for' statement
      for(i=0; i<n; i++)  {cout << st.top() << "  "; st.pop();}
      cout << endl << endl;
      
      return 0;
    }
    
    // output from execution is:
    // normal integer array
    // 3  7  5  4  
    //
    // vector<int> data
    // 3  7  5  4  
    // 
    // list<string> data
    // 3  7  5  4  
    // 
    // set<int> data - note sorted
    // 3  4  5  7  
    // 
    // multiset<int> data - note sorted
    // 3  4  5  7  
    // 
    // special print loop for queue<int>, destroys queue
    // 3  7  5  4  
    // 
    // special print loop for priority_queue<int> sorted, destroys priority_queue
    // 7  5  4  3  
    // 
    // special print loop for deque<int>, destroys deque
    // 3  7  5  4  
    // 
    // special print loop for stack<int>, destroys stack
    // 3  7  5  4  
    //
    
    

    Lecture 22, Quiz 2

     
       see homework assignment page for details 
    
    

    Lecture 23, Understand 'const' and parameters

     
    All about const in declaring types of pointers:
    Note what works, what is required and what is not allowed:
    
    // test_const.cpp     pointer and value  constant and non constant
    
    int main()
    {
       int i1 = 1;
       int i2 = 2;
       int i3 = 3;
       int i5 = 5;
       const int ic = 9;                 // variable 'ic' unchangeable
                                         // naming is pointer_object
                                         // con for constant
                                         // var for variable (not const)
                                         // four cases are possible:
       int * var_var;
       int * const con_var = &i1;        // requires initialization
       const int * var_con;
       const int * const con_con = &i5;  // requires initialization
    
    //   ^           ^-- refers to the pointer
    //   L__ refers to the value (dereferenced pointer)
    
       var_var = &i2;
       // con_var = &i2;                 // not allowed
       var_con = &i3;
       // con_con = &i2;                 // not allowed
       
       *var_var = 7;
       *con_var = 8;
       // *var_con = 7;                  // not allowed
       // *con_con = 7;                  // not allowed
    
       // var_var = ⁣                 // not allowed
       // con_var = ⁣                 // not allowed
       var_con = ⁣
       // con_con = ⁣                 // not allowed
       
       return 0;
    }
    

    Old style C is considered BAD style in C++

    /* test_swap.c             older style of test_swap.cpp */
    
    void swap(int *v1, int *v2)         /* pass by pointer */
    {
      int tmp = *v2;
      *v2 = *v1;
      *v1 = tmp;
    } /* end swap */
    
    
    #include <stdio.h>
    int main()                     /* C version of test swap */
    {
      int i=10;
      int j=20;
      printf("before swap  i= %d  j= %d \n", i, j);
      
      swap(&i,&j);         /* note users call requires & */
      
      printf("after  swap  i= %d  j= %d \n", i, j);
      return 0;
    } /* end main */
    

    C++ style uses pass by reference rather than pass by pointer

    // test_swap.cpp   demonstrate pass parameter by reference         
    
    void swap(int &v1, int &v2)   // pass parameter by reference
    {
      int tmp = v2;
      v2 = v1;
      v1 = tmp;
    } // end swap
    
    
    #include <iostream>
    using namespace std;
    
    int main()                            // C++ test swap
    {
      int i=10;
      int j=20;
      cout << "before swap  i= " << i << "  j= " << j << endl;
      
      swap(i,j);   //  note:  just use object names, no '&'
      
      cout << "after  swap  i= " << i << "  j= " << j << endl;
      
      return 0;
    } // end main
    

    Old C style to swap char * strings, not good C++

    /* test_swap_str.c           older style of test_swap_str.cpp */
    
    void swap(char**v1, char**v2)            /* pass by pointer */
    {                             /* note has to be pointer to pointer */
      char* tmp = *v2;            /* in order to swap pointers */
      *v2 = *v1;
      *v1 = tmp;
    } /* end swap */
    
    
    #include <stdio.h>
    int main()                 /* C version of test swap str */
    {
      char* i="abcdef";
      char* j="ghi";
      printf("before swap  i= %s  j= %s \n", i, j);
      
      swap(&i, &j);           /* note users call needs & */
      
      printf("after  swap  i= %s  j= %s \n", i, j);
      
      return 0;
    } /* end main */
    
    

    C++ style swap for char * strings using pass by reference

    // test_swap_str.cpp   demonstrate pass parameter by reference         
    
    void swap(char* &v1, char* &v2)
    {
      char* tmp = v2;
      v2 = v1;
      v1 = tmp;
    } // end swap
    
    
    #include <iostream>
    using namespace std;
    
    int main()                            // C++ test swap
    {
      char* i="abcdef";
      char* j="ghi";
    
      cout << "before swap  i= " << i << "  j= " << j << endl;
      
      swap(i,j);
      
      cout << "after  swap  i= " << i << "  j= " << j << endl;
      
      return 0;
    } // end main
    
    

    Now, the universal swap, as a C++ template function

    // test_swap_template.cpp   demonstrate pass parameter by reference         
    
    template <class some_type>
    void swap_any(some_type &v1, some_type &v2)
    {
      some_type tmp = v2;
      v2 = v1;
      v1 = tmp;
    } // end swap_any
    
    
    #include <iostream>
    #include <string>
    using namespace std;
    
    int main()                            // C++ test swap
    {
      int i=10;
      int j=20;
      cout << "before swap  i= " << i << "  j= " << j << endl;
      swap_any(i, j);
      cout << "after  swap  i= " << i << "  j= " << j << endl;
      
      string s10("string 10");
      string s20("string 20, longer OK");
      cout << "before swap  s10= " << s10 << "  s20= "  << s20 << endl;
      swap_any(s10, s20);
      cout << "after  swap  s10= " << s10 << "  s20= "  << s20 << endl;
      
      return 0;
    } // end main
    
    Then, for using the various cases of 'const' in parameter passing, see
     test_parameters.cpp 
    
    As a side issue, here is a C++ program that uses a dynamic matrix
    built on the vector template class, that needs no pointers, no
    malloc or new, and can use conventional matrix subscripting.
    
    // test_matrix.cpp  dynamic matrix,  just vector of vectors
    
    #include <vector>
    #include <iostream>
    using namespace std;
    
    int main()
    {
      typedef vector<double> d_vec;
      typedef vector<d_vec>  d_mat;
    
      d_mat mat; // dynamic size matrix, initially  0 by 0
      d_vec row;
    
      cout << "test_matrix.cpp" << endl;
    
      row.push_back(1.0);
      row.push_back(2.0);
      row.push_back(3.0);  // build the first row
      mat.push_back(row);
      row[0]=4.0;
      mat.push_back(row);
      row[1]=5.0;
      mat.push_back(row); // now have 3 by 3  1.0  2.0  3.0
                          //                  4.0  2.0  3.0
                          //                  4.0  5.0  3.0
    
      cout << "mat[2][2]=" << mat[2][2] << endl;
      mat[1][1]=9.0;
    
      mat[0].push_back(4.0);
      mat[1].push_back(6.0);
      mat[2].push_back(7.0);
      row.push_back(8.0);
      mat.push_back(row); // now have 4 by 4  1.0  2.0  3.0  4.0
                          //                  4.0  9.0  3.0  6.0
                          //                  4.0  5.0  3.0  7.0
                          //                  4.0  5.0  3.0  8.0
    
      cout << "mat[2][3]=" << mat[2][3] << endl;
      mat[2][3]=7.0001;
    
      // obviously do not use  mat[4][4], it does not exists
    
      for(int i=0; i<mat.size(); i++)
        for(int j=0; j<mat[i].size(); j++)
          cout << "mat[" << i << "][" << j << "]= " << mat[i][j] << endl;
    
      return 0;
    }
    
    

    Lecture 24, Recursive objects and eight queens

     
    Sometimes it is convenient to have a member function call itself. This is
    called a recursive member function.  There must be a way for the
    function to return without calling itself or else there is an infinite loop.
    
    First, look a the simplest recursive function, n! called n factorial.
      0! is defined as 1
      1! is defined as 1 * 0!  = 1
      2! is defined as 2 * 1!  = 2
      3! is defined as 3 * 2!  = 6
    
    Note that the definition is 'recursive' because n! is defined as n * (n-1)!
    Each bigger factorial is defined using the next smaller factorial.
    
    Because n! grows fast with increasing n, integer overflow will occur.
    Where the overflow (a number bigger than a C++ type can hold) occurs
    depends on what computer and possibly what compiler you are using.
    
    Beware! Overflow is not easily detected as can be seen in the output below.
    
    Note how the function 'factorial' is coded from the definition of factorial.
    
    // test_factorial.cpp   the simplest example of a recursive function
    //                      a recursive function is a function that calls itself
    
    static int factorial(int n)  // n! is n factorial = 1*2*3*...*(n-1)*n
    {
      if( n <= 1 ) return 1;     // must have a way to stop recursion
      return n * factorial(n-1); // factorial calls factorial with n-1
    }                            // n * (n-1) * (n-2) * ... * (1)
    
    #include <iostream>
    using namespace std;
    
    int main()
    {
      cout << " 0!=" << factorial(0)  << endl; // Yes, 0! is one
      cout << " 1!=" << factorial(1)  << endl;
      cout << " 2!=" << factorial(2)  << endl;
      cout << " 3!=" << factorial(3)  << endl;
      cout << " 4!=" << factorial(4)  << endl;
      cout << " 5!=" << factorial(5)  << endl;
      cout << " 6!=" << factorial(6)  << endl;
      cout << " 7!=" << factorial(7)  << endl;
      cout << " 8!=" << factorial(8)  << endl;
      cout << " 9!=" << factorial(9)  << endl;
      cout << "10!=" << factorial(10) << endl;
      cout << "11!=" << factorial(11) << endl;
      cout << "12!=" << factorial(12) << endl;
      cout << "13!=" << factorial(13) << endl;
      cout << "14!=" << factorial(14) << endl;
      cout << "15!=" << factorial(15) << endl; // expect a problem with
      cout << "16!=" << factorial(16) << endl; // integer overflow
      cout << "17!=" << factorial(17) << endl; // uncaught in C++  (Bad!)
      cout << "18!=" << factorial(18) << endl;
    
      return 0;
    }
    
    // output of execution is:
    //  0!=1
    //  1!=1
    //  2!=2
    //  3!=6
    //  4!=24
    //  5!=120
    //  6!=720
    //  7!=5040
    //  8!=40320
    //  9!=362880
    // 10!=3628800
    // 11!=39916800
    // 12!=479001600
    // 13!=1932053504  // wrong! 13! = 13 * 12!, must end in two zeros
    // 14!=1278945280  // wrong! and no indication!
    // 15!=2004310016  // wrong!
    // 16!=2004189184  // wrong!
    // 17!=-288522240  // wrong and obvious if you check your results
    // 18!=-898433024  // Only sometimes does integer overflow go negative
    
    
    Now, look at a program to solve the eight queens problem.
    The problem is defined as having an 8 by 8 chess board and you must
    find a way to place eight queens on the chess board such that no
    queen can capture any other queen.  Queens can capture horizontally,
    vertically and diagonally.  Rather obviously, no two queens can be
    in the same column or same row.
    
    The data structure is based on an object, a queen, and a simple linked
    list of such objects.
    
    The execution model is to recursively traverse the linked list.
    The author of this program went a little overboard with recursion but
    it still serves as an example.  The student must be very careful
    in analyzing the program to keep tract of which object a function
    is working on during each recursive call.
    
    // eight_queens.cpp   demonstrate recursive use of objects
    // place eight queens on a chess board so they do not attack each other
    
    #include <iostream>
    using namespace std;
    
    class Queen     // in main()  LastQueen is head of the list 
    {               //            of queen objects.
      public:
        Queen(int C, Queen *Ngh);
        int First(void);
        void Print(void);
      private:
        int CanAttack(int R, int C);   // private member functions
        int TestOrAdvance(void);       // just used internally
        int Next(void);                // try next Row
    
        int   Row;                     // Row for this Queen
        int   Column;                  // Column for this Queen
        Queen *Neighbor;               // linked list of Queens
    };
    
    
    Queen::Queen(int C, Queen *Ngh)    // normal constructor
    {
      Column = C;                      // Column will not change
      Row = 0;                         // Row will be computed
      Neighbor = Ngh;                  // linked list of Queen objects
    }
    
    int Queen::First(void)
    {
      Row = 1;
      if (Neighbor && Neighbor -> First())  // order is important
      {                                     // note recursion
        return TestOrAdvance();
      }
      return 1;
    }
    
    void Queen::Print(void)
    {
      if(Neighbor)
      {
        Neighbor -> Print();   // recursively print data inside all Queens
      }
      cout << "column " << Column << "  row " << Row << endl;
    }
    
    int Queen::TestOrAdvance(void)
    {
      if(Neighbor && Neighbor -> CanAttack(Row, Column))
      {
        return Next();   // the member function 'next' does the 'advance'
      }
      return 1;
    }
    
    int Queen::CanAttack(int R, int C) // determine if this queen attacked
    {
      int Cd;
      if (Row == R) return 1;      // a set of rules that detect in same
      Cd = C - Column;             // row or column or diagonal
      if((Row+Cd == R) || (Row-Cd == R)) return 1;
      if(Neighbor)
      {
        return Neighbor -> CanAttack(R, C);  // recursive
      }
      return 0;
    }
    
    int Queen::Next(void)   // advance Row but protect against bugs
    {
      if (Row == 8)
      {
        if (!(Neighbor && Neighbor->Next()))
          return 0;
        Row = 0;
      }
      Row = Row + 1;
      return TestOrAdvance();
    }
    
    int main()
    {
      Queen *LastQueen = 0;
    
      for ( int i=1; i <= 8; i++)             // initialize
      {
        LastQueen = new Queen(i, LastQueen);
      }
    
      if (LastQueen -> First())               // solve problem
      {
        LastQueen -> Print();                 // print solution
      }
      return 0;                               // finished
    }
    
    Output of execution of eight_queens
    
    column 1  row 1
    column 2  row 5
    column 3  row 8
    column 4  row 6
    column 5  row 3
    column 6  row 7
    column 7  row 2
    column 8  row 4
    
    

    Safe Class Design

    When a class has only objects in it, that is no pointers that
    are set by the 'new' operator, a class is reasonably safe.
    
    But, when using the 'new' operator in a class:
    
    Use all three of these C++ constructs to build a safe class:
    
    Use the keyword 'explicit' on all constructors to prevent use of "="
    
    Use a 'copy constructor' that really makes new space and copies an
    existing object into the new space.
    
    Use 'operator=', in other words, code your own '=' operator for a = b;
    This uses the same type of code as the copy constructor with the
    addition of a statement   'return *this'
    
    The links show  test_safe1.cpp  a bad example.
    Then            test_safe2.cpp  safe but ugly.
    Finally         test_safev.cpp  neat using STL.
    
    Note: The STL version provides a dynamic vector, yet it is an object,
    thus the 'explicit', copy constructor, and operator= are not needed.
    
    The above set works with Microsoft Visual C++, below with g++
    
    The links show  test_safe1.cc  a bad example.
    Then            test_safe2.cc  safe but ugly.
    Finally         test_safev.cc  neat using STL.
    
    Note: The STL version provides a dynamic vector, yet it is an object,
    thus the 'explicit', copy constructor, and operator= are not needed.
    
    

    Lecture 25, Mixing C and C++

     
    Suppose you want to make some C code so that it works in
    both C and C++.  Well, the technique is to use a specific
    form of a header file for your C code.  The commented
    example below shows the header file:
    
    /* c_header.h    header file that works for both C and C++ */
    
    /* note that this works in both C and C++ because:
              1)  only C style comments are used
              2)  name mangling is turned off using  extern "C" { ... }
                  in  #ifdef  __cplusplus   <-- a special name
              3)  good practice is used to be sure included just once
    */
    
    #ifndef C_HEADER_H_   /* to only include once */
    #define C_HEADER_H_   /* to only include once */
    
    #ifdef __cplusplus    /* make it C if in C++ compiler */
      extern "C" {        /* never get here in a C compiler */
    #endif
    
    #include <time.h>  /* other C header files */
    
    void some_f(float seconds); /* types and function prototypes */
                                /* keep strictly C, not C++      */
                                
    #ifdef __cplusplus  /* just closing } for C in C++ compiler  */
      }
    #endif
    
    #endif  /* C_HEADER_H_  to only include once */
    
    
    An example C program to be sure above header file works
    
    /* test_c_header.c */
    #include "c_header.h"
    int main()
    {
      return 0;
    }
    
    And, an example C++ program to be sure above header file works
    
    // test_c_header.cpp
    #include "c_header.h"
    int main()
    {
      return 0;
    }
    
    
    Now, demonstrate calling a C compiled function from a C++ compiled
    function and calling a C++ compiled function from a C compiled function.
    
    The C compiled function is compiled   gcc -c  call_c.c
    to produce an object file  call_c.o
    
    /* call_c.c    goes with  call_cc.cpp */
    /*             no main() in this file */
    
    char call_cc(int x, float y); // function prototype
                                  // function defined in C++
    
    #include <stdio.h>
    char call_c(int x, float y)   // simple C function
    {
      char c; /* x and y passed through */
      
      printf("in c: about to call a C++ function\n");
      c = call_cc(x, y);
      printf("in c: returned from call_cc with %c \n", c);
      printf("in c: x = %d, y = %g \n", x, y);
      return 'a';  // return something to C++
    }
    
    
    In order to call the above C function (or any C library) from C++
    the essential statement is to place the C related statements in
    a block    extern "C" {  }
    
    Any code or files in the block is treated as C code.
    The function names are not mangled as they are in C++.
    The calling and return code is for C rather than for C++.
    It turns out, you can use C++ statements inside the extern "C" { }
    but it is not recommended.
    
    Now, compile the main() and link in the  call_c.o  with the command
    
    g++ -o call_cc  call_cc.cpp  call_c.o
    
    // call_cc.cpp   calls a C program which calls back to this C++ program
    //               must be compiled with call_cc.c or at least linked with
    //               the object file from the C program
    
    extern "C" {                 // function prototype for a C function
      char call_c(int x, float y);
    }
    
    #include <iostream>
    using namespace std;
    
    int main()
    {
      char c;
      int x=5;
      float y = 1.5;
    
      cout << "in C++ about to call a c: function, call_c" << endl;
      c = call_c(x, y);
      cout << "in C++: returned from call_c with " << c << endl;
      cout << "in C++: x = " << x << "  y = " << y << endl;
      return 0;
    }
    
    extern "C" {                // a C++ function callable by a C function
    #include <stdio.h>          // probably should use C++ rather than C I/O
    
    char call_cc(int x, float y)
    {
      try     // can use C++ in C callable function
      {       // but must load C++ and C libraries
        cout << "in C++ extern \"C\" test that C++ allowed" << endl;
      } catch(...) {printf("some exception thrown in C++\n");}
    
      printf("in C++ called from c: x = %d, y = %g \n", x, y);
      return 'q';
    }
    }
    
    // output of execution is:
    // in C++ about to call a c: function, call_c
    // in c: about to call a C++ function
    // in C++ extern "C" test that C++ allowed
    // in C++ called from c: x = 5, y = 1.5 
    // in c: returned from call_cc with q 
    // in c: x = 5, y = 1.5 
    // in C++: returned from call_c with a
    // in C++: x = 5  y = 1.5
    
    The above program demonstrated a main C++ function
    calling a C function 'call_c'
    then the C function 'call_c' called a function 'call_cc' compiled in C++
    yet having  extern "C" { }
    
    The C++ compiled function returned to the C function which
    finally returned to main().
    
    One additional point:
    A class member function can only be called from a C function 
    when the class member function is declared "static" 
    See static.cc
    
    The X Windows library, for example, can be used with a C++ program,
    keeping in mind that the X Windows call-backs must be to C++ code
    enclosed in  extern "C" { }  blocks. (And be static member functions
    if the call-back is to a class member function.)
    
    

    Lecture 26, STL <complex> and extensions

     
    The STL <complex> defines a template class complex that provides
    complex arithmetic and a few complex math functions.
    
    A sample use is test_complex.cpp
    
    A simple way to extend a standard C++ library class is to just define
    functions that work on that classes type.
    An extension is defined in complexf.h and has
    the corresponding code in complexf.cpp
    
    A test of the fuller definition of the complex<double> class is
    shown in test_complexf.cpp which
    uses the  "complexf.h"  header file.
    
    Compile with   g++ -o test_complexf test_complexf.cpp complexf.cpp
    Execute        test_complexf
    
    A more general extension would provide template functions rather than
    functions for a specific type.
    
    An example of "operator" functions that can not be member functions
    using a non template, very small, implementation of a complex class:
    
    // my_complex.cc  a partial, non template, complex class
    //                demonstrate some operator+ must be non-member functions
    //                operator<<  and operator >> must be non-member functions
    
    #include <iostream>
    using namespace std;
    
    class my_complex
    {
      public:
      my_complex() { re=0.0; im=0.0; }  // three typical constructors
        my_complex(double x) { re=x; im=0.0; }
        my_complex(double x, double y):re(x),im(y) {} // { re=x; im=y; }
        my_complex operator+(my_complex & z) // for  c = a + b
          { return my_complex(re+z.real(), im+z.imag()); }
        my_complex operator+(double x)       // for  c = a + 3.0
          { return my_complex(re+x, im); }
                            // many other operators would typically be defined
        double real(void) { return re; }
        double imag(void) { return im; } 
        friend ostream &operator<< (ostream &stream, my_complex &z);
      private:
        double re; // the data values for this class
        double im;
    };
    
    // non member functions
    my_complex operator+(double x, my_complex z) // for c = 3.0 + a
    {
      return z+x;
    }
    
    ostream &operator<< (ostream &stream, my_complex &z)
    {
      stream << "(" << z.real() << "," << z.imag() << ")";
      return stream;
    }
    
    int main()
    {
      my_complex a(2.0, 3.0);              cout << a << " =a\n";
      my_complex b(4.0);                   cout << b << " =b\n";
      my_complex c = my_complex(5.0, 6.0); cout << c << " =c\n";
      my_complex d;                        cout << d << " =d\n";
    
      c = a + c;                           cout << c << " c=a+c\n";
    
      c = 3.0 + a; // not legal without non member function operator+
                                           cout << c << " c=3.0+a\n";
      c = a + 3.0; // not legal without second operator+ on double
                                           cout << c << " c=a+3.0\n";
      c = a + 3;   // not legal without second operator+ on double
                   // uses standard "C" C++ conversion int -> float -> double
                                           cout << c << " c=a+3\n";
      d = c.imag();                        cout << d << " d=c.imag()\n";
      return 0;
    }
    // Output from running my_complex.cc
    // (2,3) =a
    // (4,0) =b
    // (5,6) =c
    // (0,0) =d
    // (7,9) c=a+c
    // (5,3) c=3.0+a
    // (5,3) c=a+3.0
    // (5,3) c=a+3
    // (3,0) d=c.imag()
    
    

    Lecture 27, Object Oriented Draw Program Design

     
    We will use a very crude screen plot output to show
    physically how objects can be created and manipulated.
    
    The very crude drawing is output by a very old C program,
    header file vt100_plot.h and
    code file   vt100_plot.c.
    We added the #ifdef __cplusplus to the C header file 
    per the previous lecture in order to use the old C code
    with C and with object oriented C++.
    
    For demonstration purposes, all the C++ header files and
    code files are shown in one physical file.
    Each file name is shown as comments and should be a
    separate file. (Uncommenting the  //#include  lines.)
    
    Note the public, protected and private parts of class Shape.
    Note how classes Circle and Rectangle inherit class Shape.
    
    A sample  main()  shows just a few usage examples.
    
    The program may be compiled and executed using:
       gcc -c vt100_plot.c
       g++ -o test_shape3  test_shape3.cpp  vt100_plot.o
       test_shape3
    
    // test_shape3.cpp  all in one file, should really be split up
    
    #include "vt100_plot.h"
    // shape3.h
    
    struct Point{float X; float Y;};
    
    class Shape        // modified for shape made up of lists of shapes
    {
      public:
        Shape();       // constructor
        ~Shape();      // destructor, mainly deletes linked list
        void SetCenter(Point ACenter);
        Point Center();
        void Move(float dx, float dy); // may also want  MoveTo(Point XY)
        void AddComponent(Shape *Ashape);
        void AddSibling(Shape *Ashape);
        virtual void Draw();
      protected:
        Point TheCenter;
      private:
        Shape *list_of_component_shapes;
        Shape *list_of_sibling_shapes;
    };
    
    // shape3.cpp
    //#include "shape3.h"
    Shape::Shape()  // body for constructor, default initialization
    {
      TheCenter.X = 0; // make it legal the first time, keep it legal
      TheCenter.Y = 0;
      list_of_component_shapes = 0;
      list_of_sibling_shapes = 0;
    }
    Shape::~Shape()  // body for destructor
    {
      // could use  'delete'  in here on  list_of_component_shapes
      //                              and list_of_sibling_shapes
    }
    
    void Shape::SetCenter(Point ACenter) // body for function SetCemter
    {
      TheCenter = ACenter;
    }
    
    Point Shape::Center()
    {
      return TheCenter;
    }
    
    void Shape::Move(float dx, float dy)
    {
      TheCenter.X += dx;
      TheCenter.Y += dy;
    }
    
    void Shape::AddComponent(Shape *Ashape)
    {
      Ashape->list_of_component_shapes=list_of_component_shapes;
      list_of_component_shapes=Ashape;
    }
    
    void Shape::AddSibling(Shape *Ashape)
    {
      Ashape->list_of_sibling_shapes=list_of_component_shapes;
      list_of_sibling_shapes=Ashape;
    }
    
    void Shape::Draw() // draws linked list of sub-shapes
    {
      Shape *local_list = list_of_component_shapes;
      while(local_list){
        local_list->Draw();
        local_list=local_list->list_of_component_shapes;
      }
    }
    
    // circle3.h
    //#include "shape3.h"
    class Circle : public Shape
    {
      public:
        Circle(float X, float Y, float R);
        void SetRadius(float Aradius);
        void Draw();
        float Radius();
      private:
        float TheRadius;
    };
    
    
    
    // circle3.cpp
    //#include "circle3.h"
    Circle::Circle(float X, float Y, float R)
    {
      Point p={X,Y};
      SetCenter(p);  
      TheRadius = R;
    }
    
    void Circle::SetRadius(float Aradius)
    {
      TheRadius = Aradius;
    }
    
    void Circle::Draw()
    {
      DRAW_CIRCLE(TheCenter.X, TheCenter.Y, TheRadius);
    }
    
    float Circle::Radius()
    {
      return TheRadius;
    }
    
    // rectangle3.h
    //#include "shape3.h"
    class Rectangle : public Shape
    {
      public:
        Rectangle(float X, float Y, float W, float H);
        void SetSize(float Awidth, float Aheight);
        void Draw();
        float Width();
        float Height();
      private:
        float TheWidth;
        float TheHeight;
    };
    
    
    
    // rectangle3.cpp
    //#include "rectangle3.h"
    Rectangle::Rectangle(float X, float Y, float W, float H)
    {
      TheCenter.X = X;
      TheCenter.Y = Y;
      TheWidth = W;
      TheHeight = H;
    }
    
    void Rectangle::SetSize(float Awidth, float Aheight)
    {
      TheWidth = Awidth;
      TheHeight = Aheight;
    }
    
    void Rectangle::Draw()
    {
      DRAW_RECTANGLE(TheCenter.X, TheCenter.Y, TheWidth, TheHeight);
    }
    
    float Rectangle::Height()
    {
      return TheHeight;
    }
    
    float Rectangle::Width()
    {
      return TheWidth;
    }
    
    // test_shape3.cpp
    //#include "circle3.h"
    //#include "rectangle3.h"
    
    int main()  // just a few tests
    {
      Circle a_circle(1.0F, 2.0F, 3.0F);
      Circle *circle_ptr = new Circle(7.0F, 0.0F, 3.0F);
      Point  a_point = {3,4};
      Rectangle a_rect(-8.0, 2.0, 4.0, 6.0);
      Shape  a_shape;
      
      INITIALIZE();
      a_circle.SetCenter(a_point);
      a_circle.SetRadius(2);
      a_circle.Draw();
      a_circle.Move(12.5, 5.0);
      a_circle.Draw();
      circle_ptr->Draw();
      a_rect.Draw();
      a_shape.SetCenter(a_point);
      a_shape.AddComponent(new Circle(-12,5,4));
      a_shape.AddComponent(new Rectangle(-18,-6,4,4));
      a_shape.Draw();
      PRINT();
      return 0;
    }
    
    
    
    The very crude output would look something like:
                                                         ****                      
                                                         *  *                      
                              *****                      *  *                      
                             **   **                     *  *                      
                            **     **                    ****                      
                            *       *        ***                                   
                            *     *****     ** **                                  
                            *     * * *     *   *                                  
                            **    *** *     ** ******                              
                             **   **  *      ****   **                             
                              *****   *        *     *                             
                                  *****        *     *                             
                                               **   **                             
                                                *****                              
                        *****                                                      
                        *   *                                                      
                        *   *                                                      
                        *   *                                                      
                        *****                                                      
                                                                                   
                                                                                   
                                                                                   
                                                                                   
    
    

    Lecture 28, Traffic Simulation Classes, vehicle.h etc.

     
    Object Oriented Design, analysis problem.
    
    Proposed problem: Develop a simulation of traffic flow that can be used
    to determine travel times under varying traffic conditions.
    
    Some requirements and desires:
    
    1) Have a graphical view of the traffic situation in order to help identify
       problems and possibly identify solutions. (X Windows on Unix, MS Windows
       in Visual C++)
    
    2) Have more than one type of vehicle, e.g. at least passenger car and truck
    
    3) Have intersections and traffic signals. The traffic signals need to have
       setable timers and sensors.
    
    4) Have the ability to insert vehicles into the area of study based on
       specific requirements or based on mean and standard deviation of time
       of entry. ( For each type of vehicle.)
    
    5) Have the ability to define the road pattern and intersection type.
       ( Intersections can be no traffic control, stop signs or traffic signal.)
    
    6) The vehicles must be able to have a goal or driving instructions (a route).
       The default goal is to drive at the speed limit whenever possible while
       obeying all traffic rules.
    
    
    Analysis of objects:
    
    1) It seems a class 'Vehicle' is needed. Initialization with characteristics
       like weight, length, maximum acceleration, maximum deceleration and
       route instructions would provide the ability to get all of the required
       objects. Route instructions could be a list of travel and turn commands
       like 'go N blocks, turn W' where N counts intersections and W is left or
       right.
    
    2) It seems a class 'Road' may be needed. A road object may have any number
       of vehicles on it, may have any number of intersections. A road object
       should be able to be initialized with a speed limit. A road object may
       have to accept vehicles from other road objects and hand off vehicles
       to other road objects. But, further thought indicates...
    
    3) A better building block may be a 'Lane'. A lane handles any number of
       vehicles going in one direction. A lane starts at an intersection ( the
       entrance) and stops at an intersection ( the exit). A lane has a length
       and a speed limit ( and possibly a road condition that can affect
       acceleration.) A lane has an X,Y grid coordinate for its entrance and
       exit.
    
    4) It seems a class 'Intersection' is needed to join lanes or roads. It may
       be that an intersection needs to be composed of smaller classes.
    
    5) A 'simulation' class or object is needed for general stuff outside any
       other classes.
    
    6) A display class or object or function or package is needed to provide
       for the graphical display.
    
    7) A setup program is needed to initialize the road system and connectivity.
       This program would control over all timing and sequencing
    
    
    Miscellaneous notes:
    
       Vehicles will follow a policy of staying at least 16 feet apart for
       each 10 feet per second of speed. Vehicles can accelerate at from
       1/32 G to 1/3 G depending on type. ( 1/3 G is about 11 feet per second
       per second, 0 to 88 feet per second (60 MPH) in 8 seconds.) Loaded
       trucks accelerate much slower.
    
       Because simulation uses discrete movement, the modeling is not
       as easy as it might be. The lead vehicle in a lane must move
       forward before the vehicle behind it. The ripple effect then
       moves the later vehicles.
    
       In C++ the best data structure for an unknown number
       of objects that must be controlled is a linked list. An agent
       method will traverse the list and cause the objects to perform the
       "move" method in the right order and at the right time.
    
    Implementation:
    
       It was a design decision to allow many vehicles and to allow long
       running times. This decision led to having the vehicle class store
       needed data locally rather than "reach" outside the class for data.
       e.g. When a vehicle is placed on a lane, it acquires the speed limit
       and other lane data into its local private variables. In this way
       each vehicle object is autonomous and can move and draw itself.
    
       Each step of the simulation, the main program operates each intersection
       in a linked list of intersections. Each intersection, in turn, operates
       each outgoing lane. Each lane, in turn,  moves each vehicle in the
       linked list of vehicles. Timing is controlled by the main program and
       there is a delay if all moving is finished before the time of the next
       discrete time step.
    
    
    // vehicle.h  for traffic simulation
    //            includes  class route  for vehicle
    
    #ifndef VEHICLE_H_
    #define VEHICLE_H_
    
    #include "simulation.h"
    
    class route
    {
      public:
        route():intersection_count(0), moving(reset), next(0){}
                                                         // default last move
        route(int Aroute_count, direction Amoving, route * route_list);
        void add_route( int Aroute_count, direction Amoving);
        direction get_moving(void);
        int get_count(void);
        route * get_next(void);
      private:
        int intersection_count;
        direction moving;
        route *next;
    };
    
    
    // vehicle.h  the vehicle class for autonomous vehicle
    
    class vehicle
    {
      public:
         vehicle( float Alength,
    	      float Aweight,
    	      float Aaccel,
    	      float Adecell,
    	      float Aposition,
    	      float Aspeed,
    	      float Aspeed_limit,
    	      float Ax_base,
    	      float Ay_base,
    	      direction Amoving,
    	      float Asignal_pos,
                  signal_state Asignal,
    	      vehicle *Anext_ptr);
        void move(void);
        vehicle *next(void);
        void new_next( vehicle *a_next );
        bool exiting( void );
        void set_new_lane_data(float Aspeed_limit,
    			   float Ax_base,
    			   float Ay_base,
    			   direction Amoving,
    			   float Asignal_pos,
                               signal_state Asignal);
        void set_signal(signal_state Asignal);
        void draw(bool erase_only=false);
      private:
        int    id;
        float  length;
        float  weight;
        float  accel;
        float  decell;
        float  position;
        float  position_last;
        float  speed;
        float  speed_limit;
        float  time_last_move;
        float  x_base;
        float  y_base;
        direction  moving;
        signal_state  signal;
        float  signal_pos;
        route * route_list;
        route * route_now;
        int route_count;
        vehicle  *next_ptr;
    }; // end vehicle.h
    
    #endif  // VEHICLE_H_
    
    
    // lane.h    for traffic simulation
    
    #ifndef LANE_H_
    #define LANE_H_
    
    #include "simulation.h"
    #include "vehicle.h"
    
    class lane
    {
      public:
        lane(void): id(0), position_x(0.0), position_y(0.0),
    	   speed_limit(0.0), moving(north), length(0.0),
    	   signal(none), vehicle_list(0)
    	   {};
        lane(float Aposition_x,
    	 float Aposition_y,
    	 float Aspeed_limit,
    	 direction Amoving,
    	 float Alength);
        void operate(void);
        void vehicle_start(float Alength,
    		       float Aweight,
    		       float Aaccel,
    		       float Adecell);
        bool vehicle_exiting( void );
        vehicle * vehicle_remove( void );
        void vehicle_add( vehicle *a_vehicle );
        void lane_end( float & x_lane_end, float & y_lane_end);
        void set_signal(signal_state Asignal);
        void draw(void);
      private:
          int   id;
          float position_x;
          float position_y;
          float speed_limit;
          direction moving;
          float length;
          signal_state signal;
          vehicle *vehicle_list;
    }; // end lane.h
    
    #endif  // LANE_H_
    
    
    //  intersection.h    for traffic simulation
    
    #ifndef INTERSECTION_H_
    #define INTERSECTION_H_
    
    #include "simulation.h"
    #include "vehicle.h"
    #include "lane.h"
    
    class intersection
    {
      public:
        intersection(float Ax_location,
    		 float Ay_location,
    		 intersection * Aintersection_list);
        void set_signal( intersection * Aintersection_list,
                         signal_state Anorth_south,
                         signal_state Aeast_west);
        void operate(intersection * Aintersection_list);
        void creator( float Alength,
                      float Aweight,
                      float Aaccel,
                      float Adecell,
                      direction Amoving);
        void connect_from( intersection * Aintersection_list);
        intersection * next(void);
        void draw(intersection * Aintersection_list);
      private:
        float x_location;
        float y_location;
        intersection * next_intersection;
    
        lane * lane_north; // fed by
        lane * lane_from_south;
    
        lane * lane_east;  // fed by
        lane * lane_from_west;
    
        lane * lane_south; // fed by
        lane * lane_from_north;
    
        lane * lane_west;  // fed by
        lane * lane_from_east;
    
        signal_state north_south;
        signal_state east_west;
    
    }; // end intersection.h
    
    #endif  // INTERSECTION_H_
    
    A somewhat working version of the complete program on Unix
    using X Windows is provided by these additional files:
    
     Makefile_traffic_sim 
     traffic_sim.cc 
     simulation.h 
     simulation.cc 
     vehicle.cc 
     lane.cc 
     intersection.cc 
     x_plot_d.h 
     x_plot_d.cc 
     delay.h 
     delay.c 
    
    Some strange code may be due to this being designed for
    multiple platforms where the plot routine and delay are
    different. Other strange code is just due to the author.
    
    

    Lecture 29, Review for Final Exam

     
       see Syllabus   and
       see homework assignment page for exam details 
    
    

    Lecture 30, Final Exam

     
       see homework assignment page for exam details 
      
    

    Other Important Links

    Go To Top

    Last updated 5/1/01