Intermediate C++

Builds upon the foundation principles of C/C++, introducing the concept of Object Oriented Programming, the fundamental concept of which is the creation of objects.

 

Objects can be thought of as independent smaller self-contained mini programs that each have their own name, data and logic, and interact with one another to form a whole application.

 

OOP is intended to make thinking about programming closer to thinking about the real world, where an object’s attributes and behaviours are bundled together.

 

The three pillars of OOP are Encapsulation, Inheritance and Polymorphism.

 

This section of the site covers C++ OOP, and also some of the more advanced techniques in C++

Classes and Objects

A class is a user defined abstract data type that further expands upon the concept of a structure but instead of containing just data, it also includes functions.

 

In OO parlance, functions within a class are referred to as methods.

 

The concept of enclosing data and functions together within a single entity is referred to as Encapsulation.

 

Strictly speaking, as far as the compiler is concerned, the only difference between a structure and a class is that of accessibility (aka visibility). A structure’s members are all public by default, whereas a class’s members are all private by default. However, it is normal programming convention not to mix the two up and therefore structures are generally used for data (or POD: Plain Old Data), and classes are used for objects.

 

A class consists of three elements:

 

  • Identity
    • Name for the class
  • Attributes
    • Data members
  • Methods
    • Function members

 

A class is akin to a blueprint or plan of how to make an object, that declares its attributes (member data types) and methods.

 

A class is declared by use of the keyword class

  • followed by the user defined identity name for the class (akin to a primitive data type, e.g. int)
  • followed by the class members enclosed within a set of { curly braces }
  • followed by optional object identifiers
  • followed by a semi-colon ;

 

Syntax:

class identity {

public:

members ;

private:

members ;

protected:

members ;

} objects ;

 

The keywords public, private and protected are known as the access specifiers, which define the level of visibility of a class’s members.

 

Once the class has been declared, an object is declared just like any other data type (e.g. int x;).

 

Extremely simple Class example:

Compile & Run:

Triangle area: 15

 

The above example is overly simplified to show a function being used within a class. Normally all attributes (in this case the two floats on lines 7 and 8) should be private to ensure that only the object itself is responsible for any changes of its own data.

 

An object is said to be instantiated, by declaring a new identifier against the class’s identity (i.e. the abstract data type), as per line 17 above.

Encapsulation

The concept of combining attributes and methods (data and functions), collectively referred to as members, into a single named object / entity.

 

Access / visibility of the members is controlled by access specifiers that define what is presented externally (public) and what is kept hidden (private).

 

Since an object should be responsible for its own data, only its public methods should be used to manipulate its data, and it is these public methods that are considered as its interface.

 

Only the barest minimum should be publicly visible and utilised externally.

 

Any invoking object should not care how the requested object’s methods are implemented, all they should care about is presenting the required information via a message to the object’s public interface in the correct format, so that it can receive an expected return or action.

 

Access to an object’s attributes should only be controlled by the object itself, and no other object should directly change an attribute of another object.

 

This concept is known as data hiding.

 

This is akin to the black box model. We don’t care what goes on inside as long as we get what we want from it, providing we supply the correct information to it.

 

So as long as the interface stays the same, the internal methods of the object can be changed or improved as/if required.

 

Reduces complexity and dependencies, so that a change in one place won’t require multiple changes elsewhere.

 

In this example, objectA is instantiated on line 26, we then access its public set method to give values to its private data members, and finally we access those private data members using the public get methods on line 30:

Hello, World!

 

 

Access to the object’s private data is completely controlled by the object itself. We cannot directly access (set or get) at its attributes without using its own methods.

Interface Vs Implementation

This concept focuses upon separating what an object does from how it does it.

 

This concept goes hand in hand with encapsulation, data hiding and abstraction in providing the foundations for OOP.

 

Specifically, Interface is the public methods of a class that control how it behaves. Whereas implementation is the actual workings of how it behaves.

 

For instance, slightly modifying the previous example, the class now simply declares its interfaces within the class declaration.

 

The implementation has been defined outside of the class declaration, but is associated via the scope resolution operator ::

 

Thus, this class’s interface can be seen on lines 10, 11 and 12, and their according implementations on lines 15, 20 and 24 :

Compile & Run:

Hello, World!

Messages

Messages are the communication mechanism between objects.

 

Messages enable one object to invoke an action (method) in another object.

 

Messages comprise of three components:

  1. The object being addressed
  2. The method to perform
  3. Any parameters required by the method

Carry on with the previous example, the messages can be seen on line 32:

 

objectA.set(“Hello, “, “World!”) ;

 

and similarly on line 34 (without parameters):

Compile & Run:

Hello, World!

Abstraction

Abstraction is the concept of focusing on the key elements of an item rather than the unimportant (i.e. pulling out [abstracting] the key features), whilst also hiding the way it’s implemented.

 

For example, we all know a motorbike is made up of many objects, but essentially they all have two wheels, a frame, an engine, handle bars and a seat -- this is abstracting the main aspects of a motorbike. We don’t particularly care how it works internally, as long as it does. We can then refine any particular instance of an object according to the specifics required, i.e. by way of defining sub-classes from super classes that add specific details required for the sub-class object.

 

Similarly, we can apply this concept to programming to create generic Abstract Data Types (or Abstract Base Classes) and call upon its methods for a specific object according to the (data) type of object.

 

This allows focus on the “What” not the “How”.

 

The classic example is that of a polygon. We know that it is a shape that has sides and an area. We can declare some abstract information of the shape such as its height and width, and then further refine the shape according to the sub-class to define its area:

Compile & Run:

Rectangle has an area of: 20
Triangle has an area of: 10

Access Specifiers

The access specifiers define the level of visibility of a class’s members to other objects.

 

The following keywords are used to define the level of access:

  • public
    • public members are accessible any place the object is visible
    • public methods should be the only way to change an object’s attributes
    • public members make up an object’s public interface
  • private
    • private members are accessible only from other members of the same class
      • i.e. once instantiated, only that object
  • protected
    • protected members are accessible from other members of the same class and to members of derived classes derived
      • i.e. a child class can also access the protected members of the parent

