Array of objects in C++

In this tutorial from our C++ e-book, we will learn how to create an array of objects, as well as how to manipulate them.

Creating an array with Objects in C++

In the past tutorial, we created a class that averaged two or three grades for students. We will do something similar in this tutorial.

Let's create the Student class, and initially it gets two grades. The constructor function correctly sets variables, and has a function called getAverage() that returns the average of the two grades:

#include <iostream>
using namespace std;
class Student
{
    private:
        double grade1, grade2;

    public:
        Student();
        Student(double, double);
        double getAverage();
};

Student::Student()
{
    grade1 = 0;
    grade2 = 0;
}
double Student::getAverage() { return (grade1+grade2)/2; } int main() { Student NeilPeart(10, 8); cout<<NeilPeart.getAverage()<<endl; return 0; }

Note that we created only one object, the student Neil Peart. But which school has only one student? There are thousands, but let's take it easy. We will now work with 5 students. We could do:

  • Student alu1, alu2, alu3, alu4, alu5;

But, what if it was a real project, with a thousand students? Would you do it up to the alu1000?
Of course not, right, buddy!

You studied by Progressive C++, you are a smart person and you will soon remember our beloved and beautiful arrays, to work with many variables at the same time.

To declare 5 integers, we do:

  • int num[5];

To declare 5 chars, we do:

  • char letter[5];

So guess what we do to create 5 objects of the Student class? That's right:

  • Student alu[5];

Array of Objects

Run the code to the code below:

#include <iostream>
using namespace std;

class Student
{
    private:
        double grade1, grade2;

    public:
        Student();
        Student(double, double);
        double getAverage();
};

Student::Student()
{
    grade1 = 0;
    grade2 = 0;
}

Student::Student(double g1, double g2)
{
    grade1 = g1;
    grade2 = g2;
}

double Student::getAverage()
{
    return (grade1+grade2)/2;
}


int main()
{
    Student alu[5];
    cout<<alu[0].getAverage()<<endl;

    return 0;
}

The result will be 0, as we don't initialize the array objects.

How to Initialize Objects from an Array

We could initialize the objects one by one, after declaring:

    Student alu[5];
    alu[0] = Student(10,9);
    alu[1] = Student(10,8);
    alu[2] = Student(10,7);
    alu[3] = Student(10,6);
    alu[4] = Student(10,5);

Or we could just initialize everything when instantiating objects:
    Student alu[5] = { Student(10,9),
                       Student(10,8),
                       Student(10,7),
                       Student(10,6),
                       Student(10,5) };
Or even directly:
    Student alu[5] = { {10,9},
                       {10,8},
                       {10,7},
                       {10,6},
                       {10,5} };
If you have other constructor functions (that receive one grade or 3, for example), you can also place them directly in the code, C++ will know how to call the correct constructor. And if you don't provide any information about any element of the array, C++ will invoke the default constructor (it's always good to have one in the class).

Accessing members of the Object Array

Ok, we have already declared and initialized several objects at once, now you need to learn how to access their members.

When we had an object named 'name', we accessed its members using a dot:
  • name.var
  • name.func()

Here's the same thing, but the variable is an array element:
  • arr[0].grade
  • arr[1].getAverage();
That is, the same thing, just use the cute array and the dot, followed by the member of the class / object.
The following program creates 5 objects, initializes with the students' grade and displays their averages with the getAverage() function:
#include <iostream>
using namespace std;

class Student
{
    private:
        double grade1, grade2;

    public:
        Student();
        Student(double, double);
        double getAverage();
};

Student::Student()
{
    grade1 = 0;
    grade2 = 0;
}

Student::Student(double g1, double g2)
{
    grade1 = g1;
    grade2 = g2;
}

double Student::getAverage()
{
    return (grade1+grade2)/2;
}


int main()
{
    Student alu[5] = { {10,9},
                       {10,8},
                       {10,6},
                       {10,5} };


    cout<<alu[0].getAverage()<<endl;
    cout<<alu[1].getAverage()<<endl;
    cout<<alu[2].getAverage()<<endl;
    cout<<alu[3].getAverage()<<endl;
    cout<<alu[4].getAverage()<<endl;

    return 0;
}
Note that we only initialized the first four students, so the fifth one was initialized by the default constructor. What was the average of the fifth student (alu[4])?

