Object Oriented Programming

Table of Contents


c2_u11_Classes_titleimage.png

1. Introduction to Object Oriented Programming

1.1. Programming paradigms

Programming paradigms (pronounced "pair-uh-dimes") are ways we can classify different programming languages based on features they have available or the style programs are written. Different paradigms have popped up over the life of computers as programming languages grow and evolve.

Machine code

At the beginning, computers were programmed with machine code, where you work directly with instructions supported by the hardware - for example, add, sub, storing data in registers, and other relatively simple commands.

Procedural Languages

Eventually, those commands were abstracted into higher-level languages, where one command in, say, C, could "translate" to several machine-code instructions. Languages like C, Fortran, Algol, BASIC, and C are known as Procedural Languages, where these programs would describe a procedure to follow and instructions are executed one-at-a-time in a specific order (top-to-bottom, or calling a function and returning).

Object Oriented Programming

Languages like C supported our basic control flow and using functions, but did not include classes - a way to make more sophisticated data types. A class is a structure that can store its own member variables and member functions. A variable whose data type is from some defined class is known as an object.

There are other programming paradigms, but we are going to focus on Object Oriented Programming (OOP) now since it's a large part of using C++ and other languages like Java, C#, and Python.


1.2. Design ideals

In programming, an object is some sort of structure that stores its own data (variables) and functionality (functions). We try to design our programs as a collection of objects that interact with each other to get some job done.

Designing programs in an OOP style helps us pursue certain design goals:

  • Abstraction: Hiding complexity in the design, creating an "interface" for the user, generalizing functionality to a more "digestible" form. Think of a button on any device: If you open up the device, there's a lot actually going on, but we just see the simple button and don't have to worry about the rest.
    • Certain functions are made public that other programmers can use to interface with the object.
    • The other programmers don't need to worry about the inner-workings of the object in order to use it.
    • The developer of the class can modify how the internals work without breaking the public interface.
    • Helps protect the data within the class from being accessed or modified by things it shouldn't.
  • Encapsulation: Also goes along with creating an "interface", though in this regard we are encapsulating related pieces together. We can store data (variables) with the functions that operate on that data (functions) all within a class object.
  • Loose coupling: Ideally, different objects in a program shouldn't have their functionality tied to other objects too closely; we want to reduce inter-dependence between objects. When objects are more independent from each other, they are loosely coupled.
  • High cohesion: When we design our objects, we shouldn't just throw everything and the kitchen sink into one object. To design an object with high cohesion means that everything inside the object belongs in that object. Reduce the clutter.

1.3. Example: Moving to an OOP design

Let's think about a simple program we can make with just functions and arrays for contrast: A library of the movies a user owns.

For this program, we could start with an array of strings to store movie titles, but we'd have to create new arrays for each additional field to keep track of - year, genre, rating, etc.

string movieTitles[MAX_MOVIES];
string movieRatings[MAX_MOVIES];
string movieGenre[MAX_MOVIES];
int movieYearReleased[MAX_MOVIES];

In addition to our arrays, we need functions to deal with different operations we would want to do on our movie collection…

void AddMovie(
string movieTitles[], string movieRatings[],
string movieGenre[], int movieYears[],
int& savedMovies, const int MAX_MOVIES );
void UpdateMovie(
string movieTitles[], string movieRatings[],
string movieGenre[], int movieYears[],
int& savedMovies );
Cons of this style:
  • We have to add logic in our program to make sure that item \(i\) in movieTitles and item \(i\) in =movieRatings will refer to the same movie. There's nothing in C++ that will directly link these things so we have to add the logic ourselves.
  • Have to keep track of four different arrays and have to pass four different arrays between functions.

Instead, we can take this general structure and turn it into an object, like this:

Movie  
- title : string
- rating : string
- genre : string
- year : int
+ Setup( ... ) : void
+ Update( ... ) : void
+ Display() : void

The Movie object would store information about the movie and a set of functionality we would do on a movie. During the "Add Movie" feature of the program, we would be calling the Setup(...) function of the Movie object to get its information all set up.

In addition to our Movie object, we could also build an object that is the library itself - what data and functionality does our library of movies have?

MovieLibrary  
- movieList : Movie[]
- totalMovies : int
- MAX_MOVIES : const int
+ AddMovie() : void
+ UpdateMovie() : void
+ ClearMovie() : void
+ DisplayAllMovies() : void
+ SaveList( path: string ) : void
+ LoadList( path: string ) : void

