In this article, I will explain classes, objects, access modifiers, constructors, encapsulation, abstraction, inheritance, and polymorphism in C++. The need for OOP in C++ Hey there, future coders! Ever wondered why everyone keeps talking about OOP when it comes to C++? OOP stands for Object-Oriented Programming. It’s like a set of rules for how to write code. It makes your life easier and your code more organized. Imagine a . A dog can , , and . In OOP, you’d make a class that holds all these actions (called methods). So, next time you want a in your program, you don’t have to write code for , , and again. You just say, “Hey, I got a new ,” and boom! Your new can do all that stuff. dog bark eat sleep Dog dog barking eating sleeping Dog dog So why do you need to know OOP in C++? Keep your code clean: You don’t want your room messy, right? The same goes for code. Easier to work with others: If everyone follows the same rules, no one’s stepping on each other’s toes. Saves time: Write it once, and use it many times. Stick around, and you’ll learn the basics of OOP in no time! Classes and objects In C++, a class is like a blueprint for creating objects. Objects are instances of classes. Here’s a simple class definition for a class: Dog class Dog { public: std::string name; int age; void bark() { std::cout << this->name << " said: Woof! Woof!" << std::endl; } }; Creating an Object By defining a class, you’re essentially creating a new data type. An object is a variable of this custom data type. You can instantiate an object from the class like so: int main(void) { Dog myDog; return (0); } Accessing Members You can access the data members and methods using the operator: . int main(void) { Dog myDog; myDog.name = "Rex"; myDog.age = 5; myDog.bark(); return (0); } : output testing % ./a.out Rex said: Woof! Woof! With the class, you can create many objects, each with its own and or any other parameter that you will add to this class. Dog Dog name age Access modifiers Sometimes you need to protect some variables in your class, and for this, we have access modifiers in cpp, there are three types: : Accessible from anywhere Public : Accessible only within the class Private : Accessible within the class and its derived classes Protected Public Access Modifier Here, is public, so you can access it directly. a class MyClass { public: int a; }; int main(void) { MyClass obj; obj.a = 10; /*Allowed*/ return (0); } Private Access Modifier Here, is private, so you can't access it directly. Use a public function to get it. b class MyClass { private: int b; public: void setB(int val) { b = val; } int getB() { return (b); } }; int main(void) { MyClass obj; obj.setB(20); int b = obj.getB(); /*Allowed*/ obj.b = 3; /*Not allowed, will create error*/ return (0); } Protected Access Modifier Here, is protected, so you can't access it directly. But, you can access it in a derived class. c class Parent { protected: int c; }; class Child : public Parent { public: void setC(int val) { c = val; } int getC() { return (c); } }; int main(void) { Child obj; obj.setC(30); int a = obj.getC(); /*Allowed*/ return (0); } The main difference between and is who can access the members: private protected : Only accessible within the same class. Private : Accessible within the same class and also in classes that inherit from it. Protected Private Example class Parent { private: int a; }; class Child : public Parent { public: void setA(int val) { /*a = val; Error, can't access private member */ } }; Protected Example class Parent { protected: int b; }; class Child : public Parent { public: void setB(int val) { b = val; /*Allowed, b is protected*/ } }; In the first example, can't access because it's private in . In the second example, can access because it's protected in . Child a Parent Child b Parent Constructors Constructors in C++ are special member functions that get automatically called when an object of a class is created. They usually initialize the object’s attributes. Syntax The constructor has the same name as the class and doesn’t return anything. class MyClass { public: MyClass() { /*Constructor code here*/ } }; Here’s how you can use a constructor to initialize an object. #include <iostream> class Dog { public: std::string name; Dog() { name = "Unknown"; } }; int main(void) { Dog myDog; std::cout << "Dog name: " << myDog.name << std::endl; return (0); } Output: testing % ./a.out Dog name: Unknown You can also pass parameters to a constructor. #include <iostream> class Dog { public: std::string name; Dog(std::string dogName) { name = dogName; } }; int main(void) { Dog myDog("Buddy"); std::cout << "Dog name: " << myDog.name << std::endl; return (0); } Output: testing % ./a.out Dog name: Buddy Multiple Constructors You can have more than one constructor, as long as they have different parameters. class Dog { public: std::string name; int age; Dog() { name = "Unknown"; age = 0; } Dog(std::string dogName) { name = dogName; age = 0; } Dog(std::string dogName, int dogAge) { name = dogName; age = dogAge; } }; Encapsulation Encapsulation enhances data integrity by locking down data access to specific methods within the class. This secures your code by making variables private and providing controlled ways to modify or retrieve them through setters and getters. It also helps to manage the code more conveniently. For a better understanding, let’s consider a case with and without encapsulation: Without Encapsulation In this scenario, the data is exposed and can be modified directly, leaving it vulnerable to erroneous changes. class Dog { public: int age; }; int main(void) { Dog dog; dog.age = -15; /*Invalid, but allowed*/ return (0); } With Encapsulation In this case, the data is secured through private access, and can only be changed via specific methods that include validation checks. class Dog{ private: int _age; public: void setAge(int a){ if (a > 0) _age = a; else _age = 0; } int getAge() { return (_age); } }; int main(void) { Dog dog; dog.setAge(-15); /*Invalid, defaults to zero*/ dog.setAge(15); /*Valid, now _age = 15)*/ return(0); } Abstraction Abstraction in C++ is like using a TV remote. You press the button to turn the TV on. You don’t need to know how the TV turns on inside; you just press the button. In C++, abstraction lets you hide the complicated stuff and only show the buttons (functions) that are needed. power For a better understanding, let me show a case with and without abstraction: Without Abstraction Imagine you have to do 3 steps to start a car: turn a key, push a button, and pump gas. class Car { public: void turnKey() { /*Turn the key*/ } void pushButton() { /*Push the button*/ } void pumpGas() { /*Pump gas*/ } }; With Abstraction Instead, you can have just one button (function) that does all those 3 steps for you. start class Car { private: void turnKey() { /*Turn the key*/ } void pushButton() { /*Push the button*/ } void pumpGas() { /*Pump gas*/ } public: void start() { turnKey(); pushButton(); pumpGas(); } }; You just need to know the button, and don’t have to worry about what happens inside. It makes it easier to work with your class. start Inheritance Inheritance in C++ is similar to passing traits from parents to children. If your parent can run well, you can inherit that ability too. case with and without inheritance: Without Inheritance If you have two types of cars, both can start and stop, but they can also drift. Without inheritance, you'd write two separate classes. SportsCar class Car { public: void start() { /*Start the car*/ } void stop() { /*Stop the car*/ } }; class SportsCar { public: void start() { /*Start the car*/ } void stop() { /*Stop the car*/ } void drift() { /*Make the car drift*/ } }; With Inheritance You can make a of . It will automatically get the ability to start and stop from its . SportsCar child Car parent class Car { public: void start() { /*Start the car*/ } void stop() { /*Stop the car*/ } }; class SportsCar : public Car { public: void drift() { /*Make the car drift*/ } }; Polymorphism Polymorphism is like having a universal remote that can control different devices: TV, fan, etc. You press the “power” button, and each device knows what to do without you having to tell it specifically. And in C++ you can have the same, so to use the same functions, but for different devices. example of code with and without inheritance: Without Polymorphism If you have different types of cars, each with a special way of starting, you’d have to call each one differently. class Car { public: void start() { /*Start the car*/ } void makeSound(){ std::cout << "vooom" << std::endl; } }; class ElectricCar { public: void startElectric() { /*Start the electric car*/ } void makeSound(){ std::cout << "vooom" << std::endl; } }; in code, it will look like this: int main(void) { Car myCar; myCar.start(); ElectricCar myElectricCar; myElectricCar.startElectric(); return (0); } With Polymorphism You can make all car types have a common “start” function, so you can treat them all as just “cars”. class Car { public: virtual void start() { /*Start the car*/ } void makeSound(){ std::cout << "vooom" << std::endl; } }; class ElectricCar : public Car { public: void start() override { /*Start the electric car*/ } }; in code, it will look like this: int main(void) { Car* myCar = new Car(); Car* myElectricCar = new ElectricCar(); myCar->start(); myElectricCar->start(); delete(myCar); delete(myElectricCar); return (0); } With polymorphism, you just have one or function, and each type of car knows what to do when you press it. This makes it simpler to manage different types of cars. button Why Use Pointers in Polymorphism? Dynamic Behavior: Pointers let your program decide at run-time which version of the function to use. If you have an , it'll use If it's just a , it'll use . This happens on the fly. start ElectricCar ElectricCar::start(); Car Car::start(); Flexibility: By using pointers to base classes like , you can write code that works for any derived class like without even knowing what types of cars you might add to the program later. You can easily plug in a new type of car and your existing code will still work. Car ElectricCar Avoid Slicing: Using pointers prevents , which is when you lose the unique behaviors of the derived class ( ) if you try to store it as a base class ( ). slicing ElectricCar Car Memory Management: Pointers let you control the object’s lifetime. You can create objects when you need them and delete them when you’re done, which can help to save resources. You mostly lose polymorphism if you don’t use pointers or references. When you use stack-allocated objects, the compiler decides at compile-time which method to call, based on the object’s declared type. This is called “static polymorphism” or “compile-time polymorphism,” and it doesn’t offer the same flexibility as “dynamic polymorphism” or “run-time polymorphism” that you get with pointers or references. Finish I hope this article clarifies the intricacies of object-oriented programming in C++ for you. If you have any suggestions or questions, feel free to leave a comment. If you found this guide useful, please consider connecting with me on and leaving feedback. LinkedIn Link to follow me: LinkedIn Hackernoon Also published . here