Now do another test: create a class, which only has the default constructor, displaying a message on the screen. Create an array of size 10, of these objects. What happens?

Constructor function overloading in C++

In this tutorial from our Progressive C++ course, we will learn about overloading the constructor functions.

Overload constructors in C++


Many constructors functions

You were hired by a university to create a system to help teachers, especially to work with students' grades.

As you took the Progressive C++ course, you will create a fantastic system, with several classes and objects, hardcore.
But let's take it easy.

Let's create a math class, called Math. For simplicity, she'll do just one operation: average two grades, one student, that's all.

The constructor function will tell the student's average right away.

See how our code looks:

#include <iostream>
using namespace std;

class Math
{
    public:
        Math(double, double);
};


Math::Math(double g1, double g2)
{
    cout<<"Average: "<<(g1+g2)/2<<endl;
}


int main()
{
    Math BruceDickinson(10, 8);

    return 0;
}

The result was 9 right, all nice and cool, and student Bruce Dickinson got a great average.

And speaking of market experience, it is SOOOO common for customers to be asking for changes, new features, etc. Get used to it, it is always requests and complaints.

In that case, the university now wants you to average 3 grades.

Time, it's very simple, just add one more parameter to the list of the constructor function, create the object with the 3 notes and send a bullet, that's it, our code looks like this:

#include <iostream>
using namespace std;

class Math
{
    public:
        Math(double, double, double);
};


Math::Math(double g1, double g2, double g3)
{
    cout<<"Average: "<<(g1+g2+g3)/3<<endl;
}


int main()
{
    Math NeilPeart(10, 9, 10);

    return 0;
}

Everything was fine and Neil Peart had an almost perfect average.

But then your university get in contact and says: look, there are students who will make two disciplines and students who will make three, that is, their system has to calculate the average in both cases.

And now? How will the constructor guess? One hour calculates the average by dividing by 2 and in the other example he divided by 3 because he had one more variable.

Create a class for each case? Of course not, as this is a complication.

Programming was made to make the world easier! This is where builder overloading comes in.


Constructors overloading in C++

C++ has a card up its sleeve: it allows you to create as many constructors as you want. That's right, functions with the same name.
But one thing has to be different: the list of parameters.

Example of a list of different parameters:

  • Math (double, double)
  • Math (double, int)
  • Math (int, double)
  • Math (double, double, double, float, char, int)
  • ...

So, in our example of averages, just create two constructor functions, one that receives two grades and the other that receives three grades:

#include <iostream>
using namespace std;

class Math
{
    public:
        Math(double, double);
        Math(double, double, double);
};

Math::Math(double g1, double g2)
{
    cout<<"Average: "<<(g1+g2)/2<<endl;
}

Math::Math(double g1, double g2, double g3)
{
    cout<<"Average: "<<(g1+g2+g3)/3<<endl;
}


int main()
{
    Math BruceDickinson(10, 8);
    Math NeilPeart(10, 9, 10);

    return 0;
}

See how smart C++ is. When you created the BruceDickinson object, you only passed two arguments, and the object invoked the constructor function that only works with two numbers.

When he created the ana object, he called the other constructor function, the one that works with three values.

That is, C++ calls the correct constructor!
This is the constructor overloading.

Now if you have two constructors with the same parameter list, then there is no way for C++ to guess what function you are trying to invoke, okay?

We will learn a little more about overload when we study a super special class, the string, which does crazy things and wonders with overloading.

Desctructor Function in C++

In this tutorial for our C++ course, we will learn about and use destructors functions.

The Destructor function

In the last tutorial, we talked about the Constructor Function, which exists in every class and is executed whenever an object is instantiated.

Similarly, there is the destructor function, which is performed only when the object is destroyed.

To create a destructor function, just define a function with the same name as the class, with the tilde symbol (~) before it. Let's look at an example, creating a class with constructor and destructor:

#include <iostream>
using namespace std;

class Test
{
    public:
        Test();
        ~Test();
};

Test::Test()
{
    cout << "Constructor" << endl;
}

Test::~Test()
{
    cout << "Destructor" << endl;
}

int main()
{
    Test t;

    return 0;
}

The result will be the two words on the screen. One occurs when creating the object and another occurs when the program ends, and in this case the destructor is invoked.

And just like the constructors, every class has a destructor. Even if you don't define one, there will be a blank pattern, but it will always exist.

They also do not accept any arguments or list of parameters and do not return any information.