The access specifier is declared within a class’s codeblock after its opening curly brace { by using one of the above keywords followed with a colon : All members following from that point forward are then considered to fall within the access specifier defined, up to the next access specifier or closing curly brace }

 

The default access specifier for a class is private, and can be omitted -- however, it’s good programming practice to include for the sake of readability (if only for yourself at a future stage).

 

The general rule is to keep all of your data private and provide public methods to access it. This concept is known as data hiding.

 

public:

With all members defined public, an object’s data can be changed without having to use its methods:

 

Compile & Run:

myTriangle width was set at 12
myTriangle height was set at 10
myTriangle area: 60

 

 

Although the method was called on line 28 to set the height for myTriangle, it was possible to directly access the height on line 29 since all members (including the object’s data on lines 7 and 8) are public.

 

 

private:

Now (correctly) defining the class’s data as private, we can only access its data by using its methods:

Compile & Run:

myTriangle width was set at 12
myTriangle height was set at 2000
myTriangle area: 12000

 

 

Since the default access specifier level for a class is private, any member not explicitly defined within an access specifier is assumed private.

 

 

protected: 

Allows derived/child class access:

Compile & Run:

myTriangle width was set at 12
myTriangle height was set at 2000
myTriangle area: 12000

 

In this overly simplified example, the height attribute for the child Triangle class is derived from the parent Shape class in which the child is inheriting the protected height data attribute.

getters/setters

Getters and setters are the common names for what is more formally known as the Accessors and Mutators, respectively, which provide the methods to set and get values for an object.

 

Since we want to keep all data private to the object, we need specific methods defined to allow an object to control how its data is accessed.

 

The general approach is to prefix the method names with set / get as per their required functionality:

e.g.

setHeight()… //to set the height attribute

getHeight()… //to get the height attribute

 

Since the scope of data members is local to the class (i.e. within its curly braces {}), values can be assigned to them by passing in a parameter to a class’s method specifically defined for this purpose. Assuming a float height ; private member has been declared, we can set its value as follows:

 

void setHeight(float myVar) {

height = myVar ;

}

 

As we are simply setting a value and expect no return, we use the void data type for the method. It is then followed by the setHeight identifier for the method and the expected parameter data type and identifier to be used within the body. The function then simply assigns the passed in myVar variable to the previously declared private member variable height.

 

Similarly, to get a value, a method is defined that simply returns the desired value:

 

float getHeight(){

return height ;

}

 

The data type of the expected return value is declared for the getter, which is then followed by the getHeight identifier for the method whose body simply returns that value.

 

Continuing with the previous examples, we have already seen the getter and setter methods being used:

Compile & Run:

myTriangle width was set at 12
myTriangle height was set at 2000
myTriangle area: 12000

methods

In OO parlance, the term method is used to denote a function within a class.

 

If a class ‘s methods were to become increasingly large it could make the code difficult to read, and it is therefore common practice to declare prototypes for the methods inline (i.e. within the class) and to then define the implementation of the methods outside of the class’s body, to then be accessed using the :: scope resolution operator.

 

Compile & Run:

myTriangle width was set at 12
myTriangle height was set at 2000
myTriangle area: 12000

Constructors

A constructor is a special type of class method that is always called when an object is instantiated.

 

Typically used to set initial values for an object.

 

A constructor has exactly the same name as the class but has no return type!

 

A constructor cannot be explicitly called. It is automatically called when an object is instantiated.

 

If no constructor is defined, the compiler will automatically provide one for you. This is similar to declaring to a primitive data type (e.g. an int or a char) whereby storage will be allocated but is not initialised.

 

The previous example now has a (inline) constructor included on line 11:

Compile & Run:

myTriangle width was set at 12
myTriangle height was set at 2000
myTriangle area: 12000
myTriangle width was set at 45.45
myTriangle height was set at 12.34
myTriangle area: 280.427

 

A new triangle instance called myDefault was declared on line 39. Notice that no values have been assigned and therefore the constructor has set the object’s values to those specific in the class.

 

Here’s the same code but with the constructor defined outside the class:

Compile & Run:

myTriangle width was set at 12
myTriangle height was set at 2000
myTriangle area: 12000
myTriangle width was set at 45.45
myTriangle height was set at 12.34
myTriangle area: 280.427

Constructors with parameters

It is possible to have as many constructors as you like as long as their signatures are different, and are therefore being overloaded.

 

This allows your parameters to be set at instantiation, rather than a default set.

 

Compile & Run:

myTriangle width was set at 22.22
myTriangle height was set at 31.13
myTriangle area: 345.854

 

 

The default constructor could now be placed within the parametrised constructor to simplify:

Compile & Run:

myTriangle width was set at 22.22
myTriangle height was set at 31.13
myTriangle area: 345.854

Constructor Initialisation Lists

Members can also be initialised using initialisation lists.

 

The members to be initialised are defined after the signature of the constructor, which is followed by a colon : and then specifying the members in a comma separated list with the passed in parameter(s) from the signature making up the parameter for the member being initialised:

 

Triangle::Triangle(int x = 45.45, int y = 12.34) : width(x), height(y) {} ;

 

equivalent to:

 

Triangle::Triangle(int x = 45.45, int y = 12.34) {

width = x ;

height = y ;

} ;

 

Compile & Run:

myTriangle width was set at 45.45
myTriangle height was set at 12.34
myTriangle area: 280.427
myTriangle width was set at 16.73
myTriangle height was set at 29.84
myTriangle area: 249.612

Destructors

A destructor is another special class method, complimentary to a constructor, that cleans up resources by deleting the contents in the storage used for an object when it goes out of scope, or when the delete keyword is used on an object.

 

Has the same name as the class, preceded by a tilde ~

Has no return data type.

Takes no arguments.

 

Cannot be explicitly called. It is automatically called when the object goes out of scope.

 

If no destructor is defined, the compiler will automatically provide one for you. However, there might be time when you want to ensure the object has been cleaned, or you might want to carry out a quick exit task such as leaving a message.

 

Compile & Run:

Hello, World!
myVar = 42
See ya later…

Copy Constructor & assignment operator=

Used to make a copy of an existing object.

 

 

