Object-Oriented Programming in C++

C++ is meant to extend the C programming language in such a way that it can support information hiding. Further, C++ emphasizes classes of objects. Primary goals of C++ include: (1) compatibility with C (2) efficiency (3) strict compiler time error checking.

Object-oriented programming languages have three things in common:
1) Objects
2) Polymorphism
3) Inheritance

An object is an entity that contains both data and code. In particular, the code can be public or private and is responsible for manipulating the data. This concept is also referred to as encapsulation.

Polymorphism is the ability to have one name used for several related but different purposes. As an example we might want to maintain several different stacks in a program of differing data type. Typically, three different sets of routines would need to be defined based on the type of information desired in each stack. These routines would all have different names. This is not the case in C++. We still need to define the differing sets of routines but we can use the same name and let the compiler select the correct routine to use. Inheritance in its most simple form is one object aquiring some or all properties of another object.

Consider a structure in C:

struct stbuffer
{
  char rgbuffer[MAXBUF+1];
  int wsize;
  int pwfront;
  int pwrear;
};

We know that stbuffer stbuf1, stbuf2 would declare variables stbuf1 and stbuf2. The first big difference between C and C++ is that C++ can include functions as structure members. Upon creation, a special member function called a constructor is called to initialize the object. The constructor has the same name as the structure in which it appears. So we might have the following:

struct stbuffer
{
  stbuffer()
  {
    wsize = MAXBUF+1;
    pwfront=pwrear=0;
  }
  char rgbuffer[MAXBUF+1];
  int wsize;
  int pwfront;
  int pwrear;
};

Member functions can also be included in structures. Further, the body of the member function can be included in the structure or it can appear outside the structure where the name resolution operator :: connects the member to the structure. As an example, consider the following:

struct stbuffer
{
  stbuffer()
  {
    wsize = MAXBUF+1;
    pwfront=pwrear=0;
  }
  int Iwsucc(int wi) { return (wi+1) % wsize; }
  int Iwenter(char);
  char Cbleave();
  char rgbuffer[MAXBUF+1];
  int wsize;
  int pwfront;
  int pwrear;
};

int stbuffer::Iwenter(char ch)
{
  /* body of the function goes here */
}

So if we put it all together, we might have the following:

#include 
#define MAXBUF 100

struct stbuffer
{
  stbuffer()
  {
    wsize = MAXBUF+1;
    pwfront=pwrear=0;
  }
  int Iwsucc(int wi) { return (wi+1) % wsize; }
  int Iwenter(char);
  char Cbleave();
  char rgbuffer[MAXBUF+1];
  int wsize;
  int pwfront;
  int pwrear;
};

int stbuffer::Iwenter(char ch)
{
  /* body of the function goes here */
};

void main()
{
stbuffer stbuf1;

printf("size of buffer=%d\n",stbuf1.wsize);
};

I have decided to compile and test the C++ code on tardis, so executing the following command:

g++ buf1.cc

produces the following:

size of buffer=101

Wow, this is great!! OK, so what is the relationship between a structure and a class? In general, we've seen that a structure so far looks like the following:

struct st{ };

which is another way of saying:

class st{ public: };

In general, all members of a structure by default are public while all members of a class by default are private. This implies the following:

class st{ };

is another way of saying:

struct st{ private: };

So what does this mean? A look at the following code shoud clear things up:


#include 
#define MAXBUF 100

struct stbuffer
{
  private:
  stbuffer()
  {
    wsize = MAXBUF+1;
    pwfront=pwrear=0;
  }
  int Iwsucc(int wi) { return (wi+1) % wsize; }
  int Iwenter(char);
  char Cbleave();
  char rgbuffer[MAXBUF+1];
  int wsize;
  int pwfront;
  int pwrear;
};

int stbuffer::Iwenter(char ch)
{
  /* body of the function goes here */
};

void main()
{
stbuffer stbuf1;

printf("size of buffer=%d\n",stbuf1.wsize);
};

12 -> g++ buf1.cc
buf1.cc: In function `int main(...)':
buf1.cc:8: constructor `stbuffer::stbuffer()' is private
buf1.cc:28: within this context
buf1.cc:28: in base initialization for class `stbuffer'
buf1.cc:30: member `wsize' is a private member of class `stbuffer'

Three keywords control what is visible by the outside world of a structure or class:

1) public - members of a structure or class declared to be public are visible to code outside the class or structure.

2) private - members decalred private are only visible within the class or structure.

3) protected - similar to private except with derived classes which we may or may not get to. Know that it exists and do some research.

The structure we have been using so far in C++ would probably be defined as follows:


class stbuffer
{
 public: 
  stbuffer()
  {
    wsize = MAXBUF+1;
    pwfront=pwrear=0;
  }
  int Iwsucc(int wi) { return (wi+1) % wsize; }
  int Iwenter(char);
  char Cbleave();

 private:
  char rgbuffer[MAXBUF+1];
  int wsize;
  int pwfront;
  int pwrear;
};

So the following statement is illegal:

printf("size of buffer=%d\n",stbuf1.wsize);

but the following statement is legal:

printf("size of buffer=%d\n",stbuf1.Iwsucc(1));

If we were programming a large system, the following mechanizm would probably be used to break up the program:

File: stbuf.h

const int MAXBUF=100;
class stbuffer
{
  public:
    
  private:
    
};

File: buf.c

#include "stbuf.h"
int stbuffer::Iwenter(char ch)
{
  /* body of code */
}

and so on

File: driver.c

#include "buf.h"
#include 
main()
{
  stbuffer stbuf;

  and so on
}

Let's look at some more specific examples. In particular, we will see that:

1) When an object is called, a constructor is called automatically.
2) Function names can be overloaded.
3) Objects can be allocated and deallocated by using new and delete respectively.
4) A specific class can allow a "friend" access to its private data.

Consider the following:

class clnode
{
 public:
  int winfo;
  clnode *pnext;
};
If we had the declaration: clnode *phead , then we know that there are two ways to get at the information field: winfo and they are:

1) (*phead).winfo
2) phead->winfo

Note: . has a higher precedence than * (dereference operator)

We could create a linked list using the following two commands:

clnode clhead,cltail;
clhead.pnext = &cltail;

Note1: A null pointer is specified by 0 or NULL.
Note2: The special name "this" specifies a pointer to the object itself.

When a variable of a particular class is declared, the constructor is automatically called. As an example, consider the following declaration:

class clnode
{
  int winfo;
  clnode *pnext;
public:
clnode(int iw, clnode *plink) {winfo = iw; pnext  = plink;}
};
The following two declarations actually set up a link list:

clnode cllast(5,NULL);

clnode clfirst(6,&cllast);

Problem: Identify the error in the following program:

#include 

class clnode
{
  int winfo;
  clnode *pnext;
public:
  clnode(int iw, clnode *plink) {winfo = iw; pnext  = plink;}
  clnodeprint() {printf("information=%d\n",winfo);}
};

main()
{
  clnode cllast(5,NULL);
  clnode clfirst(6,&cllast); 
  cllast.clnodeprint();
  clfirst.clnodeprint();
  clfirst.pnext->clnodeprint();

}
Problem: Create a class integer stack that uses an array and has functions:
1) Vinit
2) Vpush
3) Ipop

Write a main program that pushes the values: 3,4,5 on the stack and then every time a pop occurs, the value is printed to the screen.


©1995 Douglas J. Ryan
Douglas J. Ryan/ryand@tardis.pacificu.edu