Classes Part 1.
Requirements:
Linux Distribution
g++
any text editor
My Setup:
Debian 10
g++ version 6.3.0
pluma
In this tutorial, we will be discussing the basics of Classes within C++.
Classes are the blueprints that are used to describe an object within object
oriented programming. Classes define how an object acts as well as its
properties. Each of its properties are governed by what is known as “access
specifiers”. They can be either : private, public, or protected. By default, the
properties of an object are private, unlike structs, whose access specifiers are
public by default. Within C++, structs and classes are treated the same with
that one difference. Structs are considered “POD-types” or Plain-Old-Data that
is, a class (whether defined with the keyword struct or the keyword class)
without constructors, destructors and virtual members functions. To declare a
class, you must use the class keyword as such:
class foo{ private: //private members here public: //public members here };
There is a rule of thumb in C++ called the “Rule of Five”, which is used for
building “exception-safe” code and formalizing rules on resource management.
Originally it was a rule of three, however with C++11 introducing move
semantics, two more items have been added to the rule. This rule claims if one
of the following is involved in a class, all five should be included :
1. Destructor
2. Copy assignment operator
3. Copy constructor
4. Move assignment operator
5. Move constructor
So what exactly does this all mean and how do you implement this? Lets start
out by saying this. Every class that contains member variables should have a
constructor. A constructor is a special function which initializes variables
within the object it is creating. Looking back at the example, lets create a
couple member variables and constructor.
class foo{ private: int _bar; int _baz; public: foo(int bar, int baz):_bar(bar),_baz(baz){} ~foo(){} };
The function which is called the class name, in this example, has two
parameters because we created two private member variables. If these variables
are going to need to be modified externally, we would also add setters, as well
as if it will be read externally, we should create getters as well. The colon
after the function name is called a member initialization list. What it does is
inserts the arguments into the specified variables of the object you are
creating. The ~foo() function is a destructor, which is called when the object
is deallocated . When you work with pointers as member variables, you may need
to deallocate the pointer to free its memory from within the destructor. Let me
give you an example of how I set up my getters and setters:
class foo{ private: int _bar; int _baz; public: foo(int bar, int baz):_bar(bar),_baz(baz){} ~foo(){} int Bar() const {return _bar;} int Baz() const {return _baz;} int getBar() {return bar;} int getBaz() {return baz;} void setBar(int bar){ this->_bar = bar;} void setBaz(int baz){ this->_baz = baz;} };
Within this example, you see two sets of getters, one is used when you are
getting a value of a constant , and the other is when you are getting the value
of a non-constant . The const variables are read only, so modifying them is out
of the question. The set functions manipulate the private variables . They use a
keyword this , which refers to the current instance of the class. Since this
class has a destructor, by rule of five, it should also have the others, so lets
implement those:
class foo{ private: int _bar; int _baz; public: foo(int bar, int baz):_bar(bar),_baz(baz){} // parameterized constructor // copy constructor foo(const foo & other):_bar(other._bar), _baz(other._baz){} //copy assignment operator foo& operator=(const foo & other){ this->_bar = other._bar; //assign the current objects member variables to this->_baz = other._baz; //the copied object return *this; } //move constructor foo(foo && other):_bar(other._bar), _baz(other._baz){ other._bar = 0; //set the other member variables to 0 other._baz = 0; } //move assignment operator foo & operator=(foo && other){ this->_bar = other._bar; //move the other member variables to the current this->_baz = other._baz; //object other._bar = 0; //set the other member variables to 0 other._baz = 0; return *this; } ~foo(){} int Bar() const {return _bar;} int Baz() const {return _baz;} int getBar() {return bar;} int getBaz() {return baz;} void setBar(int bar){ this->_bar = bar;} void setBaz(int baz){ this->_baz = baz;} };