Copy Constructor

Takes one parameter, which is a reference (i.e. using an & to create an alias) to the object being copied, of the same class data type.

 

The usual signature of a copy constructor is as follows:

 

ClassName::ClassName(const ClassName & source) :

 

 

If a copy constructor is not defined, the compiler provides an implicit copy constructor that provides a member level copy of the source object.

 

This type of member wise copy is also known as shallow copy.

 

For example, given a simple class definition with 3 attributes:

 

class MyClass {

int myInt ;

float myFloat ;

string myString ;

} ;

 

The compiler would provide an implicit copy constructor that is equivalent to:

 

MyClass::MyClass( const MyClass & source) :

myInt( source.myInt ),

myFloat( source.myFloat ),

myString( source.myString )

{ }

 

An initialiser list has been used to define each of the 3 attributes in this example, but of course the compiler implicit copy constructor would copy N attributes as required for the number of object attributes.

 

The use of the keyword const in the copy constructor signature ensures that the passed in reference (alias) to the object will not be changed, thus ensuring the original remains intact and that the copy constructor acts upon the new object without being able to change the original.

 

 

Assignment operator=

Another form of copying is provided by the = assignment operator, whereby a copy of an existing object is assigned to another. In this case, both objects must exist in the first place for the assignment operator to come into effect, otherwise a copy constructor would be invoked as per the above.

 

The usual signature of assignment = operator is as follows:

 

ClassName & operator=(const ClassName & source)

 

 

The assignment operator= is also implicitly provided by the compiler if it has not been defined, and taking the example class above would be equivalent to:

 

MyClass & operator=( const MyClass & source) {

myInt = source.myInt ;

myFloat = source.myFloat ;

myString = source.myString ;

return *this ;

}

 

Here, the assignment operator = is being overloaded and redefined within the body of its code block. The keyword operator followed by the operator to be overloaded (e.g. =) are defined after the return type of the object, which is of course the class name (in this example myClass). The reference to the object being copied is then used as the source, where in the body it assigns the attributes to the new object (in this example just the 3 primitives) and returns the new object itself via the *this self pointer.

 

It must be noted that the copy constructor and the assignment operator, are two of the “Rule of Three” with third being the destructor, whereby if the default (implicit) versions of any of these three needs to be defined, then it is likely that all three need to be defined.

 

As we have seen above, the copy constructor and the assignment operator have different implementations, and are therefore invoked in different ways.

 

The copy constructor is invoked when the object being copied is used as the parameter for a new object definition.

 

e.g. assuming MyClass objectA ; //create a new object called objectA

then MyClassB objectB(objectA) ; //uses objectA as the parameter to be copied to create objectB

 

 

Alternatively, the assignment operator= is invoked when the two objects already exist.

 

e.g. MyClass objectC ; //create a new object called objectC

then assigning an existing object to the new object e.g. MyClass objectC = objectA ;

 

What’s actually happening here is that the assignment operator = is being overloaded and objectA is being used as its parameter.

 

This example shows an objectA being instantiated, which is then used in the copy constructor to instantiate objectB. Next, objectC is instantiated with its own attributes and some text is displayed before the operator= assignment is called, then at the start of the operator= assignment and again at the end of the operator assignment. At this point, the newly copied object is returned using the *this self pointer, which actually returns a reference to the object as indicated by the use of the ampersand & being indicated as the return type at the beginning of the signature.

 

To emphasize, an assignment operator returns a reference to the target object. This allows chaining of assignments, e.g. objectC = objectB = objectA ;

 

Compile & Run:

objectA is instantiated and contains: 17, 3.1416, Apollo

objectB is instantiated via the COPY CONSTRUCTOR and also contains: 17, 3.1416, Apollo

objectC is instantiated and contains: 12, 9.666, Hammer time!
ASSIGNMENT ‘OPERATOR=’ CALLED
objectC now contains: 17, 3.1416, Apollo

objectD is instantiated, but since it did not exist before it uses the COPY CONSTRUCTOR
and we can see it also contains: 17, 3.1416, Apollo

Deep Copy Constructor

A problem exists with the implicit shallow copy constructor since it literally copies everything, including pointers to members.

 

 

Therefore if a copy or the original should go out of scope before the other, then the destructor will destroy the copied values still being pointed to by the remaining copied objects, thus potentially pointing to garbage, which could lead to a program crash. This can be overcome by creating your own copy constructor that properly allocates memory in the copy. This technique is known as deep copy.

 

 

className::className(const className &rhs) {

myVar = new int ; //the use of new here allocates separate memory for the copied object

…code goes here…

}

 

 

The destructor should then clean up the allocated storage:

 

className::~className() {

delete myVar ; //clean storage

myVasr = NULL ; //assign NULL just for safety

}

 

The following program provides a deep copy constructor, utilising new for each objects pointed data:

Compile & Run:

Constructor, allocating pointer to address 0x8a1130 containing 25
Copy Constructor, allocating pointer to address 0x8a1140 containing 25 #0
Humanoid is 25
Deleting data in 0x8a1140
Copy Constructor, allocating pointer to address 0x8a1140 containing 25 #1
Copy Constructor, allocating pointer to address 0x8a1150 containing 25 #2
Humanoid is 25
Deleting data in 0x8a1150
Copy Constructor, allocating pointer to address 0x8a1150 containing 25 #3
Copy Constructor, allocating pointer to address 0x8a1160 containing 25 #4
Humanoid is 25
Deleting data in 0x8a1160
Copy Constructor, allocating pointer to address 0x8a1160 containing 36 #5
Humanoid is 36
Deleting data in 0x8a1160
Copy Constructor, allocating pointer to address 0x8a1160 containing 25 #6
Humanoid is 25
Deleting data in 0x8a1160
Deleting data in 0x8a1150
Deleting data in 0x8a1140
Deleting data in 0x8a1130

 

 

*Note: The copy constructor is called each time the display function is called, since it is using the getAge() method which returns the assigned value using the pointer.

this

The keyword this is a pointer to the current object, automatically provided by the C++ language.

 

The value of this is &currentObject, i.e. it is the address of the current instantiated object.

 