What is the Destructor Function for?

Basically, to do things that must be done when an object ceases to exist.
A very common example is to free up memory that has been allocated within an object.

At the moment, we are using silly, small objects, but they are usually giant, with classes with hundreds of variables and dozens of functions. And inside, it is common to dynamically allocate memory, and when that object ceases to exist, it is a good practice to free up all that memory (especially in more critical systems with little memory, such as your watch or your refrigerator's digital system).

Let's suppose you created a system for the bank, and you always want to do a test: if someone accessed the Bank class, instantiating an object. To do this, you define a global scope variable called "spy" with an initial value of 0.

To check if any objects were created, make 'spy = 1' in the destructor, see:

#include <iostream>
using namespace std;

int spy=0;

class Bank
{
    public:
        ~Bank();
};


Bank::~Bank()
{
    spy = 1;
}

int main()
{
    Bank *b;
    b = new Bank;

    delete b;

    cout<<"Spy: " << spy << endl;
    return 0;
}

Let's suppose it is a giant class, with several members, several things happening ... you created an object, used it, did everything correctly and such. Then it is time to delete it, and at that time the destructor function will be executed.

The result will be 'Spy: 1' there in the main(), indicating that someone messed with that Bank class. That is, the destructor, since it is always executed, will execute the operation 'spy = 1'.

Another example of a destructor function is if you create a game and have a character (which is an object) and he simply dies or leaves the game. In your destructor function, you delete the name, take your points, take it out of the teams, take it out of the ranking.

And etc etc, around there people, almost always have things to do when an object ceases to exist, ok?

Constructors Functions in C++ - Object Oriented

In this tutorial, we will learn about a special type of function, the Constructor function, which plays a very important role in object orientation.

The C++ Constructor function

There is a very important function and used in object oriented, it is the construction function.
And it has some unique and special features.

The first is that the name is the same as the class name. That is, if your class is called "MyClass", the constructor function must be called "MyClass()", mandatorily, ok?

Another characteristic of it is that, as soon as we instantiate an object, the constructor function is always automatically invoked!

Finally, one last characteristic, it does not return any value! Do not write void, int, doublet etc., anything in your header and implementation.

Let's see a test of a working construction function? Let's create the "MyClass" class that has only one member, the "MyClass()" function, which will simply display a message, it looks like this:

#include <iostream>
using namespace std;

class MyClass
{
    public:
        MyClass();
};

MyClass::MyClass()
{
    cout << "Progressive C++ Course"<<endl;
}

int main()
{
    MyClass c;

    return 0;
}

Prontinho! Apenas criamos o objeto 'c' e veja o que apareceu na tela:

Constructor functions



Construction function: What is it for?

Ok, we've already seen how to do it, how it works and the result ... but what is it for? Just to show some silly messages on the screen?
Of course not, young man!

As the name says, it serves to 'construct' things, in this case, it builds things automatically. When we create an object, it is interesting that it already does some internal operations immediately, without having to always do everything manually.

This type of constructor, we call it default, because it doesn't take any arguments. However, the most common and useful way to use a constructor function is with a list of parameters, which are information that comes from the outside into the object, and will play an important role inside.


Constructor function: How to use?

For example, let's look at our Rect class, which created rectangles. Remember that we always had to use the setter functions to define the length and width values. How about if the builder did this automatically?

See how it looks:

#include <iostream>
using namespace std;

class Rect
{
    private:
        double length, width;

    public:
        Rect(double, double);
        double getArea();
        double getPerimeter();
};

Rect::Rect(double l, double w)
{
    length = l;
    width = w;
}
double Rect::getArea()
{
    return length * width;
}
double Rect::getPerimeter()
{
    return 2*(length+width);
}

int main()
{
    double len, wid;

    cout<<"Length: ";
    cin >> len;

    cout<<"Width: ";
    cin >> wid;

    Rect r(len, wid);

    cout<<"Area: " << r.getArea() << endl;
    cout<<"Perimeter: " << r.getPerimeter() << endl;

    return 0;
}

Note that in the header of the constructor, we have already specified that it will receive two variables of type double.

In the implementation, we call these variables 'l' and 'w', which will be provided later by the user. And what does our constructor function do? It will set the correct values for 'length' and 'width'.

In main, we ask for the length and width values, and then simply instantiate an object of type Rect, named 'r'. Notice now that in it we send two arguments, which are the two arguments that the constructor function will use!

Ready, internally, inside the object, the constructor has already done everything and the functions getArea() and getPerimeter() can now be used.

A little more about the Constructor Function

Another curiosity about constructors is that they ALWAYS exist! Even if you don't define any, C++ goes there and creates one, empty, that does nothing.

When you do:

  • MyClass c;

It will automatically invoke the constructor, but you don't see anything happening, as you haven't defined any constructors.

Another trick you may need and use, is through pointers, declaring a pointer to an object:

  • MyClass * ptrClass;

When doing this, the constructor method will not be executed, since an object was not created, only a pointer to an object.

Now if you do this next:

  • ptrClass = new MyClass;

Then, the object was instantiated and allocated in memory, and the constructor will be summarily executed.

How to create a project with Classes and Objects in C++: Headers and Implementations

In this tutorial from our C++ e-book, we will learn how to organize our classes and function implementations, creating a very professional project.


How to create a C++ project

Open your Code :: Blocks (or any other IDE you are using).
Click File, then New and choose Project.
Choose Console application and check the C ++ option.
Give your project a name, choose the folder you want to save and click Finish,

It will look like this:


How to create a C++ project
Note that the file 'main.cpp' already came, we will use it.
Now, click on that white file up there, saying 'New file', and click on Class. Name this class as 'Calc' as we are going to create a calculator.

Now see how cool:

He created two files: Calc.h and Calc.cpp

Implementing a Class in a C++ Project: .h

Okay, come on, first let's work on the Calc.h file, it's called a header.
Let's go to the outline of our class.

It will have two numbers: num1 and num2, obviously private.
It will have functions of sum, subtraction (sub), multiplication (prod) and division (div), all public and const (they will not be able to change the variables).

Only the setNum1 and setNum2 functions will be able to change the variables.
And ready, our Calc.h class header looks like this:
#ifndef CALC_H
#define CALC_H


class Calc
{
    private:
        double num1, num2;

    public
        double sum() const;
        double sub() const;
        double prod() const;
        double div() const;
        void setNum1(double);
        void setNum2(double);
};

#endif // CALC_H
See that there are some #ifndef, #define, #endif ... do not touch them, they are super important, leave them there and then we explain their meanings.

Implementing Functions of a Project Class: .cpp

It is in this file that we will implement the functions of our Calc.h class

Note that at the very beginning, there is a "#include Calc.h", so this .cpp file is directly related to the .h file, do you understand the magic of it?

Note that there is no 'cout', if it did, we would have to include 'include <iostream>:

In this file, we will implement all the getters and setters functions that were previously presented in the class header. The .cpp file looks like this:
#include "Calc.h"

double Calc::getSum() const
{
    return num1+num2;
}

double Calc::getSub() const
{
    return num1-num2;
}
double Calc::getProd() const
{
    return num1*num2;
}
double Calc::getDiv() const
{
    return num1/num2;
}
void Calc::setNum1(double n1)
{
    num1=n1;
}
void Calc::setNum2(double n2)
{
    num2=n2;
}

Running our C++ project: main.cpp

Now, in main.cpp, the only thing you have to do is to include :#include "Calc.h" up there in the file, and that's it, see how the main went:

#include <iostream>
#include "Calc.h"

using namespace std;

int main()
{
    Calc c;
    double num;

    cout << "First number: ";
    cin >> num;
    c.setNum1(num);

    cout << "Second number: ";
    cin >> num;
    c.setNum2(num);

    cout << "\nResults: "<<endl;
    cout << "Sum: " << c.getSum()<<endl;
    cout << "Substraction: " << c.getSub()<<endl;
    cout << "Product: " << c.getProd()<<endl;
    cout << "Division: " << c.getDiv()<<endl;

}
Now imagine for a moment if we had declared this class and the implementations of the functions all in main.cpp, the mess it would be and the gigantic code.

This way we did, separating by headers (.h) and function implementations (.cpp), everything was more cute, organized and professional, that's how they do it and use it in professional software.

Get used to doing your projects with this organization, okay?

Dynamic allocation of objects with pointers: The new and delete operators

In this tutorial of our Object Orientation Course in C++, we will learn how to use pointers with objects, and see a new way to allocate memory.


Object Pointers

Initially, we will create a class, called Student, which will have two variables (math and english), which will store the student's grades. There will also be a get and set, and a getAverage() call to calculate the average for each student.

Our class, all complete and cute looks like this:

class Student
{
    private:
        double math, english;

    public:
        double getAverage() const;
        void setMath(double);
        void setEnglish(double);
        double getMath() const;
        double getEnglish() const;
};

double Student::getAverage() const
{
    return (getMath() + getEnglish())/2;
}

void Student::setMath(double m)
{
    math = m;
}
void Student::setEnglish(double e)
{
    english = e;
}
double Student::getMath() const
{
    return math;
}
double Student::getEnglish() const
{
    return english;
}

We will now declare a pointer named 'ptr' of type Student:

  • Student *ptrStudent

Now let's actually declare an object of the Student class:

  • Student s1;

We can associate the pointer with the object as follows:

  • ptrStudent = &s1

(don't forget &, remember that pointers store memory addresses, and by placing & before a variable, it gives the address of that variable)


Working with Pointers with Classes and Objects: ->

As we are already studying pointers, we saw that they can work wonders.
For example, in the previous example, for the pointer to access the elements of an object, we must use the operator ->

Let's define two grades for a student:
  • ptrStudent-> setMath (8);
  • ptrStudent-> setEnglish (10);

And to know the average, using a pointer, just do:
  • ptrStudent-> getAverage()

Our complete code is:
#include <iostream>
using namespace std;

class Student
{
    private:
        double math, english;

    public:
        double getAverage() const;
        void setMath(double);
        void setEnglish(double);
        double getMath() const;
        double getEnglish() const;
};

double Student::getAverage() const
{
    return (getMath() + getEnglish())/2;
}

void Student::setMath(double m)
{
    math = m;
}
void Student::setEnglish(double e)
{
    english = e;
}
double Student::getMath() const
{
    return math;
}
double Student::getEnglish() const
{
    return english;
}

int main()
{
    Student *ptrStudent;
    Student s1;

    ptrStudent = &s1;
    
    ptrStudent->setMath(8);
    ptrStudent->setEnglish(10);

    cout << ptrStudent->getAverage()<<endl;

    return 0;
}
Note that we used only one student, we could have created a thousand students and make the pointer point to each one of these students, and the code would always be the same, for example:
Student s1, s2, s3, s4;

Do: ptrStudent = &s1
Then: ptrStudent = &s2
Then: ptrStudent = &s3
Then ptrStudent = &s4

And ready, then just use usr
ptrStudent-> setMath();
ptrStudent-> setEnglish();
ptrStudent-> getAverage()

This avoids writing code for nothing.

Allocating space for objects: new operator

There is a new way to instantiate objects, it is from the new operator.
First you declare your pointer:
  • MyClass *ptr;
At this point, do we have a pointer to point to an object? What object is that? Let's create it now, as follows:
  • ptr = new MyClass

Deleting objects - delete

When we create an object, via normal instantiation, it will always exist and occupy a space in memory. One of the advantages of making dynamic memory allocation, through new, is that we can destroy this pointer, destroying this object and freeing space in memory, for this, just use the delete operator:
  • delete ptr;

And ready, we have a free space on the machine. This may seem silly now, but when you create super complex systems that require maximum efficiency and the least possible use of memory (such as programming a watch or microwave, which has little memory), it is a very important technique.

See the code below, just with a pointer, we fill the grade of two students and calculate their average, and all this just as an object / pointer of the Student class. See how the code looks:

#include <iostream>
using namespace std;

class Student
{
    private:
        double math, english;

    public:
        double getAverage() const;
        void setMath(double);
        void setEnglish(double);
        double getMath() const;
        double getEnglish() const;
};

double Student::getAverage() const
{
    return (getMath() + getEnglish())/2;
}

void Student::setMath(double m)
{
    math = m;
}
void Student::setEnglish(double e)
{
    english = e;
}
double Student::getMath() const
{
    return math;
}
double Student::getEnglish() const
{
    return english;
}

int main()
{
    Student *ptrStudent;
    ptrStudent = new Student;

    ptrStudent->setMath(8);
    ptrStudent->setEnglish(10);
    cout << "Studant average 1: " <<ptrStudent->getAverage()<<endl;

    ptrStudent->setMath(6);
    ptrStudent->setEnglish(7);
    cout << "Studant average 2: " <<ptrStudent->getAverage()<<endl;

    delete ptrStudent;

    return 0;
}