Our movie library would contain one array - an array of Movie objects - instead of the four separate arrays for each of the movie fields. The library itself would have the Add, Update, Clear, etc. functions and know how to handle that functionality.

Finally, in our main() function we would declare a MovieLibrary variable for our single library - or, we could make an array of MovieLibrary objects if the program accomodated multiple users!

After initialization, we would have our main menu as usual…

1. Add movie
2. Update movie
(etc)

and when a choice is made, we call the appropriate function via our MovieLibrary object.

Creating the MovieLibrary object:

int main()
{
// Creating a MovieLibrary object
MovieLibrary movieLibrary;

Calling the AddMovie function of MovieLibrary:

if ( choice == 1 )
{
    // Calling the AddMovie function
    movieLibrary.AddMovie();
}

When defining the AddMovie() function, it could call the Setup() function for the next Movie object in the array…

#+BEGIN_SRC cpp :class cpp
void MovieLibrary::AddMovie()
{
movieList[ totalMovies ].Setup();
totalMovies++;
}

And the Movie object's Setup() function could handle dealing with the inputs and outputs…

void Movie::Setup() {
cout << "Enter a title: ":
getline( cin, title );

cout << "Enter a rating: ";
getline( cin, rating );

cout << "Enter a genre: ";
getline( cin, genre );

cout << "Enter a year: ";
cin >> year;
}

Here's a diagram of the main program and objects:

c2_u11_Classes_LibraryDiagram.png


1.4. Additional OOP examples

Here are a few more examples of using object oriented design in different types of programs:

Video game

c2_u11_Classes_Game.png

A video game usually has characters that can move around, a lot of 2D game levels are built out of a grid of tiles.

Anything that gets displayed to the screen will need \((x,y)\) coordinates as well as dimensions like width and height. C++ doesn't have a built-in image object, but there are graphical libraries that provide graphics functionality.

Player  
- x : int
- y : int
- width : int
- height : int
- image : Texture
+ Setup() : void
+ Move(...) : void
+ BeginJump(...) : void
+ BeginAttack(...) : void
+ Draw(...) : void
Tile  
- x : int
- y : int
- width : int
- height : int
- image : Texture
+ Setup() : void
+ Draw(...) : void
Level  
- tiles : Tile[][]
- worldWidth : int
- worldHeight : int
+ LoadLevel() : void
+ DrawLevel() : void

College

c2_u11_Classes_Campus.png

A college could be separated into multiple different objects, where a College might have different Campuses (such as KU-Lawrence, KU-Edwards or the different MCCKC campuses), and each campus has an array of different Departments (CSIS, MATH, ENG, etc.), each department has an array of Teachers and an array of Courses. A course would generally have one Teacher and an array of Students.

As far as functionality goes, this diagram has only some basic functions, like SetTeacher for a course, or =AddStudent=h. The idea is that, anything related to a course would go in the Course object, with each object handling its own little domain.


2. Structs

2.1. Structs vs. Classes

Structs in C++ have all the same functionality of a Class, but are generally used design-wise to group a few variables together, maybe some functions, into a simple structure. A class, on the other hand, is usually used for much bigger and more complex objects.

In C++, the only difference between Structs and Classes are default accessibility - if you don't specify the accessibility of your variables/functions within a struct, they are public by default. This means that anything in the program can access those variables/functions. For Classes, everything is private by default - only that Class itself can use the variables/functions.


2.2. Declaring a struct

Struct declarations go in header (.h) files. Usually the file name should reflect the name of the struct itself, and the file guard labels should also match.

#ifndef _STRUCTNAME
#define _STRUCTNAME

struct STRUCTNAME
{
  int var;
  void Func();
};

#endif

Design-wise, structs should only contain a small amount of data. While structs can contain functions, it is generally better to create a class if you find yourself needing functions.

An example of a small struct would be grouping \(x\) and \(y\) coordinates together in a Coordinate Pair…

struct CoordinatePair
{
    float x, y;
};

Sometimes we need to group some basic variables together. Structs are great for this.

struct Rectangle
{
    float left, right, top, bottom;
};

2.3. Declaring object variables

Once a struct has been declared in a program, you can then create a variable with that data type. To access the internal variables of the struct, you use the variable's name followed by the dot operator . and then the name of the member variable.

// Declare two variables
CoordinatePair point1, point2;

cout << "Enter the x y coordinates for the first point: ";
cin >> point1.x >> point1.y;

cout << "Enter the x y coordinates for the second point: ";
cin >> point2.x >> point2.y;

float slope = ( point2.y - point1.y ) / ( point2.x - point1.x );
cout << "The slope is: " << slope << endl;

We will have more examples during the section on Classes, since that's mostly what we will be using. Again, structs are useful for combining a small amount of member variables together under one name, usually used for structures for math (coordinates, rectangles, etc.) but more sophisticated objects ought to be created with a class.


3. Classes

Traditionally, a struct is used to create small objects that join a few variables together. Classes are much more heavily used in C++ and is the back-bone of Object Oriented Programming. There is a lot we can do with classes, but for now we are just going to look at the basics.

A class declaration looks just like a struct declaration except that we use the keyword class. Take note that with a struct and class declaration, we must end the closing curly brace with a semi-colon.

class Player
{
    public:
    void SetPosition( int newX, int newY );
    void Move();

    private:
    int x, y;
};

3.1. Accessibility

We can define our member variables and functions with three different levels of accessibility, dictating where these members can be accessed throughout the program:

public:
Any part of the program can access these members. This is usually used for the member functions of a class.
private:
These members can only be accessed by the class itself, from within its functions.
protected:
Similar to private except that classes that inherit from the class we're defining will also have access to protected members. More on this when we cover inheritance.

3.2. Header and Source files

When we are creating a class, we generally will put the class declaration within its own header file. Header files end with .h or .hpp - I tend to use .hpp since we're using C++ and I like to explicitly state this is a "C++ header"; the .h extension was also used in C. However, the C++ Core Guidelines document says to

NL.27: Use a .cpp suffix for code files and .h for interface files

Reason It's a longstanding convention. But consistency is more important, so if your project uses something else, follow that.

(From https://github.com/isocpp/CppCoreGuidelines/blob/master/CppCoreGuidelines.md#reason-451)

Class declaration goes in Rectangle.hpp (or Rectangle.h):

#ifndef _RECTANGLE_HPP
#define _RECTANGLE_HPP

class Rectangle
{
    public:
    void SetPosition( int newX, int newY );
    void SetDimensions( int newWidth, int newHeight );
    private:
    int x, int y, int width, int height;
};

#endif

Context: What is #ifndef?

In C++, when we #include "Rectangle.hpp" in a different source file, the compiler essentially copy-pastes the code from the included file into the includer file.

Because of this, if multiple files are #include "Rectangle.hpp"-ing (or any other header), the same code in that header gets copied multiple times, making the compiler think you've declared the same class over and over.

#ifndef, #define, and #endif are preprocessor commands used by the compiler. We are essentially saying

"if-not-defined _RECTANGLE_HPP, define _RECTANGLE_HPP, … … end-if.",

preventing the compiler from copying the same file twice.

Class function definitions go in Rectangle.cpp:

#include "Rectangle.hpp"

void Rectangle::SetPosition( int newX, int newY )
{
    x = newX;
    y = newY;
}

void Rectangle::SetDimensions( int newWidth,
                            int newHeight )
{
    width = newWidth;
    height = newHeight;
}

In our source file (.cpp), we need to make sure to include the header that goes with our class so it knows about the class declaration. Then, we can define the member functions here.

Note that when we're defining the functions outside of the class declaration, we must prefix the function name with the class name, followed by the scope resolution operator ::

void Rectangle::SetPosition( int newX, int newY )

This is the standard way C++ files are organized - for each class we create, we create a header and a source file for it.

Defining member functions inside the class declaration

It is completely possible to define our member functions inside the class declaration, getting rid of the need for the .cpp file. However, this does end up being treated differently by the compiler - writing our class this way makes the functions inline… Basically, instead of having a separate function that the compiler will mark as "call this", it will copy the contents of the inlined function to the function call, basically replacing the call with the contents of the function.

I haven't written much about how the compiler works since that's a more specialized topic and we don't need to worry about it at this stage of learning C++. This is just here for your own info.

The entire class in Rectangle.hpp:

#ifndef _RECTANGLE_HPP
#define _RECTANGLE_HPP

class Rectangle
{
    public:
    void SetPosition( int newX, int newY )
    {
        x = newX;
        y = newY;
    }

    void SetDimensions( int newWidth, int newHeight )
    {
        width = newWidth;
        height = newHeight;
    }

    private:
        int x, int y, int width, int height;
};

#endif

This is also closer to how Java and C# files look, defining all their methods within the class declaration. It also does simplify having to go back and edit functions, not having to keep track of two different places where we have the function declaration and the function definition.

Should you define functions in the .hpp or the .cpp?

In general, for my courses, you can choose either approach. Starter code I provide will have the .hpp and .cpp files for classes, but when writing your own class you can throw it all in the .hpp file if you'd like.

The main thing is that it's important to be aware of the standard way so that when you're working with other people you understand how things are structured.

Function vs. Method

A method is a word that means "member function". In Java and C#, the term method is used instead of function, since you physically cannot have functions defined outside of a class in those languages.

I tend to stick with the term "member function", but if I write "method", it means that, whereas a "function" would be a standalone function elseware in the program.


3.3. Getters and Setters

In Object Oriented Programming, we generally want to hide the inner-workings of a class from the outside world (other functions and other classes). If everything were exposed, then other parts of the program could make changes to our class and it could be difficult to track down all those modifying locations (this, in particular, is a nightmare at a job with a large codebase).

We generally write all our member variables as private - accessible only to the class' member functions themseves - and use public member functions to interface with those variables as needed.

For example, in our Rectangle class, we might have a function to set up the entire Rectangle all at once…

void Rectangle::Setup( int newX, int newY, int newWidth, int newHeight )
{
    x = newX;
    y = newY;
    width = newWidth;
    height = newHeight;
}

… or just a couple things at a time …

void Rectangle::SetPosition( int newX, int newY )
{
    x = newX;
    y = newY;
}
Setters:
Or, we might want to just set one member variable's value.

In this case, we write a member function called a setter (aka mutator) that is responsible for setting a member variable's value:

void Rectangle::SetX( int newX )
{
    x = newX;
}

The good thing about having setters instead of directly updating the x variable is that we can add error checks in our setter. Say, perhaps, that we don't want x to be negative, so we validate the input before changing it…

void Rectangle::SetX( int newX )
{
    if ( newX >= 0 )
    {
        x = newX;
    }
}

If somewhere else in the program tries to set a negative value for the x variable, it will just ignore that command. (We could also throw an exception or display an error message or set a default value - it's up to your design.)

Getters:
Likewise, sometimes we want to access

those member variables to see what values they store. In this case, we write Getter member functions that are responsible for returning a copy of the data (or, in some cases, a reference to it - but that's a design decision).

int Rectangle::GetX()
{
    return x;
}

Getters are also handy for formatting output prior to returning it, or doing some other operation before returning something. Let's say we have a member function for a Student class, and a GetName function is going to combine their names and return it together:

int Student::GetFullName()
{
    return lastName + ", " + firstName;
}

To summarize…

  • Setters: Sets the value of a member variable. Generally, return type is void and there is one parameter for the new value. void SetThing( int newValue );
  • Getters: Returns the value of a member variable. Generally, return type matches that variable, and there are no parameters. =int GetThing(); =

3.4. Constructors and Destructors

Constructors and Destructors are special types of member functions in a class.

  • Constructor:
    • Runs automatically as soon as a new object is declared.
    • Can declare more than one constructor.
    • No return type
    • Constructor name \underline{must} match name of the class.
  • Destructor:
    • Runs automatically as soon as a new object is destroyed.
    • Can only have one destructor.
    • No return type
    • Destructor name \underline=must= match name of the class, prefixed with a tilde \~.
Example: Text file wrapper

Let's say we're writing a class that works with a text file.

When the object is created, the constructor will open the file and get it ready to write to. With various member functions we can write to that text file as the program is running. Then, when the program ends and the object is destroyed, it will automatically close the text file for us.

Class declaration:

class Logger
{
    public:
    // constructors
    Logger( string filename );
    Logger();

    // destructor
    ~Logger();

    // other method
    void Write( string text );

    private:
    ofstream m_output;
};

We have two constructors: one where we pass in a filename for a file to open, and one where we don't. In the second case, we can use a default file name.

Context: Why "m_" with the variable?

In some places, it is standard to prefix private member variables with an underscore (_output) or (m_output). I tend to use the latter when writing private member variables of a class. The "m" stands for "member".

The constructor definitions could look like this:

Logger::Logger( string filename )
{
    m_output.open( filename );
}

Logger::Logger()
{
    m_output.open( "log.txt" );
}

The Write function could be used to write information to that text file, and could be implemented like this:

void Logger::Write( string text )
{
    m_output << text << endl;
}

And then the destructor would be used to close up the file at the end:

Logger::~Logger()
{
    m_output.close();
}

Now, we don't have to manually deal with file operations since this class wraps that functionality and deals with it for us.

To use the program, we just declare the object variable:

#include "Logger.hpp"

int main()
{
    // At this point,
    // a constructor is called automatically
    Logger log( "logfile.txt" );

    // Writing to the text file
    log.Write( "Hello!" );
    log.Write( "How are you?" );

    // Program ends here. The destructor
    // is called automatically when the log variable
    // goes out of scope and is destroyed.
    return 0;
}

It can often be handy to overload our constructors so that we can initialize our objects in several different ways.

3.4.1. Default Constructors

A default constructor is a constructor with no parameters in it. This will be called when a variable of this class type is declared with no constructor explicitly called.

Usually, a default constructor would be used to initialize variables to default values, and if your class contains pointers, this constructor should initialize those pointers to point to nullptr.

class MyClass
{
public:
MyClass()       // Default constructor
{
    m_value = 0;
}

private:
    int m_value;
};

When we create a new object of this type like this, the default constructor will be called:

MyClass classVar;

3.4.2. Parameterized Constructors

Parameterized Constructors take in one or more parameters to help initialize the member variables of the object. We can have 0, one, or multiple parameterized constructors for our class.

class MyClass
{
    public:
    MyClass() {       // Default constructor
        m_value = 0;
    }

    MyClass( int value ) {  // Parameterized constructor
        m_value = value;
    }

    // etc
};

When we instantiate our object, we can pass in argument(s) in order to call the parameterized version of the constructor:

MyClass classVar( 100 );

If you declare a parameterized constructor but not a default constructor, then the C++ compiler will assume that you don't want an object to be declared with the default constructor. This means, you would only be able to declare the variable with an explicit call to the constructor and with arguments.

If this isn't the desired design, make sure to include a default constructor, even if you just leave it blank.

3.4.3. Copy Constructors

A copy constructor takes in another object of the same type as its parameter and uses this to copy over members from the parameter to the new object.

class MyClass
{
    public:
    MyClass()       // Default constructor
    {
        m_value = 0;
    }

    MyClass( int value )  // Parameterized constructor
    {
        m_value = value;
    }

    MyClass( const MyClass& other ) // Copy constructor
    {
        m_value = other.m_value;
    }

    private:
    int m_value;
};

In this case, we might already have an object of this type available, and when we are creating a new object, we want to make a copy of the old object:

MyClass classVar( 100 );
MyClass anotherOne( classVar );   // Copy
Design:
When a class has multiple member variables, it is up to us to decide which variables get copied over during this operation. There could be some design cases where all information is copied over except certain fields (maybe a unique customerID or a name).
Default copy constructor:
If you don't explicitly declare a copy constructor, the C++ compiler will provide one for the class behind-the-scenes. This implicit copy constructor will only do shallow copies.

Ways to copy:

  • A Shallow Copy is where values of variables are copied over. This is generally fine for any sort of non-pointer-based variables. If the class contains a pointer that is pointing to some address, the shallow-copy of the pointer will point to the same address.
  • A Deep Copy is where values are copied over like with a shallow copy, but also will allocate new memory for a dynamic array (if the class has one) in order to copy over values of the element of the array to the new class copy.

c2_u11_Classes_ShallowCopy.png

Example of a shallow copy: With the implicit copy constructor, any pointers in the copied version will be pointing to the same address as in the original. If the class contains a dynamic array, both the copy and the original will end up pointing to the same address of the array in memory.

c2_u11_Classes_DeepCopy.png

Example of a deep copy: A new block of memory has been allocated for the int * numArr. The values from InstanceA's numArr would be copied to InstanceB's numArr via a for loop during construction.


3.5. Example program: House Builder

This program allows a user to enter information about multiple rooms in a house and at the end it will output a text file with all the room information.

Example output:

HOUSE BUILDER
(A)dd new room or (Q)uit: a
Enter room name: livingroom
Enter width: 10
Enter length: 20

(A)dd new room or (Q)uit: a
Enter room name: kitchen
Enter width: 10
Enter length: 15

(A)dd new room or (Q)uit: a
Enter room name: bathroom
Enter width: 6
Enter length: 10

(A)dd new room or (Q)uit: q

Outputted to house.txt

In my design, I have two objects: A Room, which contains a room name, width, and length, and a House, which contains an array of rooms. In the main() function, we work directly with the House, and the house takes care of dealing with the Rooms.

Room.hpp:
#ifndef _ROOM_HPP
#define _ROOM_HPP

#include <string>
using namespace std;

class Room
{
    public:
    void Setup();

    string GetName();
    int GetWidth();
    int GetLength();
    int GetArea();

    private:
    string name;
    int width;
    int length;
};

#endif

We have getter functions for the name, width, and length of the room, as well as to get the area. We don't need to store the area because we can calculate it any time we need it.

There is also a function Setup() that deals with the cins and couts to set up the room's information.

Room.cpp:
#include "Room.hpp"

#include <iostream>
using namespace std;

void Room::Setup()
{
    cout << "Enter room name: ";
    cin >> name;

    cout << "Enter width: ";
    cin >> width;

    cout << "Enter length: ";
    cin >> length;
}

string Room::GetName()
{
    return name;
}

int Room::GetWidth()
{
    return width;
}

int Room::GetLength()
{
    return length;
}

int Room::GetArea()
{
    return width * length;
}
House.hpp:
#ifndef _HOUSE_HPP
#define _HOUSE_HPP

#include "Room.hpp"

const int MAX_ROOMS = 10;

class House
{
    public:
    House();
    ~House();

    void AddRoom();

    private:
    Room rooms[MAX_ROOMS];
    int roomCount;
};

#endif

The House class contains a constructor, which will be used to initialize data automatically. In particular, we want roomCount to be set to 0.

The destructor will automatically open a text file, output all the rooms' information, and then close the text file when the House object is destroyed at the end of the program.

House.cpp:
#include "House.hpp"

#include <iostream>
#include <fstream>
using namespace std;

House::House()
{
    roomCount = 0;
}

House::~House()
{
    ofstream output( "house.txt" );
    for ( int i = 0; i < roomCount; i++ )
    {
        output << rooms[i].GetName() << "\t"
                << rooms[i].GetWidth() << "x"
                << rooms[i].GetLength() << "\t"
                << "(" << rooms[i].GetArea() << " sqft)"
                << endl;
    }
    output.close();

    cout << "Outputted to house.txt" << endl;
}

void House::AddRoom()
{
    if ( roomCount == MAX_ROOMS )
    {
        cout << "House is full!" << endl
             << "Cannot fit any more rooms!" << endl;
        return; // exit this function
    }

    rooms[ roomCount ].Setup();
    roomCount++;
}
main.cpp:
#include <iostream>
using namespace std;

#include "House.hpp"

int main()
{
    House myHouse;

    cout << "HOUSE BUILDER" << endl;
    bool done = false;
    while ( !done )
    {
        cout << "(A)dd new room or (Q)uit: ";
        char choice;
        cin >> choice;

        if ( toupper(choice) == 'A' )
        {
            myHouse.AddRoom();
        }
        else if ( toupper(choice) == 'Q' )
        {
            done = true;
        }
        cout << endl;
    }

    return 0;
}

This program only deals with one house, but we could have an array of Houses, each with their own separate array of Rooms.


4. Additional class concepts

4.1. Const member methods

When we declare a class' member function as const, we are saying that this function should not ever change any values of any member variables of this class. This can be handy for methods like a Display() where we just want to output member variables but should never change them.

class Coordinates
{
public:
int GetX() const; // Can't change m_x or m_y
int GetY() const; // Can't change m_x or m_y

void SetX( int val );
void SetY( int val );

private:
int m_x, m_y;
};

The function definition will also need to have this const marked at the end of the function header as well.

int Coordinates::GetX() const 
{
return m_x;
}

4.2. this

Within our class methods, we can explicitly refer to the object we are currently working with as this. this is a pointer, so it is pointing to a memory address. Any member of the class can also be accessed via the this pointer:

class MyClass
{
public:
  void A()
  {
    cout << "Hello!" << endl;
  }

  void B()
  {
    this->A();
    cout << this->var << endl;
  }

  void C()
  {
    A();
    cout << var << endl;
  }

private:
  int var;
};

In this example, methods B() and C() do the same thing, but B() explicitly uses this.


Author: Rachel Wil Sha Singh

Created: 2023-10-05 Thu 12:40

Validate