Useful for disambiguating identifier names, i.e. where two data members have the same name, the this pointer can be used to refer to the current object being pointed to.

 

private:

int myVar;

public:

void method(int myVar) {

this->myVar = myVar

}

 

In this case the rhs passed in parameter is assigned to the current object’s myVar attribute within the scope of the method, which evades ambiguity towards the class’s private myVar by use of the this-> pointer.

 

The current object can also be returned by dereferencing *this

 

return *this ;

 

Compile & Run:

myCube side: 6, volume: 216
myCube address using this: 0x28ff0c
which should be the same as using &: 0x28ff0c 

 

boing side: 6, volume: 216
boing address using this: 0x28ff08
which should be the same as using &: 0x28ff08

 

yourCube side: 5, volume: 125
yourCube address using this: 0x28ff04
which should be the same as using &: 0x28ff04

 

 

 

 

Another example:

(source)
Compile & Run:

180

Pointers to Classes

A pointer to a class object is declared just like any other data type:

 

className identifier ;  //declare the instance of the object

 

className *myPtr ;  //declare a pointer of the same class as the object

 

myPtr = &identifier ;  //assign the address of the object instance to the pointer

or

className *myPtr = &identifier  //both previous two steps in one

 

Members of classes can be accessed using the shorthand little arrow -> member pointer dereferencing operator (aka member selection operator).

 

Compile & Run:

Valentino Rossi is a 33 year old Italian Moto GP Rider, who has won 9 World GP championships.
All hail the 9 times world champion! 

 

Troy Bayliss is a 43 year old Aussie World SuperBike Rider, who has won 3 World SuperBike championships.
All hail the 3 times world champion!

 

 

 

 

 

Summary of pointer and class operators:

*x pointed by x
&x address of x
x.y member y of object x
x->y member y of object pointed by x
(*x).y member y of object pointed by x (equivalent to above)
x[0] first object pointed by x
x[1] second object pointed by x
x[n] (n+1)th object pointed by x

Static Attributes

Static attributes refer to the data members of a class that may be required to be declared as static data types. This could be useful, say, if you needed to know how many objects had been created, were in existence, etc.

 

As with other static declarations the keyword static is used prior to the attributes to be made static, which will ensure that only one copy of the static member exists despite how many objects are made from that class.

 

Static data members of a class are also known as “class variables”, because there is only one unique value for all the objects of that same class. Their content is not different from one object of this class to another.

 

Thus a static attribute is shared by all objects of the class and is initialised to zero, should no other initialisation be defined.

 

Static attributes also need to be initialised outside of the class, using the double colon :: scope resolution operator to identify which class it belongs to.

 

Compile & Run:

25 year old object #0 created!
32 year old object #1 created!
63 year old object #2 created!
Object #2 destroyed!
Object #1 destroyed!
Object #0 destroyed!

Static methods

Just as a class’s data can be made static, so can its methods.

 

In this respect they act like global functions and are accessed like object members of a class.

 

Since static methods are members of the whole class and not of any instance of an object, they can:

  • only use a class’s static data
  • not use non-static members
  • not use the keyword this, as it uses a pointer to an object
  • be called independently, even if no object exists
  • be accessed using the scope resolution operator ::

 

If access to a class’s private data member is required, we would normally write a public accessor (get) method. However, this would require instantiation of a class object to access the function. Therefore in the case of static data, we write a public static method to return the static data.

 

Compile & Run:

In the beginning there were 0 Humanoids!
But they bred…
Humanoid #0 created!
Humanoid #1 created!
Humanoid #2 created!
Humanoid #3 created!
Creating 4 Humanoids!
So we killed them…
Humanoid #3 destroyed!
Humanoid #2 destroyed!
Humanoid #1 destroyed!
Humanoid #0 destroyed!

 

 

Or even simpler, not even creating any objects and just using the static method:

Accessing static method 1 times
Accessing static method 2 times
Accessing static method 3 times
Accessing static method 4 times
Accessing static method 5 times
Accessing static method 6 times

Friend Functions

Prototypes of friend functions are declared within a class preceded by the keyword friend and then followed by the function prototype signature, to allow external functions (i.e. the friends) to access private and protected members.

 

Syntax:

friend void display() ;

 

This will allow the display() function, defined outside the class, to access all members within the class:

Compile & Run:

Valentino Rossi is a 34 year old motorcyclist, who has won 9 world championships!
All hail the 9 times world champion! 

 

Troy Bayliss is a 43 year old motorcyclist, who has won 3 world championships!
All hail the 3 times world champion!

Friend Classes

Whole classes can also be made into friends to grant full access to another class’s protected and private members, by declaring the keyword friend followed by the keyword class and then the identifier name of the class you wish to make into a friend.

 

Syntax:

friend class classOne ;

 

This will allow all members within the class in which it is declared to be accessed by the friend classOne.

 

The friendship is not mutual, and just because the original class may have declared its friend it does not mean that it will be able to access members of the friend, unless the friend has explicitly reciprocated the friend declaration back to it. All friends must be explicitly specified, and they do not transfer between one another.

 

Compile & Run:

precious 

rotten

Inheritance

Process of creating a new class from an existing class.

 

New class is know as the Derived, or Child or Subclass.

 

Existing class from which it was derived from is know as the Base, or Parent or Superclass.

 

The most important feature of inheritance is that derived class is a type of the base class -- the emphasis being on type!

 

 

 

*Note: In UML (Unified Modelling Language), Inheritance is shown as a large blank arrow head

 

Promotes code reuse and reduces code size.

 

Allows us to identify some shared members between objects.

 

Overriding Parent methods for specific Child behaviour.

 

Identify commonality, put into Parent, then let Child classes inherit.

 

This technique implements what is referred to as the “Is-A” relationship.

e.g.

a car is a vehicle

a Ferrari is a car

Therefore a Ferrari is also a vehicle

 

 

A class inherits from another by using a colon : in the declaration of the derived class, like so:

 

class childClass : public parentClass {

… code goes here…

}

 

Notice the use of the keyword public in the declaration, this instructs the compiler that the public members of the parent will be inherited by the child.

 

Similarly, the keywords private or protected could have been used to inherit to those levels.

 

