C++ - Miscellaneous Topics

Pure Virtual Functions

Some classes cannot be instantiated i.e. they server only as place-holders for descendant classes. These are called abstract classes. A class automatically becomes abstract if it contains one or more pure virtual functions. A pure virtual function is a virtual function that has no body and has the following syntax.

Example:
void virtual ExampleFunction () const =0;

The "=0" at the end makes the function pure. This class cannot be instantiated. Every descendant of the class must over-ride this function in order to make the class non-abstract and usable.

Operator Overloading

In keeping with the property that Objects are normal data types, they can be operated upon by the standard operators. Unfortunately, the compiler does not know how to handle the various objects eg. how to add two ComplexNumbers together. The programmer who writes the class has to define how these operations are to be performed. The functions of the various operators need to be extended to cater for the object in question. This is known as operator overloading.

Most of the standard operators (+, -, /, *, <, !, [], -=, =, ...) can be overloaded for a given object but their precedence or arity cannot be changed. Also, no new operators can be introduced and only new object types can overload operators.

For binary operators (like +, *), the first parameter is taken as the object itself and the second parameter is given as the parameter. The return type can be anything. The operator overloading takes the format of a function with the word "operator" followed immediately by the operator.

Example: 
Fraction operator*( const Fraction &Another );

The above operator overload uses a constant reference parameter. This tells the compiler that the parameter will not change and the compiler generates faster code.

Example:
Fraction Fraction::operator*( const Fraction &Another )
{
   return Fraction (numerator*Another.numerator, denominator*Another.denominator);
}

This declaration can then be used elsewhere in the program using the standard syntax for integers.

Example:
Fraction x(1,2), y(2,3), z;
z=x*y;

Similarly, operators can be overloaded to handle any function involving the object as the first parameter. Unary operators can be handled using no parameters.

Just as the compiler promotes types of variables in normal arithmetic, types of objects are promoted when overloading is used. If an expression involves an object and an integer and no overloaded operator exists for this, then the compiler tries to promote the integer into an object.

Example:
Fraction a, b(1,2);
a = b + 555;

The compiler looks for a constructor for Fraction that has only one parameter, an integer, and uses this to create a new Fraction from the integer. If no such function exists, the compiler will try other alternatives.

The alternative is to overload the typecasting operators for the Fraction. In this case, the problems might be solved if the first parameter could be converted into an integer. To achieve this, the compiler will automatically try to typecast the parameter. Such conversion operators take no parameters and have no return values since the return type is of the function name itself.

Example:
operator integer () const
{
   return numerator/denominator;
}

In many cases, the compiler finds that more than one implicit conversion is possible. An error is normally returned and the programmer must explicitly convert one of the parameters.

In order to handle an operation where the object is the second parameter and the first parameter is a standard data type, a friend function must be used.

friend Classes/Functions

Friend Functions are functions defined in one class(or in the main program body) that can access the private members of another class. A friend function must be defined in the class whose members it needs access to. An entire class can be defined as a friend, in which case any of the members of the friend can use any of the private data.

Example: 
class Vector { 
   friend class Matrix; 
}

Friends are especially useful when overloading operators where the first parameter is a standard data type.

Example: 
class Fraction { 
   friend Fraction operator*( int anInteger, Fraction &Another ); 
}; 
Fraction operator*( int anInteger, Fraction &Another ) 
{ 
   return Fraction (anInteger*numerator, denominator);
}

The Copy Constructor

The Copy Constructor is a special constructor that is usually defined in objects that are on the receiving half of an assignment statement. The object must have a constructor with a reference to an object of the same type as its single parameter. This constructor must effectively replicate the object.

Example:
Fraction ( const Fraction &Another )
{
   numerator=Another.numerator;
   denominator=Another.denominator;
}

The constructor is used automatically in assignment statements and when passing parameters to functions where the parameters are not references or pointers.

Streams

Standard input and output streams are implemented as objects in C++. There are various streams classes for doing input and output to and from the screen, files and buffers. cin is an object that refers to the standard input stream and cout refers to the standard output stream. The << and >> operators are used to perform input and output.

Example:
cout << "Hello there, " << MyName << "\n";
cin >> a >> b;

These are simpler ways of performing input and output in C++. There are classes in the iostream.h library to perform file I/O and buffer I/O as well.

The this pointer

In any object, there is a built-in pointer called this, which points to the object itself. This can be useful when returning values and passing parameters.

Static Members

Any class can have data members which are part of the class rather than part of every object created. These are called static data members. Static functions are functions within a class which only operate on static data members.

Example:
class Fraction
{
   static GoldenRatio;
   static double getGoldenRatio ();
}

Multiple Inheritance

A derived class can inherit the attributes of many base classes.

Example:
class FractionList : public Number, public List
{
};