Code with Mosh in CPP Part Three
What is OOP
Program paradigm - Style of programming
- Procedural
- Functional
- Object-Oriented
- Event-Driven
Functional and OOP is the two most popular ones nowadays
Object - A software entity that has attributes and functions
- attributes - properties
- function - methods
Class - A class is a blueprint or recipe for creating objects
We can represent a classs using vision language called UML - which is short for Unified Modelling Language
In UML, we use a box with three sections, on the top we have the class name, in the middle section we list attributes or variables that holds data, at bottom section we list functions. These variables and functions are called members of the class
Structure is more about data, Class is more about DATA+BEHAVIOR. This is one of the core principles of OOP called Encapsulation (Combining the data and functions that operate on the data into one unite)
An object is an instance of class
In UML we call the data part attributes, in C++ we call them member variable, in other languages we call them fields or properties. All of those terminology is the same - a variable inside a class
Methods = Member functions
Define a Class
Use Clion’s feature of creating a C++ class will automatically create a Classname.h headerfile and a Classname .cpp file with same name which is linked. We can also do these thing manually.
In the headerfile, we are going to define the features of class. We are gonna include this headerfile later
we have header guard in header files to prevent this headerfile being included multiple time in the compilation process
1 |
|
- We use double quotation marks to include header files from out own project while using angle bracket to include headers from standard library
- We only do function declaration or aka function prototype in header files, the actual implementation is gonna go our cpp files
- In cpp files we qualify the function by its class name using double colon
::which is called scope resolution operator
1 |
|
Creating Objects
We are not gonna include .cpp file of class implementation, we only #include "ClassName.h" because
The reason we have two files per class is to reduce compilation time, our main file main.cpp is independent from the Rectangle.h
When we do change to ClassName.cpp, only itself will be recompiled. but if we do change to ClassName.h then every files that is depend on the ClassName.h like #include "ClassName.h" will be recomipled.
An object is an intance of a class, we can access the member of this object by dot operator like
1 | ClassName object; |
Compilation error will told us we can not access a private member of a class outside of the class. This is the main difference of classes and structures.
- In structures, when we declare members they are always public or accessible by default
- In classes, when we declare members, they are always private or inaccessible by default
In order to make them public, we have to use keyword public by a colon : before all those public members declaration
1 | class Rectangle{ |
To compile a C++ project with a header file (e.g., ClassName.h), an implementation file (e.g., ClassName.cpp), and a main file (e.g., main.cpp), we can compile all files together into an executable using g++:
1 | g++ -o myProgram main.cpp ClassName.cpp |
The sequence of main.cpp and ClassName.cpp in the command does not matter for the compilation process itself. The g++ compiler processes all the source files listed (main.cpp and ClassName.cpp) and links them together into the executable (myProgram), regardless of the order in which they appear on the command line. Here’s why:
We can also compile each .cpp file into an object file first, then link them:
1 | g++ -c ClassName.cpp -o ClassName.o # Compile ClassName.cpp to object file |
-c: Compile without linking.-o: Specify output file name.
Access Modifier
Data hiding principle of OOP saves invalid value assignment - A class should hide its internal data from the outside code and provide functions for accessing the data.
public private protected are access modifiers , as the best practice we add public section first then add private section
Getters and Setters
To achieve Data Hiding principle, by making them private, for each varibale we need two functions accessing one to get value one to set value.
Getter is also called accessor
Setter is also called mutator (to mutate something means to change something)
this is the pointer to current object, we need to de-reference it before we use dot operator to access the actual object, pointer is always just address
Lots of ppl use m_ M Underlined prefixed to differenciate the parameter and member variable with same name
1 | /* |
We can also use ClassName followed by double colon to access current object’s private member variable like Rectangle::width = width, it is another equivalent
- Plus sign in UML means
public - Hyphen sign in UML means
private
Mosh’s implementation of a TextBox class suggested that
- When we pass a string parameter to fucntion, it is always better to pass it as a reference parameter like
void setValue(string& value);, value are not copied across function calls, this is for performance reasons - Also it is better to declare the parameter a constant
void setValue(const string& value);, avoiding accidental modification onvalue
Constructors
Constructor is a special function inside class that initialising object, that could avoid the invalid calling of getter by properly do assignment to private variables at first.
Consturctor doesn’t have return type, not even void, their name is exactly the same as the ClassName
1 | class Rectangle{ |
Using curely braces to initialise objects
1 | int main(){ |
Member Initialiser List
In modern C plus plus, our compiler clearly know the left widthis our member variable, the width inside curely braces is paramter, we can do the same for height after comma
1 | Rectangle::Rectangle(int width,int height):width{width},height{height}{ |
A member initialiser list - techinique is slightly effiecient, but doesn’t give us access to do validations
The approach is not exclusive with the tradition approach that do the initialisation inside of the body
The Default Constructor
A default constructor is a constructor with no parameters
Just like functions we can always overload constructors, we can have two constructors with different signature
In modern C++ we can tell our compiler to generate a default compiler for us in ClassName.h like this
1 | class ClassName{ |
Instead of do implementation in ClassName.cpp like this
1 | ClassName::ClassName(){ |
C plus plus compiler generator a default constructor for every class, that is the reason why we can create class and object without out manually and explicitly defined constructors.
Compiler stops generating a default constructor for us until we declare a non-default constructor, that is why we can’t use the ClassName ObjectName; to declare an object in main program without giving it parameters.
Using the Explicit Keyword
Single-argument constructors must be marked explicit to avoid unintentional implicit conversion
That is how implicit conversion happens
1 | class Person{ |
In main.cpp :
1 |
|
After declare our single-argument construct with explicit keyword, we can’t pass int to showPerson() anymore. Because after that, our compiler will force us to pass a proper Person object to the function. If we apply explicit keyword, there is no longer a “Convertin Constructor”
1 | class Person{ |
Constructor Delegation
A constructort can delegate the initialisation of an object to another constructor. With this feature, we can remove duplicate code.
Given that Rectangle.h
1 | class Rectangle{ |
We have the Rectangle.cpp
1 |
|
- Call another constructor by using
:after the curent constructor’s implementation signature, passing all the paramters it should take - There is no limitation of delegation times, but too much constructor leads to unmaintainable code
The Copy Constructor
Copy constructor is another special constructor, which is used for copying objects.
1 | int main(){ |
As for second line Rectangle second = first; , the reason coyping works is because under the hood the compiler automatically generates a copy constructor in the rectangle class . This copy constructor takes source object, and then it copy all the values in the source objects to initialise the second object.
This automatical work of copy constructor works pretty well all the time, but sometimes there are situations we need to have control of how objects are copied
Explicitly declare a copy constructor in the header file
1 | Rectangle(const Rectangle& source); |
In the implementation Rectangle.cpp
1 | Rectangle::Rectangle(const Rectangle& source){ |
It’s always best to rely on the copy constructor that the compiler generated for us, because when we have more members added into the class, we have to add more lines to copy more object’s variable members manually.
- If we pass object directly to a function as parameter, it is gonna be copied
- Use reference we pass the source object to the function
- Every time we pass object as parameter, a copy constructor will be called
If we go to implementation file and remove the explicitly created copy constructor then go to header file telling our compiler to delete the copy constructor for us
1 | Rectangle(const Rectangle& source) = delete; |
With that we can validate that every time we pass object directly as parameter, copy constuctor will be called, we explicitly deleted the copy constructor then all the function with direct object pass can’t work
The Destructor
Destructor are automatically called when our object are being destroyed and this is an opportunity for us to free system resources that an object is using. So if we allocate memory or open a file or network connection, then we need to release these resources in destrcutors
In our header file we type a ~ followed by the name of the class, similar to constructor, our destructor doesn’t have a return type and the name is exactly the class name : ~Rectangle(); We can not overload destructors. Each class can have a maximum of one destructor.
1 | Rectangle::~Rectangle(){ |
In main program we have a Rectangle object, this object is declared on stack. So when the main function finishes the execution. This object is going to go out of scope. So it will be destroyed, At that point, the destructor will be called.
1 | int main(){ |
Static Members
All the functions and variables we declared so far is are what we called instance member, this member belong to instances of the Rectangle class, each instance is gonna have its own copies of these members. We can also declare members that belong to the Rectangle class itself, so we will have only single copy of this member in memory. And this single copy will be shared by all instances. We call it static members
1 | class Rectangle{ |
- Whenever we declare a static variable, we should always define it in our implementation
.cppfile
1 | int Rectangle::objectCount = 0; |
We access static variable member by scope resolution operator and class name
ClassName::staticVariableif it is in public sectionBut better practice is put in private section and use line break to separate it from instance members, then define a getter for it
1
2
3
4
5
6
7
8
9
10
11static int getObjectCount();
// In header file we need static keyword, in implementation file we remove it
int ClassName::getObjectCount(){
return objectCount;
}
int main(){
// Call it by ClassName
cout << Rectangle::getObjectCount() << endl;
return 0;
}
Constant Objects and Functions
Just like we can declare a constant integer, we can also declare a constant object
1 | int main(){ |
- We can’t modify the attributes of a constant object - They’re read only and immutable
- When we add the keyword
constat the end of a function declaration, we are telling the compiler that in this function we’re not gonna change the state of this object - It’s the best practice that put the
constkeyword at the end of function declaration if it doesn’t change the state of object - Compiler doesn’t allow us to use function without
constkeyword for constant object, because it think there may be some risk that object will be changed in those non-constant functions
1 | // Syntax for constant function in header file |
A function to get static member, which is static member, we can still call them when we use a constant object without declare it withconst. But we should always call it use the ClassName::staticMemberGetter
1 | int main(){ |
The reason we can still access it is due to static member is shared by all objects, we can not change it.
Pointer to Objects
All the object we have created so far is on the stack - which is a part of memory that automatically cleaned when our object go outside the scope. When the main function finishes execution, the object we created in the main function will go out of scope, the destrcutor of object’s class get called, the memory allocated to this object will be freed automatically.
Object created on stack is useful when they are local to a function, so we don’t need them outside the function. But sometimes we need an object to stay in memory when the function finishes its execution. In this case we need create object on the heap use the new operator
1 | int main(){ |
new operator returns a pointer, in this case Rectangle object pointer, so we store it in a Rectangle pointer.
Because it is a pointer, so we can access by arrow operator -> or dot operator. after dereferencing.
We can rewrite with smart pointer
1 |
|
1 |
|
then implementation
1 |
|
main.cpp
1 |
|
- Use new operator + DataType, we get a return of DataType Pointer, then we use it to initialise the object of SmartPointer Class.
- Use curely braces to initialise the object
Array of Objects
We can create array of Objects just like any other data type.
There is two ways to generate an array of object
1 | Rectangle recArray[3] = {Rectangle(10,20),Rectangle(10,20,"blue"),Rectangle()}; |
Here we explicitly call the constructors
1 | Rectangle recArray[3] = { {10,20},{10,20,"blue"},{}}; |
We can also use the brace initialiser, because compiler know each element of array is an instance of Rectangle object, we directly put the brace initialiser in body of array declaration without giving the data type.
Overloading Equaliser Operator
Overloading Comparison Operator
- Title: Code with Mosh in CPP Part Three
- Author: Ricardo Pu
- Created at : 2025-04-07 01:19:56
- Updated at : 2025-04-12 21:28:09
- Link: https://ricardopotter.github.io/RicardoBlog/2025/04/07/Code-with-Mosh-in-CPP-Part-Three/
- License: This work is licensed under CC BY-NC-SA 4.0.