The Child class inherits the Parent’s methods as follows:

Access by: public private protected
Same class Yes Yes Yes
Derived class Yes Yes No
External class Yes No No

 

Public inheritance provides the Parent’s Public and Protected methods to the Child (as its own Public and Protected methods) but not its Private methods, these should be accessed using calls to the Parent’s Public methods.

 

Protected inheritance provides the Parent’s Public and Protected members as Protected members in the Child.

 

Private inheritance provides the Parent’s Public and Protected members as Private members in the Child.

 

Here, the two subclasses (Ducati and Yamaha) use the Parent Motorbike class. This Parent class is never called directly, but just acts to provide similar qualities (members) to the derived classes, in this case the ability to set the int speed. The Child classes then use their own methods unique for their own behaviour, in this case their public methods of getSpeed and saySpeed (just using different names to show they are different):

Compile & Run:

Here’s a Ducati Panigale Motorbike going: 129mph…
This Yamaha Enduro Motorbike goes: 79mph, off road!

 

 

C++ also allows multiple inheritance, by providing a comma separated list of the classes to be inherited. This is an extremely simple example, just to show multiple classes being inherited on line 20:

Compile & Run:

Here’s a red Ducati Panigale Motorbike going: 129mph…

Method Overloading

We have already seen a form of method overloading, whereby the constructor was overloaded with parameters to provide default values. This concept is simply applied to other methods of a class to provide method overloading, which is the same as function overloading but within a class.

 

Overload resolution is the term given to the process of determining the appropriate overloaded method, and relies upon the data type, order and quantity of parameters passed to the method.

 

The overloaded methods all have the same name but will have different signatures, as per their specific functionality.

 

For instance, consider three add() methods:

 

add(int, int) {…code body…}  //requires two int parameters

 

add(float, float) {…code body…}  //requires two float parameters

 

add(string, string) {…code body…}  //requires two string parameters

 

The compiler will select the correct method to be acted upon, according to the data type of parameters sent.

 

This very simple example shows a method being overloaded according to the data being sent to it:

Compile & Run:

Passed in value is: 5
Passed in value is: 17.3634
Passed in value is: Amazing

 

 

 

A variation, showing the constructor being overloaded:

Compile & Run:

Derrick is a 49 year old English Ducati 900SS rider, who has won 0 world championships!
Vanessa is a 43 year old American pillion rider, who has won 0 world championships!
Troy Bayliss is a 43 year old Aussie Ex World SuperBike rider, who has won 3 world championships!
Valentino Rossi is a 33 year old Italian Moto GP rider, who has won 9 world championships!
The object with Italian nationality has been destroyed!
The object with Aussie nationality has been destroyed!
The object with American nationality has been destroyed!
The object with English nationality has been destroyed!

Operator Overloading

Allows operators to work on classes.

 

Just as methods can be overloaded, so too can most of the operators:

+ -- * / % = ! < > & | ^ << >>
+= -= *= /= %= == != <= >= &= |= ^= <<= >>=
++ --- ->* -> ~ , () new delete && || [ ] new[ ] delete[ ]

 

The exceptions being:

Ternary ? : sizeof() Scope Resolution :: Dot member selector . Member pointer selector .*

 

There are two main types of operator overloading:

  • Unary
    • Acting upon a single object
  • Binary
    • Acting on more than one object

Operators are overloaded by use of the keyword operator followed by the operator sign to be overloaded e.g. (operator=) and its new functionality then defined in the code body.

 

We have already seen the assignment = operator being overloaded in the above article on Copy constructor and assignment overloading, and the same principle is applied to overload the other operators.

 

Syntax:

return_type operator sign ( parameters ) {

…code body…

}

 

It should be fairly straight forward to see that a + b, could easily be translated into operator+(a, b). Assuming there are overloaded methods for the + operator, the compiler knows which one to use based on the type of parameters being sent. i.e. two ints or two floats, or two objects.

 

This example creates two simple Book objects consisting of an ISBN number and a price. The + operator has been overloaded to add the price component of the two objects, based on the type of parameter which in this case is a Book object. The overloaded method operates on the first operand (internally the this self reference) and adds the second object (as the passed in parameter):

Compile & Run:

The address of CPP is: 0x28ff08
The address of Perl is: 0x28ff00
The address of this is: 0x28ff08
The address of the passed in parameter is: 0x28ff00
The price value of this is: 15.66
The price value of this is: 15.66
The price value of the passed in parameter is: 21.32
The total cost of the two books = 36.98

 

This example is overly verbose to show the internals of the objects.

Note, that a reference has been used as the parameter to simplify memory allocation, by referring to the passed in parameter another copy of the passed in object is not required and hence the additional memory overhead associated with that copy is not required. The example would have worked just the same without using a reference, but give it a try and you will see a new address created for the temporary object being created from the passed in parameter.

 

Also note, the operator+ function could have been called like so: CPP.operator+(Perl) however it is far less awkward to simply write + to produce the same result. Again, try it out; replace the addition expression on line 35 with CPP.operator+(Perl) to achieve the same (but not so easily read) result.

 


 

It is also possible to make the +, --, & and * operators act as a unary or a binary operator, depending on context. This example show the -- minus sign operator being overload in both unary (line 15, called from lines 39 and 40) and binary (line 22, called from line 45) contexts:

Compile & Run:

xPos: -12, yPos: 34, zPos: 63
xPos: 17, yPos: -22, zPos: -42
xPos: -29, yPos: 56, zPos: 105

Unary Operator Overloading

Refers to operator overloading that act on a single object.

 

NOT Address One’s Comp Point deref Plus Increment Negation Decrement
! & ~ * + ++ -- ---

 

The ! Logical NOT operator is being overloaded here on line 11 to return a bool to the test condition on line 28:

Compile & Run:

Please enter a number
56
56 is an even number

Binary Operator Overloading

Refers to overloading operators that act on more than one object.

 

In this example, the less than < operator is being overloaded to check the ‘GetUp’ object against the ‘GoToWork’ object. In this case, the ‘GetUp’ object is the calling object and the ‘GoToWork’ object is the paraemeter being used in the overloaded method:

