Introduction

The C++ syntax extends the standard C language syntax so that the concepts of object oriented programming (OOP) can be included. The intention was to add OOP features to C while maintaining all the flexibility and speed of the original language. Programmers who are familiar with C and the concepts of object oriented programming can quickly start applying those ideas in C++ programs.

This document gives a short overview of C++ syntax. One or two isolated examples of each language feature are shown. For more extensive explanation and/or more detailed examples refer to the C++ books listed in the course bibliography.

C++ Syntax

Basic data types

C++ supports integer (int), real (float and double) and character (char) basic data types.
int
the int data type is used for signed integer values in the natural word size of the machine. The data type can be modified with the unsigned modifier for use with unsigned values.
float
the float data type is used for 32 bit floating point values.
double
the double data type is used for 64 bit double precision floating point values.
char
the char data type is a one byte value. This data type can also be modified with the unsigned modifier for use with unsigned byte values.

There is also a void data type that can represent nothing when used as the return type for a Function or as a generic data type when used as the base type for a pointer data type.

Pointer and reference data types

C++ provides two data types that give indirect access to an item of data. The * is used to specify a pointer data type. This defines a data type that is the address of an item of the specified data type. For example, int *, specifies a data type that is the address of an integer. The second type in this category is the reference type specified with &. This data type is similar to the REF function parameter in Pascal. It can be considered to create a new name or alias for a data item. For example, float &, would specify an object that references a float item. This type allows an object to be accessed using the normal syntax while the compiler handles the indirection for you.

Variable declaration

Variable declarations are composed of a data type and variable name. The data type can be: The variable name must start with an letter or underscore character and the following characters can be letters, numbers, or the underscore. Names are case sensitive. Arrays of items can be declared using the array reference symbol [] with an integer number of elements specified in the brackets. Multiple declarations of the same type can be placed in a single declaration statement separating individual variable names with a comma. The declaration statement is terminated with a ;.

Here are some examples of variable declarations:

  
  int i;                        <= integer
  char *cPtr;                   <= pointer to a character
  float arrayOfPoints[10];      <= array of 10 floats
  unsigned char uc1, uc2, uc3;  <= three separate unsigned characters.
  void *genericPtr;             <= indirect pointer to anything
  
  

A variable declaration can be placed anywhere in your program. This allows the programmer to decide if placing the declaration at the point in the program where the variable is used is more appropriate than the common practive of declaring all variables in one location. The variable is known, or scoped, only within the program unit in which it was declared. A variable is scoped from the point where it is declared to the end of the program unit containing the declaration. The lowest level of program unit is the code block delineated by the {} that define a compound statement or function. A variable declared within a compound statement in a function is scoped only within that compound statement. A variable declared outside of all functions in a file is accessible by any code in that file. For large programs that require multiple files of source code variables declared in one file can be made accessed by code in other files by specifying that it is an extern variable. extern declarations are often placed in an include file that is used by any file requiring access to the external variable. If there are multiple variables with the same name whose scopes overlap at one point in a program, the variable with the innermost scope will be used.

One of the concepts in OOP is data hiding. This implies that data contained in an abstract data type should not be directly visible to the outside world. Even before OOP techniques were developed, it was good coding practice to keep the scope of data as narrow as possible. If a variable was only used within a function then declare it within that function so it is only accessible in that program unit. (We will see with the concept of classes how C++ provides for data hiding at the level of data object.) The use of global variables, i.e. those scoped within an entire program, has never been good practice. To maintain compatibility with older C programs, C++ has kept the nasty feature that any variable declared outside of all functions in a file can be accessed by other files in the program simply by declaring the variable as an extern in those other files. If it is necessary to declare variables as global within a file but they are to be hidden from other program files, the compulsive programmer must declare each of these variables as static.

Here is an example of using extern and static variable declarations.

In file a.cc:

  
  int anExternInt;
  static int aPrivateInt;
  
  

