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
Human
class there is a constructor with 3 parameters:Human(int age, int height, string name)
This constructors sets 3 private fields.
-
In
MyClass
class 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
MyClassOther
class. -
In
Point
class there are 3 constructors:Point()
Default constructor sets
x_
andy_
to zeros.Point(int x, int y)
Parameterized constructor sets
x_
andy_
to acceptedx
andy
.Point(const Point & other)
This constructor accepts other
Point
object and sets values of accepted object. -
In
MyClassOther
there is default constructor:MyClassOther()
This constructor sets a value of
10
to 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 assignment
Overloading 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
MyClass
class 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 ofMyClass
class. Then we have aoperator
keyword 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
Point
class 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
Point
class 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
temp
copy of*this
, make some updates withthis->
and return atemp
, not reference to atemp
! Because lifetime of thistemp
object is limited by this method. We should do like that because of priority of postfix incrementation. Also we have a one unused parameterint value
we should do this to differ overloading of prefix incrementation from postfix.
-
-
In
Human
class there is an overloaded!=
operator but it realize implemented as overloading==
described above. -
In
TestIndexing
class 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 acceptindex
we 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
Point
class 2 functions arefriend
to 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
Test
class 1 function isfriend
to this class:void changeX(Point & pointChange, Test & testChange)// this function is friend to two classes
Function
changeX
is friend toPoint
andTest
in it's body we access to private fields ofPoint
andTest
classes.
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 class
Static 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 CONSTANT
And 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 Human
Access 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 release
Pure 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 inheritance
The 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
companyName
field in base class because as we can see inGraphicGard
constructor we can setcompanyName
both forGpu
andMemory
class, declaringcompanyName
field 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 avirtual
keyword when we inheritOrc
andWarrior
classes fromCharacter
class. So the only 1HP
field 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:
TypeSize
andTypeInfo
, andTypeInfo
inherited fromTypeSize
. TheTypeSize
constructor is called fromTypeInfo
class:TypeInfo(T value) : TypeSize <T> (value){}
-
Another example of templated class is
Printer
class, we have a specific realization for the case when we receive an variable ofstring
type: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 block
shared_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
Node
ofList
. -
void pop_back()
:This method deleted the last
Node
ofList
. -
void insert(TList data, int index)
:This method inserts the
Node
withdata
byindex
toList
. -
void removeAt(int index)
:This method deletes the
Node
byindex
fromList
. -
void clear()
:This method clears the entire
List
by deleting allNode
s. -
int getSize()
:This method returns a
List
size. -
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
list
is 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_list
is an implementation of singly linked list. -
Array and compare operators:
The
array
in STL is a container for a static array. -
Deque:
The
deque
is 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
set
implemented as a binary tree, so all the elements are ordered.set
keeps only unique values, whereasmultiSet
may keep equal values. -
Map/multimap:
The
map
data structure also is implemented as a binary tree by key and as a pair to key it also keeps a value.map
keeps only unique values, whereasmultiMap
may keep equal values.
Stack, queue and priority_queue are container adapters they lay down rules on container.
-
Stack:
The
stack
is a structure, which guided by the rule of FILO - First In Last Out. On defaultstack
usesdeque
. Thestack
based onlist
declaring 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
queue
structure is a structure, which guided by the rule of FIFO - First In First Out. On defaultqueue
also usesdeque
. Butvector
can't be a base of it. -
Priority queue:
The
priority_queue
is aqueue
where all elements are ordered in descending order. Thelist
can't be a base ofpriority_queue
.