Compile & Run:

Please enter the hour of your getting up time
8
and the minutes
22
Hmm, temporal shift! You seem to go to work before you get up!

inline Functions

The use of the keyword inline placed before a function, instructs the compiler to replace all functions calls to the function with a copy of the function code.

 

 

This greatly increases speed since the compiler doesn’t need to store the return address from the function call, thus saving time.

 

However, the down side to this is that this can greatly increase the code size, since each function call is now expanded with a copy of the whole function code.

 

Syntax:

inline functionName() {

…code body

}

 

Compile & Run:

The cube of box 1 is: 27
The cube of box 1 is: 27

Operator Methods

aka Operator member functions

 

To make a class’s code more readable, it is suggested that its methods be declared as prototypes within the class and that their implementation is external to the class.

 

Methods then gain access using the scope resolution operator :: e.g. void myClass::getValue(){return myVal;}  This effectively makes the external member a class method, but is it simply being defined externally, for readability.

 

Other functions can also gain access to a class’s members by declaring their prototype as a friend within the class.

 

This goes the same for operator methods, which in general can be coded in this manner. However, some operators can only be defined as being class members, that is belonging to the class using the scope resolution operator ::, since they require an lvalue as the first operand, those being:

 

=, [], (), ->

 

new, delete, new[], delete[]
It has also been suggested that it is better to declare methods as friends, when the operator does not modify the operands, as it tries to make the code more readable by using explicitly listed parameters (as opposed to implicit or by the use of the this self reference pointer object).

 

  • If a unary operator is overloaded using a member function, it takes no arguments. If it is overloaded using a global function, it takes one argument.
  • If a binary operator is overloaded using a member function, it takes one argument. If it is overloaded using a global function, it takes two arguments.

Another aspect of declaring a method with class scope that is that it utilises the (generally considered hidden) this self referencing pointer object. Thus, the following two code snippets achieve similar functionality but use different techniques to do so:

 

using member function using friend function
#include <iostream>
using namespace std; 

 

 

class Speed {

private:

int turbo ;

public:

Speed(int mph = 165) : turbo(mph) { }

Speed operator+(int) ;

int getVal(){return turbo;}

} ;

 

//member function, using scope resolution ::
Speed Speed::operator +(int a) {

return Speed(this->turbo + a);

}

 

int main() {

Speed a ;
Speed b = a + 25 ;

cout << “Turbo speed: ” << b.getVal() << endl ;

return 0 ;

}

#include <iostream> 

using namespace std;

 

 

class Speed {

private:

int turbo ;

public:

Speed(int mph = 165) : turbo(mph) { }

friend Speed operator+(const Speed &, int);

int getVal(){return turbo;}

} ;

 

//friend function, NOT a member function
Speed operator+(const Speed &faster, int a) {

return Speed(faster.turbo + a) ;

}

 

int main() {

Speed a ;
Speed b = a + 25 ;

cout << “Turbo speed: ” << b.getVal() << endl ;

return 0 ;

}

Compile & Run:

Turbo speed: 190

 

Notice how the member function only requires one parameter since it is being self referenced using the this pointer. Whereas, the friend function requires both the calling object and the integer value as its parameters.

Overloading ++ & — operators

There are two version of the increment and decrement operators, those being prefix and postfix:

 

  • increment
    • prefix: increment the value, then use it
      • ++a
    • postfix: use the value, then increment it
      • a++
  • decrement
    • prefix: increment the value, then use it
      • --a
    • postfix: use the value, then increment it
      • a--

Since there are two types of the same operator (pre/post fix) C++ introduced a hack to enable the compiler to determine which version is required.

 

The prefix versions take no parameters.

 

The postfix versions take a dummy int that is not used.

 

Their prototypes are typically as follows:

  • MyClass & operator++() ;  //prefix
  • const MyClass & operator++(int) ;  //postfix
  • MyClass & operator--() ;  //prefix
  • const MyClass & operator--(int) ;  //postfix

 

We can tell the difference between the prefix and the postfix, since the postfix operators have a dummy int in their signatures. Since the dummy is not  used or given a name, the compiler knows to treat this as a placeholder and therefore not bother with warnings that it has been declared but not used.

 

Both forms of the methods perform the same functionality and we can see that the postfix versions actually call the prefix versions to carry out the actual increment/decrement, but the only difference is in the return values. Specifically, the postfix versions return the original value that has been temporarily stored whilst the prefix inc/dec method is called to actually carry out the inc/dec and the original value is then returned, thus performing a postfix inc/dec.

 

In the example below, lines 3 and 7 of the output show that the prefix versions of the overloaded operators are called:

Compile & Run:

increment before PREfix 6, increment after PREfix 7
increment before POSTfix 7
increment before PREfix 7, increment after PREfix 8
increment after POSTfix 8
decrement before PREfix 8, decrement after PREfix 7
increment before POSTfix 7
decrement before PREfix 7, decrement after PREfix 6
increment after POSTfix 6

<< & >> stream operator overloading

To read and write classes to and from streams (input / output), overloaded methods are required for the stream insertion << and stream extraction >> operators.

 

The << and >> are usually shit left and shift right bitwise operators that are overloaded in the ostream and istream classes of the iostream file, that is usually included at the beginning of a program with the following statement:    #include <iostream>

 

When used for output, the << operator is known as the insertion operator.

When used for input, the >> operator is known as the extraction operator.

 

These operators act as insertion / extraction operators when cout / cin is used on the left of the statement as the driving object for their relative implementations within the ostream or istream class of the iostream file.

 

Overloading << Output

cout is actually an object of the ostream class, which has function prototypes for the builtin data types such as int, float as follows:

 

ostream & operator<<(ostream&, int) ;

ostream & operator<<(ostream&, float) ;

 

As you can see they both return a reference to ostream, which is implemented in the function body by returning the next address for output to allow chained statements of the form: cout << objectA << objectB << objectC << endl ; The left operand should always be returned when we want overloaded binary operator to be chained.

 

They both also accept two parameters, those being a reference to ostream and the relevant data type.

 

Similarly, we can write our own overloaded << operator functions to act upon our classes.

 

A typical overloaded << function prototype would look like this:

 

