I decided to repeate OOP principles on cpp, and I found a YouTube channel #SimpleCode. I considered themes I'm interested in, wrote and debugged code in these files from this playlist to practice.
Themes covered in file:
- constructors
- copy constructors
- overloaded operators
Description:
Constructors:
We use constructor to initialize object we create with data.
-
In
Humanclass there is a constructor with 3 parameters:Human(int age, int height, string name)
This constructors sets 3 private fields.
-
In
MyClassclass there are 2 parameterized constructors:MyClass(int size)
This constructor sets a size of dynamic array, also the memory for dynamic array allocates here and the array fills by default values.
MyClass(MyClassOther &object)This constructor accepts as a parameter an object of
MyClassOtherclass. -
In
Pointclass there are 3 constructors:Point()Default constructor sets
x_andy_to zeros.Point(int x, int y)
Parameterized constructor sets
x_andy_to acceptedxandy.Point(const Point & other)
This constructor accepts other
Pointobject and sets values of accepted object. -
In
MyClassOtherthere is default constructor:MyClassOther()This constructor sets a value of
10to it's private fieldprivateInfo_and displays an information about does the code running this constructor.
Copy constructors:
We use copy constructors to initialize object we are creating with data of another object of the same class. The copy constructor might be called by 2 ways:
int dataToEnter = 5;
MyClass firstObject(dataToEnter);
MyClass secondObject(firstObject); // Copy constructor is used here
MyClass thirdObject = firstObject; // And here copy constructor too, not assignmentOverloading operators:
Primitive data types like int "knows" how to interact with different operators like >, +, * and so on. But when we create a class with fields of different types compiler don't know what does it mean to add one object of some class to another.
In this file some operators like =, ==, +, !=, [] are overloaded, also operators of postfix and prefix increments are overloaded too:
-
In
MyClassclass the assignment operator is overloaded and it declared like this:// overload assignment operator MyClass & operator = (const MyClass &other)
This method returns
MyClass &- a reference to an object ofMyClassclass. Then we have aoperatorkeyword and operator we want to overload:=in this case.We prescribe some logic in the body of this method and in the end it returns:
return *this;
We should return a reference to an object because the program won't work if we didnt do this in following case:
sixthObject = fifthObject = firstObject; //overloaded operator returns reference to object so we able to do this -
In
Pointclass there are 4 overloaded operators:==,+, postfix and prefix increment:-
The first overloaded operator is
==:bool operator == (const Point &other)
It accepts as a parameter an object of
Pointclass and return abool. -
The second overloaded operator is
+:Point operator + (const Point &other)
We create a new object in the method's body and return it as a result of addition.
Point temp(this->x_ + other.x_, this->y_ + other.y_); return temp;
-
The final block of overloaded operators are overloaded prefix and postfix increments :
Point & operator ++ ()We update some fields here by
this->pointer and returnreturn *this. This is a overloaded prefix increment.The overloaded postfix increment should be considered more attentively:
// postfix form differs from prefix by unused int parameter // we cant return a reference to temp because its lifetime is limited by this block of code Point operator ++ (int value){ // overloaded ++ operator (postfix) Point temp(*this); this->x_++; this->y_++; return temp; }
We create a
tempcopy of*this, make some updates withthis->and return atemp, not reference to atemp! Because lifetime of thistempobject is limited by this method. We should do like that because of priority of postfix incrementation. Also we have a one unused parameterint valuewe should do this to differ overloading of prefix incrementation from postfix.
-
-
In
Humanclass there is an overloaded!=operator but it realize implemented as overloading==described above. -
In
TestIndexingclass there is overloaded[](indexing) operator:int & operator [] (int index){ // we use reference to be able to change the value by index return array_[index]; // without returning reference we can only get a value by index }
It returns a reference
int &and acceptindexwe are point in square brackets when we use it as a parameter.
Themes covered in file:
- friend functions
- removing the implementation of methods outside the class body
- friend class
Description:
Friend functions:
-
In
Pointclass 2 functions arefriendto this class:friend void changeX(Point & pointChange, Test & testChange); // friend function friend void changeY(Point & pointChange); // friend function
These functions are declared and realiazed outside the class, but they can access private fields of classes in which these functions are
friend. -
In
Testclass 1 function isfriendto this class:void changeX(Point & pointChange, Test & testChange)// this function is friend to two classes
Function
changeXis friend toPointandTestin it's body we access to private fields ofPointandTestclasses.
Removing the implementation of methods outside the class body:
In MyClass there is a constructor and print() method - they are declared in class, but their realizations are out of class body and it work like this:
void MyClass::print(){ // method outside the class
cout << "Data is: " << data_ << "\n";
}As an example here the realiztion of print() method outside a class.
Friend class:
The Human class is designated as friend in Apple class this leads us to the conclusion that we can access private fields of object of Apple class in Human method.
void Human::takeApple(Apple &apple){
cout << "Weight: " << apple.weight_ << ", color: " << apple.color_ << "\n";
}As an example: takeApple method is Human class method, but we can access apple.weight_
although weight_ is private in Apple class.
Themes covered in file:
- static fields & static methods
Description:
Static fields & static methods:
In Apple class there is a static field static int appleCount_ this field we need for counting objects of Apple class. The initializing of static field must be done outside the class body.
int Apple::appleCount_ = 0; // static initializing outside a classStatic fields and functions are global for all class and it's objects, we can access it by class namespace like this:
Apple::changeColor(apple2, "red");Here it is the example of static method.
Also we cant reach non-static field in static methods:
static int getAppleCount(){
// weight_ = 0; we cant access to non-static field
return appleCount_;
}But we can do it this way (by receiving an object like a method's parameter):
static void changeColor(Apple &apple, string color){ // we can do like this
apple.color_ = color;
}Themes covered in file:
- inner class
- agregation & composition
Description:
Inner class:
In this file class Pixel inside the Image class (image consist of lots of pixels - by this logic), class Pixel declared in private section of Image, these classes can't access each other private data.
Also in Image there is static field:
static const int LENGTH = 5; // must be static
// we can initialize it here because this field is CONSTANTAnd it initialized here because this field is const despite being static at the same time.
This field sets the size of Pixel array in Image.
Agregation & composition
The example of composition in this file is Brain class in Human class, we create also a Brain object in Human. We can use Brain only with Human class - this is composition. We call brain.think() method in think() method of Human - this is delegation.
The example of aggregation in this file is an object of Cap class, which we both use in Human class and Mannequin class.
Themes covered in file:
- inheritance
- access specifiers in inheritance
Description:
Inheritance:
In this file Human is base class for Student, Professor and also Extramural student inherited from from Student. The inheritance denoted like:
class Professor: public Human // inherited from HumanAccess specifiers in inheritance:
The public keyword before Human means the specification modifier of inheritance, it also could be private and protected. Here is the table of inheritance:
| empty | public | protected | private |
|---|---|---|---|
| public | public | protected | private |
| protected | protected | protected | private |
| private | private | private | private |
The columns mean inheritance type, the rows mean access specifiers and to which type they transform after inheritance.
Themes covered in file:
- polymorphism
- virtual method
- overridden method
In this file there are 3 classes: Gun, MachineGun inherited from Gun and player, which receive a pointer to Gun as a parameter in it's shoot() method.
In this block of code two objects creating of Gun and MachineGun classes, then a shoot() method called for for Gun object the output is Bang! and for MachineGun object the output is Bang! Bang! Bang!:
Gun gun;
gun.shoot(); // Bang!
MachineGun machineGun;
machineGun.shoot(); // Bang! Bang! Bang!But the next block is showing polymorphism itself:
// pointer of base class type may point to the object of base class or to the object of inherited class
Gun gun1;
Gun *weapon1 = &gun1;
weapon1->shoot(); // Bang!
//
MachineGun gun2;
Gun *weapon2 = &gun2;
weapon2->shoot(); // Bang! Bang! Bang!
// called methods are determined by "object" type!In the Gun class the method shoot() is virtual and in the MachineGun class this method is overridden.
But if the shoot() method in Gun class wasn't virtual the output would be another:
/*weapon1->shoot(); // Bang!
weapon2->shoot(); // Bang!*/
// called methods are determined by "pointer" type!We can send to method objects of both class, because shoot() method in Player receive a pointer to Gun and we will see different realisation depends on object type!
Themes covered in file:
- pure virtual methods
- virtual destructors
- pure virtual destructors
Pure virtual methods:
We need pure virtual methods to force inheritor-classes to realize this method.
Virtual destructors:
These destructors are needed for correct memory release like in this block of code:
A *bptr = new B;
delete bptr;
// we only release memory by A destructor, without B destructor
// we should make ~A() - virtual
// we need virtual destructor for a correct memory releasePure virtual destructors:
These destructor don't let us create an object of class where such a destructor exists. But we still need them for correct memory release.
Themes covered in file:
- delegating constructors
- overridden methods
Description:
Delegating constructors:
In Human class there are 3 contructors: 2 of them call other contructor:
Human(string name, int age) : Human(name){ // firstly calling Human(name)
age_ = age;
}
Human(string name, int age, int weight) : Human(name, age){ // firstly calling Human(name)
weight_ = weight;
}These are delegating constructors.
Overridden methods:
In this file there are Msg and Brekets classes: BreketMsg inherited from Msg and in BreketMsg class the getMsg() method is overridden and if we want to call a getMsg() from Msg class we should use it's by Msg namespace:
string getMsg() override{
return "[" + Msg::getMsg() + "]"; // we should directly point to method we want to use
}Themes covered in file:
- plural inheritance
Description:
Plural inheritance:
The order of constructor order deepends on inheritance order:
class FlyingCar : public Car, public Airplane // plural inheritanceThe Car class constructor will be called first.
Also we can use a pointer of both base classes with FlyingCar object:
Car *ptrC = &fc;
Airplane *ptrA = &fc;And if we have in base classes same method we need to lead object to required type:
((Car)fc).use();
((Airplane)fc).use();Themes covered in file:
- interfaces
- diamond-shaped inheritance
Description:
Interfaces:
The example of interface in this file is a IBicycle class with all methods pure virtual.
The SimpleBicycle and SportBicycle classes realize this interface.
The Human's class method rideOn() receive as a parameter a reference to IBicycle class object so it still let us use polymorphism.
Diamond-shaped inheritance:
There are 2 groups of classes which implement the diamond-shaped inheritance: Component, Gpu, Memory, GraphicCard and Character, Orc, Warrior, OrcWarrior:
-
For the first group of classes it's ok to have a
companyNamefield in base class because as we can see inGraphicGardconstructor we can setcompanyNameboth forGpuandMemoryclass, declaringcompanyNamefield only in base class. -
For the second group of classes this feature is destructive for logic because we will have 2
HP(heat points) fields. To solve this problem we can use avirtualkeyword when we inheritOrcandWarriorclasses fromCharacterclass. So the only 1HPfield ramains after this.
Themes covered in file:
- namespaces
- enumerations
Description:
Namespaces:
Namespaces are used to prevent a conflict between functions with same names, so we can wrap code to namespaces and if we want to access any information for exaple classes, functions and so on we need to do this with using namespace. Also we are able to declare one namespace within the other namespace:
namespace firstNM{
void Foo(){
cout << "First foo()\n";
}
}
namespace secondNM{
void Foo(){
cout << "Second foo()\n";
}
}Enumerations:
Enumeration is a structure with constants for better code readability. The examples of enumerations:
enum PcStates{
OFF,
ON,
SLEEP
};
enum Speed{
MIN = 150,
RECOMMEND = 600,
MAX = 800
};Themes covered in file:
- template functions
- template classes
Description:
Template functions:
In this file the sum() function is templated, and it declared like this:
template<class T1, class T2>
T1 sum(T1 a, T2 b)T1 and T2 here means that we can transfer to this function arguments with different types.
Template classes:
-
In this file there are 2 templat eclasses:
TypeSizeandTypeInfo, andTypeInfoinherited fromTypeSize. TheTypeSizeconstructor is called fromTypeInfoclass:TypeInfo(T value) : TypeSize <T> (value){} -
Another example of templated class is
Printerclass, we have a specific realization for the case when we receive an variable ofstringtype:template <class T> class Printer{ public: void print(T value) /*...*/ } // for string: template<> class Printer<string>{ public: void print(string value) /*...*/ }
Themes covered in file:
- smart pointer
- auto_ptr
- unique_ptr
- shared_ptr
Description:
Smart pointer:
In this file the templated SmartPointer class realized.With smart pointer we dont need to manage deleting.
auto_ptr:
We can do like this:
auto_ptr<int> ap1 (new int(1));
auto_ptr<int> ap2 (ap1);After this ap1 becomes NULL and ap2 point to those memory block, where ap1 pointed before.
unique_ptr:
unique_ptr<int> up1(new int(1));
//unique_ptr<int> up2(up1); we cant do like this (UNIQUE pointer)
unique_ptr<int> up2;
//up2 = move(up1);
// when we try to make second pointer, which point to the same memory block, previous pointer lost the way to memory block
up2.swap(up1); // move analogue
int *p = up1.get();
int *p1 = new int(5);
unique_ptr<int> up3(p1);
//up3.reset(); // data overwritten in memory
up3.reset(); // pointer stops point to this memoty blockshared_ptr:
shared_ptr<int> sp1(new int(1));
shared_ptr<int> sp2(sp1);Shared pointer solves the problem of correct memory releasing when several pointers points to the same memory block: the data is deleted when the last shared_poiner deleting.
Themes covered in file:
- linked list implementation
Description:
Linked list implementation:
Linked list is a data structure where the Node cover some data and points to another Node. The Node where linked list starts is head Node. In this file linked list is templated and the Node class is inner class of List class. The methods of List:
-
void push_back(TList data):This method pushes the data into the end of
List. -
void push_front(TList data):This method pushes the data into the beginning of
List. -
void pop_front():This method deleted the first
NodeofList. -
void pop_back():This method deleted the last
NodeofList. -
void insert(TList data, int index):This method inserts the
NodewithdatabyindextoList. -
void removeAt(int index):This method deletes the
NodebyindexfromList. -
void clear():This method clears the entire
Listby deleting allNodes. -
int getSize():This method returns a
Listsize. -
TList& operator[](const int index):This is overloaded [] (indexing) operator.
Themes covered in file:
- STL
- auto keyword
- vector
- iterator
- list
- prefix and postfix increment for iterator
- forward list
- array and compare operators
- deque
- set/multiset
- map/multimap
- stack
- queue
- priority queue
Description:
STL
-
Auto keyword:
With auto keyword compiler can automatically define the type of variable. This keyword is used for those objects, which have a long type, iterators for example:
vector<int>::iterator it = myVector.begin(); // !! auto it2 = myVector.begin(); // !!
We can use auto instead of long type define. Using for primitive worsens the readability of the code.
-
Vector:
Vector is a container for dynamic array with different methods, some of them are considered in file.
-
Iterator:
Iterators are something like smart pointers, they are declaring by container's namespace, each STL data structure have it's own iterator, which can work with it.
Constant iterator don't let make any changes with data by itself.
-
List:
The STL
listis an implementation of doubly linked list. -
Prefix and postfix increment for iterator:
The postfix increment is slower than prefix because of priority of operations, then an iterator increments with postfix incrementation the copy of object is creating, so the operation slows down.
-
Forward list:
The STL
forward_listis an implementation of singly linked list. -
Array and compare operators:
The
arrayin STL is a container for a static array. -
Deque:
The
dequeis a container which can be represented as a list of small dynamic arrays. Accessing is faster than in list but slower than in vector. Inserting is faster than in vector. -
Set/multiset:
The
setimplemented as a binary tree, so all the elements are ordered.setkeeps only unique values, whereasmultiSetmay keep equal values. -
Map/multimap:
The
mapdata structure also is implemented as a binary tree by key and as a pair to key it also keeps a value.mapkeeps only unique values, whereasmultiMapmay keep equal values.
Stack, queue and priority_queue are container adapters they lay down rules on container.
-
Stack:
The
stackis a structure, which guided by the rule of FILO - First In Last Out. On defaultstackusesdeque. Thestackbased onlistdeclaring like this:stack<int, list<int>> st1; // stack based on list
The
emplace()method is faste thanpush()becausepush()methods create a copy of object before moving to the collection whereasemplace()method directly create an object. -
Queue:
The
queuestructure is a structure, which guided by the rule of FIFO - First In First Out. On defaultqueuealso usesdeque. Butvectorcan't be a base of it. -
Priority queue:
The
priority_queueis aqueuewhere all elements are ordered in descending order. Thelistcan't be a base ofpriority_queue.