C++ Page #2: Object-Oriented Programming


There's strictly no warranty for the correctness of this text. You use any of the information provided here at your own risk.


Contents:

  1. Creating Classes in C++
  2. "Lamp"-Class Examples
  3. Pointers to Objects
  4. Inheritance
  5. Further Reading


1. Creating Classes in C++

What object oriented programming (programming with classes and objects) is about, I've already explained for Perl on my site "Object-Oriented Programming in Perl Made Easy". In Python, classes and objects are defined and used in a rather similar way, it's even a little easier there, as I've explained here.
In C++, it's actually not much different either.

In a class, there's a special function called a "constructor". It is called, when an object is instantiated. In Python this special function is called "__init__". In C++, constructors have the same name as the class itself.
When you want to use a constructor that doesn't expect any arguments, you call the constructor at instantiation without any arguments - and also without any brackets. That's a rather unusual function-call.

The code inside a class is divided by two special lines:

private:

and

public:

The attributes and methods in the "private" part can only be accessed from inside the class. While the code in the "public" part can also be accessed from other classes or functions.
If for example you try to access a private variable of a class directly from the function "main()" of the program (that is from outside the class), you get an error at compilation time.

I'm giving the special lines "private:" and "public:" an indentation level of two characters, so that the declarations of attributes and methods inside the class can start at the ordinary indentation level of four characters. But I don't know, if that's common to do.

As the code inside the class is by default private, unless it is written below the "public:" statement, the statement "private:" can also be left out. But I suggest writing it anyway to make things clearer.
The "private:" section can also be left empty, so this code is valid and compiles:

class Test {
  private:
    // pass
  public:
    Test() {
    }
};

int main() {
    Test t = Test();
    return 0;
}

The constructor function (which has the same name as the class itself) should be in the "public"-part of the class (like it is in the example above).

When a class in C++ is defined, it automatically receives a special pointer called "this". The attributes and methods of the class are normally defined and used without this pointer. But they can also be accessed through it when necessary. For example, an "int"-variable called "a" can be accessed inside the class like this:

this->a

The attributes can be declared either in the "private"-part or in the "public"-part, depending on whether they should be accessed from outside the class or not.

The constructor function (which is in the "public"-part of the class) can also receive ordinary function arguments. The values of these arguments can then be assigned to the attributes.

Just like you'd do for example in Python:

#!/usr/bin/python
# coding: utf-8

class Test:
    def __init__(self, argument):
        self.attribute = argument

t = Test("a_test")
print t.attribute

Or in Perl:

#!/usr/bin/perl

use warnings;
use strict;

package Test {
   sub new {
        my $classname = shift;
        my $self = {};
        my $argument = shift;
        $self->{attribute} = $argument;
        return bless($self, $classname);
    }
}
my $t = Test->new("a_test");
print $t->{attribute} . "\n";

In C++, you'd do:

#include <iostream>

using namespace std;

class Test {
  private:
    // pass
  public:
    string attribute;
    Test(string argument) {
        attribute = argument;
    }
};
int main() {
    Test t("a_test");
    cout << t.attribute << endl;
    return 0;
}

The syntaxes may be a bit different, but the principles and the results are rather similar. Though C++-code may run quite a bit faster.

There has to be a semicolon after the closing bracket of the class-definiton (similar to struct-definitions in C).

Notice, that in Python, variables that are supposed to be visible throughout the class, are usually defined in the "__init__"-function using the syntax

self.variable = ...

while in C++, these variables have to be declared outside the constructor function, directly under the keywords "private" or "public".
Inside the class' methods, they can then just be used, without writing "this->variable" all the time. They are recognized as class-wide variables, because local variables would have been declared in the method itself or in the methods's parameter list. So the syntax "this->variable" is only needed, when in rare cases the class-wide variables need to be distinguished from local variables.


2. "Lamp"-Class Examples

Here are the first of two OOP-examples on my Perl-page (example number 2 and 3), now translated to C++:

#include <iostream>

using namespace std;

// OOP-example 2

class Lamp {

  private:
    string name;
    string state;
    int lightintensity;

  public:

    Lamp(string name, int light) {
        // To distinguish between the attribute and the function parameter:
        this->name = name;
        lightintensity = light;
        state = "off";
    }

    void switchOn() {
        state = "on";
        cout << "'" << name << "' is on at " << lightintensity << " Watt." << endl;
    }

    void switchOff() {
        state = "off";
        cout << "'" << name << "' is off." << endl;
    }

    void newLightBulb(int light) {
        if (state == "on") {
            cout << "Light bulb can not be changed. ";
            cout << "First, '" << name << "' has to be switched off." << endl;
        } else {
            lightintensity = light;
            cout << "Light bulb in '" << name << "' has been changed. ";
            cout << "The new bulb has " << light << " Watt." << endl;
            switchOn();
        }
    }
};

int main() {
    Lamp lamp1("First Lamp", 50);
    Lamp lamp2("Second Lamp", 40);
    lamp1.switchOn();
    lamp2.switchOn();
    lamp2.newLightBulb(100);
    lamp2.switchOff();
    lamp2.newLightBulb(100);
    return 0;
}

And the second one:

#include <iostream>

using namespace std;

// OOP-example 3

class Lamp { 

  private:
    int lightintensity;
    string name;

  public: 

    Lamp(int lightintensity = 40, string name = "Default lamp") {
        this->lightintensity = lightintensity;
        this->name = name;
    }

    void showLightIntensity() {
        cout << "'" << name << "' has a light bulb with ";
        cout << lightintensity << " Watt." << endl;
    }
};

int main() {
    Lamp lamp1(11, "1");
    lamp1.showLightIntensity();
    // This is a weird function call (no brackets):
    Lamp lamp2;
    lamp2.showLightIntensity();
    return 0;
}


3. Pointers to Objects

You can also set a pointer to an existing object:

Lamp *pLamp = &lamp1;

It is also possible to instantiate an object directly with a pointer. Then, the keyword "new" has to used:

MyClass *p = new MyClass();

In this line of code, the "new" first allocates the memory required for the object on the heap. Then the constructor of the class is called. Finally the pointer is pointed to the object's memory location.

The attributes and methods of the object can then be accessed through the pointer with the "->" operator (instead of "."). Here's an example:

#include <iostream>

using namespace std;

class MyClass { 

  public: 
    int a;

    MyClass() {
        a = 5;
    }

    void printHello() {
        cout << "Hello from class." << endl;
    }
};

int main() {
    MyClass *p = new MyClass();
    cout << p->a << endl;
    p->printHello();
    return 0;
}

This technique is for example used when creating new widget objects with the GUI-library "wxWidgets".

The discussion about the keyword "new" here says, using it allocates memory on the heap, while not using it in many cases allocates memory on the stack. And the pointer is of course useful, when the objects are meant to be passed around functions by reference.
It also says, it is a general rule to call "delete" after having used "new" earlier.
Calling "delete" on the pointer may not be necessary for wxWidgets-objects, because the classes of the library may already take care of freeing the memory in the end (when calling "Close()" on the "wxFrame"-object).


4. Inheritance

If a subclass should inherit from a superclass, the following syntax can be used. The constructor of the subclass also has to call the constructor of the superclass:

class Subclass : public Superclass {
  public:
    // Constructor:
    Subclass([parameter-list of Subclass' constructor]) : Superclass {[arguments for Superclass' constructor]} {
    }
};

Here I translated the little inheritance-example from the Perl-OOP-page mentioned above:

#include <iostream>

using namespace std;

class Animal {
  public:
    string name;
    Animal(string animalname) {
        this->name = animalname;
    }
    void startMoving() {
        cout << name + " is walking." << endl;
    }
    void makeNoise() {
        cout << "Woof! Woof!" << endl;
    }
};

class Cat : public Animal {

  public:

    Cat(string catname) : Animal {catname} {
    }

    void showName() {
        cout << name << endl;
    }
    void makeNoise() {
        cout << "Meow!" << endl;
    }
};

int main() {
    Cat c = Cat("Tibby");
    c.showName();
    c.startMoving();
    c.makeNoise();
    return 0;
}


5. Further Reading

The last page deals with special topics.



Back to the computing-page


Author: hlubenow2 {at-symbol} gmx.net