friend ostream& operator<<(ostream&, const MyClass&) ;

 

The prototype is declared as a friend function to allow the function to access members of the class, and since it takes an ostream as its first parameter it can’t be a member of the class.

 

The second parameter is defined as a const to ensure that the function does not alter the incoming object.

 

Because the function has to be defined as a friend and not a member function, the class name and scope resolution operator :: are not required. However, this necessitates the use of the class name and the dot operator for the relevant members required. This also means that the function cannot use the self referencing ‘this’ pointer, and cannot directly refer member data just by using their attribute name.

 

This example illustrates overloading the insertion stream operator >> to print the values of a class:

Compile & Run:

myVar: 9, myFlt: 4.567, myStr: David
myVar: 6, myFlt: 6.6, myStr: Diablo

 


 

Overloading >> Input

 

The extraction operator >>  can also be overloaded for input, using the istream class of the iostream file, and has a similar prototype to the above:

 

friend istream& operator>>(istream&, MyClass&) ; 

 

Again the prototype is declared as a friend to allow access to  members of the class (in this case to set the values) and taking an istream as its first parameter means it can’t be a member of the class.

 

Returning a reference allows chaining.

 

please enter an int: 5
please enter a float: 21.45
please enter a string: Jimmy
myVar: 5, myFlt: 21.45, myStr: Jimmy

Child Initialisation

A child class inherits members from its parent which it needs to initialise, as per any other class.

 

The preferred style of setting the attributes is to use an initialisation list, as can be seen in the parent class on line 11 in the example below.

 

The child also utilises an initialisation list, as per line 25. In this case, the first four attributes of the child object are of the same (inherited) type as the parent and therefore the parent class is utilised to initialise these values. This can be seen in the first parameter of the initialisation list: Motorbike(a,b,c,d), the remaining attributes for the child class are then initialised in the usual manner:

Compile & Run:

Make: Honda, Model: Fireblade, Speed: 190mph, cc: 1000
Make: Ducati, Model: Panigale, Speed: 185mph, cc: 1198, Track: Silverstone, Position: 2

Multiple Files

Breaking programs down into multiple files enables the principle of “doing one task and doing it well” (aka the Unix philosophy).

 

This principle focuses on the creation of developing smaller, modular and more manageable code with well defined interfaces. Any issues local to that code should then be more easily addressed, whilst also enabling replacement/upgrade for newer versions to provide new features, and thus saving time/effort debugging problems on long/complex/unreadable code.

 

Modularity also allows specialisms to be utilised by more than one program (the ‘why re-invent’ rule) as can be seen in the STL.

 

Compilation times are improved by only having to compile files that have changed. Imagine if a program consisted of a single file with thousands of lines and took say 15 minutes to compile, only for you to remember a simple change and then have to recompile everything again.

 

The main program file has a forward declared prototype:

 

Implementation of the forward declared prototype in a separate file:

 

When compiled in an IDE, it should automatically find and compile both files for the program.

 

To compile from the command line, simply include the names of the files to be compiled with the desired output file name as follows:

 

g++ main.cpp multiply.cpp -o multiplier

 

*remember to add a .exe extension for windows environments.

Header Files

  • Contain declarations to be used in .cpp source code files
  • end with a .h extension
  • aka include file
  • Consist of:
    • Include (/ Header) Guard, which consists of conditional compilation directives:
      • #ifndef header_name
      • #define header_name
      • … code definitions …
      • #endif
  • Code Body

 

Example header file:

 

To use a header file, it has to be included by the preprocessor keyword #include followed by the name of the required header file:

#include <iostream>

#include myHeader.h

 

Angle brackets indicate files that come with the compiler/OS, whereas self created header files are included by stating their file name within double quotes.

 

Best practice

  • Use UPPERCASE for the header identifier
  • Always use include guards, to ensure the compiler doesn’t try to duplicate a header file that has already been included in another file
  • Generally should only be used for declarations. Leave the implementations in .cpp files
  • Make them as specific as possible, just concentrating on one aspect

 

Compilation process:

  1. The preprocessor strips out all comments and replaces all code specified by the #include with copies of the actual code within those files
    1. The output is in the form of preprocessed source code, which is sent to the standard output
    2. The following command stops after the preprocessing stage and does not run the compiler proper:
      1. g++ -E main.cpp
  2. The compiler converts the source code to assembler code
    1. The following command stops after the compilation stage:
      1. g++ -S main.cpp
      2. creates a file with a .s extension containing assembly code
  3. The assembler converts the assembly code into object code
    1. The following command stops after the assembler stage:
      1. g++ -c main.cpp
      2. creates a file with a .o extension containing object code
  4. The linker converts all object and required linked files to produce an executable / binary file

 