In file b.cc:

  
  extern int anExternInt;
  extern int aPrivateInt;        <= error was declared as static
  
  

The variable anExternInt is declared in file a.cc and can be accessed by the file b.cc by declaring it as an extern. In file b.cc an references to anExternInt would use the variable declared in file a.cc. aPrivateInt is not accessible outside of a.cc because it was declared static. One point to keep in mind is that a global variable is declared without the extern in only one file. This should be the file with which the variable is most closely associated. It is then referenced with an extern, usually specified in an include file, in all other files that will use that variable.

Constants

C++ supports the standard integer and floating point constants. Character constants can also be defined within '', such as 'a'. A string constant can be defined by enclosing the string between "", such as "Hello, world\n". (The '\n' represents a newline character.) This constant is actually an array of characters that is one larger than the number of character in the string. This extra byte at the end of the string stores a '\0' (zero byte) which is considered as the terminator for the string. Most string functions assume that the zero byte terminator is present at the end of the string.

Expressions

The C++ syntax accepts all expressions that are allowed in C. This flexibility of expressions is loved by experienced C programmers and hated by novice programmers. Everything, even down to the assignment statement, is evaluated as an expression. Every expression has a value resulting from its evaluation. In addition, some expressions generate side effects during the evaluation process.

There are the standard +, -, *, and / binary arithmetic operators. The value of one of these expressions is the result of the arithmetic operation.

Binary comparisions are performed using the operators == (equal), != (not equal), <=, and >=. The value of these expressions is 1 if the comparison was true and 0 otherwise. C++ does not have a boolean data type.

As is standard in mathematical notation and other programming languages, () can be used to control the order of evaluation of complex expressions. Expressions are evaluated according to the precedence of operators which is specified in a table in any book on C++. (In Stroustrup, it is in section 3.2.)

The post/pre decrement/increment operators are available. The ++ increment operator will add 1 to the value of a variable. For example, a++ will increment a by 1. The value of this expression when used as part of another expression is the initial value of a. The side effect is the incrementing of a. If, on the other hand, ++a is used in an expression, the side effect is still the incrementing of a but the value for the expression is now the incremented value. This is pre-increment. It similarly holds for the pre/post decrement operator, --, which will subtract 1 from a variable.

As noted above, even the assignment operation is considered an expression. The value of this expression is the value assigned to the variable on the left of the assignment. The side effect is storing this value at that location.

The indirection operator, *, is used to dereference a pointer and provide access to the data at that location. The address-of operator, &, can be used to get a pointer to a data item. There is an example of this syntax under Simple Statements below.

One way in which C++ supports polymorphism is by allowing operators to be overloaded. Overloaded operators are described below in the section on classes, which are C++'s primary construct for supporting abstract data types (ADT).

Calls to functions that return values can be included as part of an expression. The function return data type and formal parameter definition must be specified before the function call. This definition is often done in an Simple Statements Statements are terminated in C++ with a ;. Here are examples of some simple statements:

  
  int a,b,c;
  int *d;
  
  a = 3 + b;
  b = ++c;
  
  d = &a;            // example of pointer operations
  *d = 4;
  b = c + *d;
  
  

Control Statements

Program control flow is available with if, while, do ... while, and switch statements.

The syntax for an if statement is:

  
  if expression
      if-statement
  
  

There is also an if-else statment:

  
  if expression
      if-statement1
  else
      else-statement2
  
  

If expression evaluates to a non-zero value then the if-statement is executed. Otherwise the else-statement is executed if it is present.

  
  while expression
      while-statment
  
  

While expression evaluates to a non-zero value execute the while-statement. The while-statement may not execute if expression is 0 when first executed.

  
  do
      do-statement
  while expression
  
  

Execute the do -statement while expression is non-zero. This loop will always execute at least once.

  
  for (initial-statement;test-expression;end-of-loop-statement)
      for-statement
  
  

Execute initial-statment, then evaluate test-expression and execute for-statement. Execute end-of-loop-statement and loop to evaluate test-expression. The most common use of the for statement is as a loop counter where the counter variable is initiazed, compared against a termination condition, and adjusted in the three parts of the for statement.

  
  switch expression
      case value1:
          case-statement1
          break;
  
      case value2:
          case-statement2
          break;
  
      default:
          default-statement
          break;
  
  

Evaluate expression and execute the case-statement with matching value label. If the value does not match any label execute default-statement if the default section is present. Execution falls through from case to case if the break statement is not present.

Compound

A compound statement is a group of statements enclosed by {} characters. This block of code is treated as a single statement thus allowing the programmer to create a multiple statement body for a simple statement such as a while, for, or if. The braces define a new program scope so that variables used only within the compound statement can be declared within these braces.

C++ has only one subprogram unit called a function. A function is defined with formal parameters which are replaced by the actual parameters provided when the function is called. The declaration of the function must also define the data type for the formal parameters. This is used as a check on the usage of the function within the program. A function returns a value to the calling program. The data type for the return value is specified in the function definition. A function that is specified to return a void value is equivalent to a Pascal procedure that does not return a value. Within the function code the return statement is used to define the value returned by the function. The body of the function is contained within {}.

Here is an example of the definition of a function:

  
  float Quadratic(float x,float a,float b,float c)
  {
      float result;
  
      result = a * x * x + b * x + c;
      return result;
  }
  
  

A functions can be further defined as an inline function. If the actual operation to be carried out by the function is small (this is small compared to the overhead of calling a function) program execution could be sped up by making the function inline. You still have the advantages of parameter type checking but gain the faster execution as if you had written the code directly inline.

Variables defined within a function are in existence only for the life of the invocation of the function. These are called automatic variables and their values do not persist from one function invocation to the next. If you want to maintain the values across function calls then declare the variable as static. (This is a second use for the keyword static that has no connection with its use for limiting the scope of global variables.)

Here is the above function written as an inline function and streamlined:

  
  inline Quadratic(float x,float a,float b,float c)
  {
      return a * x * x + b * x + c;
  } 
  
  

Classes and structures

The support that C++ provides for OOP is concentrated in the class notion. The class is the unit of data abstraction in C++. On the surface the class can be viewed like a C struct or a Pascal Record definition. The class provides the programmer with a method to encapsulate, i.e. hide, the state of the abstract data type (ADT) and specify the external interface for use of the ADT.

Here is a simple class definition for a complex number data type. Features of the definition are described in the sections that follow.

  
  class Complex
  {
  private:
      double re;
      double im;
  
  public:
      Complex(float r,float i) {re = r; im = i;}
      Complex(float r) {re = r;im =0;}
      ~Complex() {};
  
      double Magnitude();                  // calculate magnitude
      
      double Real() {return re;}           // return real part
      inline double Imag();                // return imaginary part
  
      Complex operator+(Complex b)
  	{return Complex(re + b.re,im + b.im);}
  
      Complex operator=(Complex b)
          {re = b.re;im = b.im; return *this;}
  }
  
  inline double Complex::Imag()
  {
      return im;
  }
  
  double Complex::Magnitude()
  {
      return sqrt(re*re + Imag()*Imag());
  }
  
  

Class and Structure Members

Elements of a class or structure can not only be variables storing the state of the object, such as re and im in Complex, but also functions that perform operations on objects of that type. The functions can have parameters specified, such as with Complex in Complex which is a special class member called a constructor. Note here the C++ support for polymorphism by allowing function names to be overloaded with different parameters. The compiler will determine the correct function to use based on the data type of the parameters in the function call. Complex has two Complex constructors defined with different parameter lists. Code that is part of a member function of a class, such as the code in Complex::Magnitude(), has access to all members of the class directly. Within Copmlex::Magnitude references to re or im will access the data members of that object. Note that defining a class member function is exactly the same as a regular function except that the function name is prefaced with class-name::. A class function member can also be directly accessed inside of a class function which was done to get the imaginary part of the complex number in Complex::Magnitude(). Class member functions that are very short can be declared as inline functions. If code for the member function is defined within the class definition, such as Complex::Real() it is assumed to be an inline function. Otherwise, the function must be explicitely defined as inline and code provided outside of the class definition, such as Complex::Imag().

Public, private, protected members

When defining a class the programmer can specify the level of access to members of the class from outside of the class. The keywords private, public and protected define the levels of access. Any members defined below a public label are accessible directly by any other data type. Members below a private label are accessible only from within member functions of the class. If some private members, either data or function members, must be available to other classes these can be specified using the friend designation. As with the scope of variables in your program, good practice dictates limiting access to the data members of a class. If program code outside of the class definition must modify data members provide a class function to do this. Similarly, if code must access the data members of the class, define them as private and provide public class functions to access the data. If no access designators are provided, all class members default to being private. The orignal C struct has been enhanced in that it can now define member functions as well as data. struct can be viewed as a class in which all members are public. The protected keyword is used to allow derived classes to have access to some class members that are not available as public. A derived class is a new class that inherits all of the properties of the class from which it is derived and may add new features specific to this new class. An example of this from a drawing program is that the programmer has defined a base class called Object. From this base class other objects are derived such as Rectangle and Circle. The are some aspects of the Object class that are shared by all objects. Each of the new objects may add other features such as a specific technique for drawing itself or calculating its area. If functions in this derived class need access to some of the base class's non-public members then those members can be declared protected. Alternatively, you could use the friend keyword to explicitely specify which other classes get access to the private members. A more detailed discussion of inheritance and derived classes is beyond the scope of this C++ primer.

Public access to class

Access to the public members of a class is through the structure access operators provided in standard C. If a has been declared as an object of Complex type then access to the magnitude of this complex number in an expression is gotten using a.Magnitude(). If b is declared as a pointer to an object of Complex type then access to the magnitude of this complex number in an expression is gotten using a->Magnitude(). If there are public data members in the class they can be accessed using similar syntax.

Constructors/destructors and creating objects

C++ handles many of the details of memory allocation for objects. The programmer can have explicit control over the operators performed when an object is created or destroyed. The constructor function is called whenever an object is created and the destructor function is called when the object is destroyed. The constructor is specified as a class function with the same name as the class. The destructor function is the class name prefaced with a ~. The class constructor function can be overloaded, as in the Complex class, to provide for different method to create new objects in the class. There can only be one destructor specified. If either constructors or destructors are not defined for the class then the compiler will substitute a default function. A call to a class constructor issued by code generated by the compiler at the beginning of an object's lifetime. A call to the destructor is performed at the end of the object's lifetime. For example, consider the simple program below that uses the complex class:
  
  #include <stdio.h>
  #include "Complex.h"
  
  main()
  {
      Complex a(1.0,1.0);
      Complex b(5.0);
  
      printf("a real = %f  a imaginary = %f\n",a.Real(),a.Imag());
      printf("b real = %f  b imaginary = %f\n",b.Real(),b.Imag());
  }
  
  
In this program the two Complex variables a and b are automatically created when main() starts executing. The compiler will determine the appropriate constructor to call based on the parameters specified in the object creation. The data members are accessed using the structure operator, .. When main finishes executing the destructor for all objects automatically created in main will be called. In the case of the Complex class the destructor is an empty function. Note the inclusion of the file stdio.h. This is necessary for the definition of the printf function. It is also possible to directly create a new object. This process is similar to the new operation in Pascal or malloc'ing an object in C. The C++ operator for creating a new object is new. This will return a pointer to an object of the requested data type. This is shown in a rewrite of the proceeding program to explicitely create the objects:
  
  #include <stdio.h>
  #include "Complex.h"
  
  main()
  {
      Complex *a;
      Complex *b = new Complex(5.0);
  
      a = new Complex(1.0,1.0);
  
      printf("a real = %f  a imaginary = %f\n",a->Real(),a->Imag());
      printf("b real = %f  b imaginary = %f\n",b->Real(),b->Imag());
  
      delete a;
      delete b;
  }
  
  
In this program the two complex objects are directly created using the new operator. Again the compiler will determine the appropriate constructor to call based on the parameter sequence given. Access to the class members is now via the structure reference operator, ->. Since these objects were explicitely created, good coding practice dictates that the programmer explicitely remove the objects when through with them. This is done using the delete operator. Again, if there was a destructor function defined it would be called at this point.

Overloaded operators

C++ provides the capability to overload standard operators. This is a very useful feature if there is a commonly accepted use for the operator. For example, complex mathematics defines the addition of two complex numbers. It is appropriate to overload the + operator to provide this for the Complex class. This class also overloads the assignment operator so that one complex number can be assigned to another. These features are used in the following program:
  
  #include <stdio.h>
  #include "Complex.h"
  
  main()
  {
      Complex a(1.0,1.0);
      Complex b(5.0);
      Complex c(0,0);
  
      c = a + b;
      printf("c real = %f  c imaginary = %f\n",c.Real(),c.Imag());
  }
  
  
The definitions for the overloaded assignment and addition operators each take only one parameter. C++ in reality treats the overloaded operator as another function. The left hand side of the operator is considered to be the current object. In the example above, this would be the a. Writting a + b could have equivalently been written a.operator+(b). The overloaded operators are defined within the class and are considered class member functions. Note that direct access to the private data of the Complex object on the right hand side of the operator is permitted is the operator+ function. Also, not the use of the keyword this in the assignment function which cooresponds to a pointer to the current object. The return statement dereferences this to return a value that is equal to what was stored in the left hand side of the assignment.

Include files

A part of standard OOP practice places the definition of a class and its inline class functions into an include file. This file holds the definition of the external interface to the class. The user of a class object has access to it through the public members of the class. This include file must be specified at the beginning of any source code file that will manipulate objects of the class. The standard file suffix is .h for include files. The following lines of code might appear at the top of a source file that works with complex numbers:
  
  #include <stream.h>
  #include <stdlib.h>
  #include "Complex.h"
  
  
This file will include three other files. It is as if the information contained in those three files was directly typed into the source file. The C++ compiler provides include files for the standard run time library functions that are used in C programs. The standard include files are specified inside <>. This instructs the compiler to search for the files in the "standard" include file locations. If your program is using any functions from the run time library you must include the appropriate files so that the compiler has a definition (return data type and parameters) for these functions. Enclosing the include file name inside "" starts the file search using the current directory and standard file reference mechanisms. You can specify the fully qualified path name for the include file in this statement. If the file is not found and it was not specified as a full path name then the search is continued in the standard places.

Using the compiler

The compiler we will use for this course is g++. There are not many command line options that you should need. The basic compilation command on troi is:
  g++ -o executable-file source-files -L/u1/cs173/ca1cs173/lib
  
This will create an executable program in the file you specify as executable-file. If there are several source file that make up your program they can all be listed on the command line separated by spaces. The -L option is needed to specify where to find the g++ library which is not correctly installed on troi. If your program is made up of many files and you have only made changes to a few of them then it would be faster to only compiler the changed files and link everything together separately. To just compile one file use the command for each changed .cc file:
  g++ -c source-file
  
This generates an object file with a .o extension for each source file you compile. When all the source files have compiled sucessfully then link them together using the command:
  g++ -o executable-file object-files -L/u1/cs173/ca1cs173/lib
  
The object-files are the names of all the .o files created when you compiled each of your source .cc files.