The following command takes two .cpp files, and an included header file (specified via its #include statement):

g++ main.cpp multiply.cpp -o multiply

 

 

 

Composition

The concept of building (more complex) objects from other objects is known as composition.

 

Smaller specialised objects are created that perform one specific task and are then used as members of the complex object.

 

Composition creates objects that have the ‘Has-A’ relationship, such as a motorbike has an engine, or a plane has a wing, or a pub has a bar.

 

Composition also implies ownership, since when the complex object is created so are the subclasses, and accordingly die when the complex object dies.

 

 

Here we have an (RaceTeam) object called owner that uses another (Championship) object to keep track of the points accumulated:

Compile & Run:

What team do you ride for?
Honda
Honda has 0 points.
How many points did you get in the last race (-1 to exit) :
20
Honda has 20 points.
How many points did you get in the last race (-1 to exit) :
25
Honda has 45 points.
How many points did you get in the last race (-1 to exit) :
25
Honda has 70 points.
How many points did you get in the last race (-1 to exit) : 
-1
Thanks!

Aggregation

Type of composition where no ownership is implied.

 

Thus (sub) objects are independent of each other, and are not destroyed when the aggregate object is destroyed.

 

Aggregate objects contain pointers or references to other objects, created outside of the class. When the aggregate object is destroyed and their pointers or references to the sub-objects goes out of scope, the sub-objects continue to exist.

 

Composition is creating a Person object, that gets destroyed when composition object goes out of scope

 

Whereas aggregation is creating a Person object that still exists when aggregation goes out of scope.

 

In this example, a sub-object is created (on line 34) independently of any other class. It is then used (line 38) in the constructor parameter when creating the aggregate object. When the aggregate object goes out of scope (line 39), the sub-object continues to exist as can be seen on line 40, until it is deleted on line 42:

Compile & Run:

Person Created: Sharon
Sharon Lives!
Sharon got a job with MegaCorp!
Sharon got the sack from MegaCorp!!!
Sharon Lives!
Person Destroyed: Sharon

Function Pointers

Allow functions to be assigned (and therefore reassigned) to pointers, assuming the signature following the pointer is the same.

 

int (*funcPtr) (int) ;

 

In the above example, funcPtr is a function pointer that returns an int, takes an int parameter and can point to any function that matches this signature.

 

The pointer should be enclosed within parentheses to ensure it is recognised as a pointer, due to the rules of operator precedence whereby parentheses are of higher precedence than *.

 

int (*funcPtr)() = funcOne ;  //declares a pointer that points to funcOne -- Notice NO parentheses being used on the function being pointed to!

 

In this case, funcPtr now points to the funcOne function and can be reassigned to point to another function, as long as it uses the same signature.

 

In this example a function pointer has been created that points to functions that take one parameter in their signature:

Compile & Run:

9
35

Constant Pointers & Pointers to Constants

The use of the keyword const when working with pointers is the same as when it’s used at any other time: you don’t want the value to change! It is that simple. It was named const for a reason.

 

We (should) already know that a pointer is assigned the address of the target using the & address of operator:

 

int myInt = 42 ;

int *myPtr = &myInt ;

 

So, by making specific expressions const in the statement, we are stating that we do not want these values to change.

 

Here’s three examples:

 

The first declares a constant int pointer. Thus stating that the value being pointed at cannot be changed:

const int *myPtr = &myInt ;

 

The second declares a constant pointer to an int. Thus the location stored on the pointer cannot change the address of what is being pointed at:

int * const myPtr = &myInt ;

 

Finally, this example declares a pointer to an int where both the value being pointed to and its address cannot change:

const int * const myPtr = &myInt ;

 

 

Basically, the placement of the keyword const determines what is being declared as a constant, the value, the address or both!

 


 

Const correctness, makes it clear that you do not want an object to change.

 

Good idea to create reference parameters const:

 

return_datatype funcName(const myClass& objectA){

…code body…

}

Dynamic Memory

When programs are created with variables of a pre determined / fixed size, the overall program has a fixed size and uses a specific amount of memory at run time.

 

However when variable sizes are unknown, say for multiple objects being created or for user input (e.g. text form entry), it is not possible to pre-determine the memory requirements as, for example, we will not know how many objects will be created or how many characters a user may enter into a text form. We therefore need a method to create (and delete) memory ‘on the fly’, at run time.

 

The technique to allocate new storage at run time is known as Dynamic Memory management, and utilises the operator keywords, new and delete to allocate and deallocate storage, as required.

 

It is very important to ensure that storage is deallocated using the operator keyword delete, in order to stop memory leaks!

 

Storage is dynamically allocated using the operator keyword new followed by the data type, which returns a (void) pointer to address space on the heap.

 

For example, int *ptr = new int  ; declares an int pointer, which will be assigned the address returned by new at runtime. We can then assign values to the dereferenced *ptr as follows: *ptr = 555 ;  //just as an example.

 

Once used, we delete the storage space using the operator keyword delete as follows: delete ptr ;

 

Compile & Run:

Please enter an integer:
63
Please enter another integer:
49
63
0x3f1130
49
0x3f1140

 


 

 

Similarly, the operator keywords new[ ] and delete[ ] are used to allocate/deallocate storage for arrays:

Compile & Run:

Constructor called!
Constructor called!
Constructor called!
0x711134
0x711134
0x711135
0x711136
Destructor called!
Destructor called!
Destructor called!

Inheritance construction & destruction order

A Child class derived from a Parent can be thought of as consisting of two parts: a Parent from which it inherits the Parent’s members and its own Child part which has its own members.

 

The inherited Parent of a Child class is constructed before the Child.

 

Thus multiple inherited classes have the most senior class constructed before its child classes are constructed and so on down the chain of inheritance.

 

Similarly, a class’s destructors destroy each part of a Child’s derived classes, starting from the Child’s own top level before destroying its inherited parts.

 

For example, a Child that is derived from a Parent that is derived from a Grand-Parent, will firstly have the Grand-Parent constructed, then the Parent before finally constructing the Child. Destruction starts with the Child, then the Parent and finally the Grand-Parent:

Compile & Run:

Instantiating one:
Constructing 1 

 

Instantiating two:
Constructing 1
Constructing 2

 

Instantiating three
Constructing 1
Constructing 2
Constructing 3

 

Destroying 3
Destroying 2
Destroying 1

 

Destroying 2
Destroying 1

 

Destroying 1

Inherited members

A Child class inherits members of the Parent according to the Access Specifiers: public, private, protected

 

The Child can also call the constructor of the Parent in its initialisation list, as per line 3 (any required values should be included within the parentheses -- if the Parent constructor requires these values):

 

Similarly, the Child inherits the attribute identifiers, but not the values, of the Parent and can then utilise its own methods upon these attributes, say, to set its own values for these inherited fields.

 

This example shows the Child utilising the Parent’s protected string name, which it then uses within its own method on line 26, which can be called as per line 37:

Compile & Run:

Instantiating Parent
Constructing Parent attributes for Bill 

 

Instantiating Child
Constructing Parent attributes for Ted
Constructing Child attributes for Ted
Ted had its Parent’s attributes constructed before its own

 

Objects will now be destroyed
Destroying Child attributes for Ted
Destroying Parent attributes for Ted

 

Destroying Parent attributes for Bill

 

 

Overriding

Allows inherited methods to be redefined.

 

Prototype of the Child must have the same signature as the Parent.

 

In this example the Parent and the Child have the same method theFunc() (on lines 12 and 22 respectively) which is being implemented differently in the Child, on line 23: