Quick answer
OOP concepts in C++ rest on the class, which bundles data with the functions that use it, and four pillars: encapsulation (private data), abstraction (pure virtual functions), inheritance (a child class reuses a parent) and polymorphism (virtual functions pick the right version at run time). Learn them together in one small program, not as separate definitions.
Key takeaways
- • A class is the blueprint and an object is one real instance built from it; memory is used only when an object is created.
- • Constructors run automatically when an object is created, and destructors run when it goes out of scope.
- • Encapsulation keeps data members private, and abstraction uses pure virtual functions so a base class cannot be created on its own.
- • Inheritance fits only "is a" relationships, while polymorphism needs virtual in the base class and override in each child.
- • Most OOP lab marks are lost to small habits: public data, missing virtual destructors and storing child objects by value.
Table of contents 13 sections
- What Does Object-Oriented Programming Mean in C++?
- Class vs Object: The Blueprint and the Real Parcel
- The Four Pillars of OOP Concepts in C++ at a Glance
- Constructors and Destructors: An Object's Life From Start to End
- Encapsulation: Why Data Members Stay Private
- Abstraction: Showing What a Parcel Does, Not How
- Inheritance: Reusing a Class Without Copying It
- Polymorphism: One Call, Different Behavior
- The Complete Program: Every Concept in About 50 Lines
- Which OOP Mistakes Cost Students Marks in Labs and Vivas?
- Where OOP Fits in a Pakistani BSCS Degree
- Building Your C++ Base With PSTA
- A 20-Minute Exercise to Lock It In
OOP concepts in C++ come down to one tool, the class, and four ideas built on it: encapsulation, abstraction, inheritance and polymorphism. That matters early, because in the 2025 Stack Overflow Developer Survey, 44.6% of respondents who were learning to code said they had used C++, far more than the 21.8% of professional developers who did.
Most of those learners meet C++ in a first-year university course. Then the second semester switches from functions and arrays to classes, and many students who could write any loop suddenly feel lost. This guide fixes that with one small, realistic program that grows section by section, so every concept has a job instead of a definition to memorize.
Here's what this explainer covers:
- What a class and an object really are, in code and in plain words
- How constructors and destructors control an object's life
- Each of the four pillars, with the line of code that shows it
- One complete program you can compile and change tonight
- The mistakes that cost students marks in OOP labs and vivas
What Does Object-Oriented Programming Mean in C++?
In procedural code, data and functions live apart. You keep a parcel's weight in one variable, its city in another, and pass both into a function that calculates the delivery charge. That works for 50 lines. At 2,000 lines, nobody remembers which function is allowed to change which variable.
Object-oriented programming groups the data and the functions that work on it into a single unit. In C++ that unit is a class. The data inside it are called data members (or attributes), and the functions are called member functions (or methods).
Our running example comes from a business you can see in every Pakistani city: an online shop booking parcels with a courier. Each parcel has a destination city, a weight and a delivery charge. Some parcels are cash on delivery (COD), which means extra handling. The rates used below are made-up example numbers, not any courier's real tariff.
Class vs Object: The Blueprint and the Real Parcel
A class is a description. An object is one real thing created from that description. The class Parcel says every parcel has a city and a weight. The object going to Lahore today is one parcel with the value "Lahore" and 800 grams.
class Parcel {
public:
string city;
int grams;
};
int main() {
Parcel p1; // p1 is an object
p1.city = "Lahore";
p1.grams = 800;
}
Writing the class uses no memory for parcels. Memory is set aside only when you create an object such as p1. You can create a thousand objects from the same class, and each keeps its own city and weight.
Notice the semicolon after the closing brace of the class. Forgetting it is the single most common compile error in a first OOP lab, and the error message usually points to the line after the class, which makes it confusing.
The Four Pillars of OOP Concepts in C++ at a Glance
Before the code grows, here is the map. Keep this table open while you read the rest of the guide.
| Concept | Plain meaning | C++ feature that shows it | In our parcel program |
|---|---|---|---|
| Encapsulation | Keep data safe inside the object | private members, public functions |
Weight can't be set to a negative number |
| Abstraction | Show what it does, hide how | Pure virtual function, abstract class | Every parcel can report a charge |
| Inheritance | Build a new class from an old one | class CodParcel : public Parcel |
COD parcel reuses city and weight |
| Polymorphism | Same call, different result by type | virtual and override |
charge() differs for COD parcels |
Constructors and Destructors: An Object's Life From Start to End
Setting p1.city by hand after creating the object is risky. If you forget one line, the parcel has a garbage weight. A constructor solves this. It's a member function with the same name as the class and no return type, and it runs automatically the moment an object is created.
class Parcel {
public:
Parcel(string city, int grams) : city(city), grams(grams) {}
~Parcel() { cout << "Parcel record closed\n"; }
private:
string city;
int grams;
};
Parcel p1("Lahore", 800); // constructor runs here
The part after the colon is a member initializer list. It sets each member directly instead of assigning it later, and it's the style most C++ teachers expect.
The function starting with a tilde (~Parcel) is the destructor. It runs automatically when the object goes out of scope, for example at the end of the block where it was created. In small programs it often does nothing. In real programs it releases whatever the object was holding, such as a file or a network connection.
Encapsulation: Why Data Members Stay Private
Encapsulation means the object guards its own data. Outside code can't reach in and change a value directly. It must ask the object through a public function, and that function can refuse bad input.
C++ gives you three access specifiers. According to the W3Schools C++ reference, public members can be reached from outside the class, private members can't, and protected members are hidden from outside code but available to child classes. If you write no specifier at all, members of a class are private by default.
class Parcel {
public:
void setGrams(int g) {
if (g > 0) grams = g; // rejects zero and negative weight
}
int getGrams() const { return grams; }
private:
int grams = 0;
};
Now p1.grams = -500; won't even compile. The only way in is setGrams(), and it ignores nonsense. That is the whole point of encapsulation: one place controls the rules, so a bug in some other file can't quietly break your data.
The const after getGrams() promises that the function only reads. Examiners like seeing it, and the compiler will stop you if you accidentally change a member inside a const function.
Abstraction: Showing What a Parcel Does, Not How
Abstraction is about the outside view. The shop owner wants to ask any parcel, "What's your charge?" They don't care how the number is worked out.
In C++ you express that with a pure virtual function, written with = 0 at the end. Microsoft's C++ documentation on abstract classes explains that a class with at least one pure virtual function becomes abstract. You can't create an object of it, but you can still use pointers and references to it.
class Parcel {
public:
virtual int charge() const = 0; // every parcel must answer this
virtual ~Parcel() = default;
};
Writing Parcel p; now gives a compile error, which is exactly what you want. There is no such thing as a parcel with no delivery type, so the language stops you from making one.
Students often mix up abstraction and encapsulation. A simple way to keep them apart: encapsulation hides the data, abstraction hides the details of the work. They usually appear together, but they answer different questions.
Inheritance: Reusing a Class Without Copying It
A COD parcel is still a parcel. It has a city and a weight, and it needs everything a normal parcel has, plus one extra value: the order amount the courier will collect. Inheritance lets you write only the difference.
class CodParcel : public Parcel {
public:
CodParcel(string city, int grams, int orderValue)
: Parcel(city, grams), orderValue(orderValue) {}
private:
int orderValue;
};
Here Parcel is the base (parent) class and CodParcel is the derived (child) class. The child's constructor calls the parent's constructor first, in its initializer list, so the shared part is built before the extra part.
Use inheritance only when the sentence "a child is a parent" makes sense. A COD parcel is a parcel, so inheritance fits. A shop has parcels, so Shop should hold a list of parcels rather than inherit from Parcel. That relationship is called composition, and choosing correctly between the two is a favorite viva question.
Polymorphism: One Call, Different Behavior
Polymorphism means the same function call gives the right answer for each object's real type. A standard parcel and a COD parcel both answer charge(), but they calculate it differently.
The keyword that makes this work is virtual. As LearnCpp's lesson on virtual functions explains, when you call a virtual function through a pointer or reference to the base class, C++ runs the most-derived version that matches the actual object. Call it directly on an object, and you always get that object's own type.
class StandardParcel : public Parcel {
public:
int charge() const override { return baseRate(); }
};
class CodParcel : public Parcel {
public:
int charge() const override {
return baseRate() + orderValue * 2 / 100; // example 2% handling
}
};
Write override on every function you mean to replace. If you misspell the name or change a parameter type, the compiler reports an error instead of silently creating a brand-new function that never gets called.
You'll also hear about compile-time polymorphism, which is function overloading: two functions with the same name but different parameters. It's real, but when a teacher says "polymorphism" in an OOP exam, they almost always mean the virtual-function kind shown here.
The Complete Program: Every Concept in About 50 Lines
Here is the whole thing in one file. It compiles with any compiler that supports C++17. We checked it with g++ using -std=c++17 -Wall -Wextra, and it built with no warnings.
#include <iostream>
#include <memory>
#include <string>
#include <vector>
using namespace std;
class Parcel {
public:
Parcel(string city, int grams) : city(city), grams(grams) {}
virtual ~Parcel() = default;
virtual int charge() const = 0; // pure virtual: Parcel is abstract
string label() const { return city + " (" + to_string(grams) + " g)"; }
protected:
int baseRate() const { // children may use this
int extraKg = (grams > 1000) ? (grams - 1) / 1000 : 0;
return 250 + extraKg * 100;
}
private:
string city; // hidden from outside code
int grams;
};
class StandardParcel : public Parcel {
public:
using Parcel::Parcel;
int charge() const override { return baseRate(); }
};
class CodParcel : public Parcel {
public:
CodParcel(string city, int grams, int orderValue)
: Parcel(city, grams), orderValue(orderValue) {}
int charge() const override { return baseRate() + orderValue * 2 / 100; }
private:
int orderValue;
};
int main() {
vector<unique_ptr<Parcel>> today;
today.push_back(make_unique<StandardParcel>("Lahore", 800));
today.push_back(make_unique<CodParcel>("Karachi", 2000, 4500));
for (const auto& p : today)
cout << p->label() << " -> Rs. " << p->charge() << "\n";
}
The output is two lines: the Lahore parcel at Rs. 250 and the Karachi COD parcel at Rs. 440 (Rs. 350 base plus Rs. 90 handling on a Rs. 4,500 order). Again, these are teaching numbers only.
Trace where each pillar lives:
- Encapsulation:
cityandgramsare private, somain()can only read them throughlabel(). - Abstraction:
charge() = 0makesParcelabstract, so nobody can book a parcel with no type. - Inheritance: both child classes reuse the constructor,
label()and the protectedbaseRate(). - Polymorphism: the loop calls
p->charge()through a base pointer and gets a different answer for each type.
The unique_ptr deletes each parcel for you when the vector is destroyed. Because the base destructor is virtual, the child part is cleaned up correctly too.
Which OOP Mistakes Cost Students Marks in Labs and Vivas?
Most lost marks have nothing to do with not knowing the OOP concepts in C++. They come from small habits like these, which show up again and again in first-year assignments:
- Making every member public. It compiles, but it throws away encapsulation, and an examiner will notice.
- Forgetting
virtualon the base function. The program runs, yet the base version is called every time. No error, just wrong output. - A base class with virtual functions but a non-virtual destructor. Deleting a child through a base pointer then skips the child's cleanup.
- Calling a virtual function inside a constructor. LearnCpp's best-practice advice is to never do this, because during construction the derived part doesn't exist yet, so the base version runs.
- Storing children in a
vector<Parcel>by value. Each object gets cut down to its base part, which is called object slicing. Store pointers instead, as the complete program does. - Using inheritance for a "has a" relationship. If the "is a" sentence sounds silly, use composition.
Where OOP Fits in a Pakistani BSCS Degree
In most Pakistani computer science programs, OOP is the second programming course. Sindh Madressatul Islam University's BSCS scheme of studies, for example, places Object Oriented Programming in the 2nd semester at 3-1 credit hours, meaning three theory hours and one lab, with Programming Fundamentals as the prerequisite. Other universities follow a similar order, though course codes differ.
That timing explains why so many students struggle with OOP concepts in C++. You get one semester of loops and functions, then a few weeks of classes, and mid-terms arrive before the ideas settle. Data Structures usually comes next and leans heavily on classes, so a weak OOP base hurts twice.
C++ isn't only a university language, either. JetBrains' State of C++ 2025 report, based on 1,800 C++ developers, found C++17 used by 43% of respondents and C++20 by 34%. The report notes its sample may lean toward JetBrains users. Every feature in our parcel program works in C++17, so what you practice here is the C++ that working developers write.
If you came to this page from Python, the ideas carry over directly. Python simply hides some of the machinery, such as access control and virtual dispatch, that C++ makes you write out. Our earlier comparison of C++ vs Python for beginners covers which language to start with.
Building Your C++ Base With PSTA
PSTA's C++ programming course for beginners is built for the stage just before university OOP. Over 12 weeks it takes complete beginners through variables, if/else, loops, functions, arrays and strings. Module 7 introduces the basics of OOP with classes and objects, and Module 8 is a small final project written from scratch.
The course page lists the fee at Rs. 12,000. You can join on campus in Mandian, Abbottabad, online, or in a hybrid mix of both. It suits matric and FSc students heading into BSCS or BSSE, and non-CS students who want to test whether programming is for them.
If you already have the basics, our C++ project ideas matched to your semester give you something to build with classes. Fees and timings for every program sit side by side in the PSTA course list. To reserve a seat, fill in the short online admission form. Questions about batch timings can go to our WhatsApp line, +92 314 5424399.
A 20-Minute Exercise to Lock It In
Copy the complete program into your compiler and run it once unchanged. Then add a third class, FragileParcel, that charges the base rate plus a flat Rs. 150 for careful handling. Book one fragile parcel to Peshawar in main(), and don't touch the loop.
If the new line prints with the right charge, you've just used inheritance and polymorphism without editing any existing code. That one experience will teach you more about OOP concepts in C++ than rereading definitions ever will.
Frequently asked questions
What are the 4 pillars of OOP in C++?
The four pillars are encapsulation, abstraction, inheritance and polymorphism. Encapsulation keeps data private behind public functions. Abstraction exposes what an object does through pure virtual functions. Inheritance lets a child class reuse a parent class. Polymorphism lets one call, made through a base pointer or reference, run the correct child version at run time using virtual functions.
What is the difference between a class and an object in C++?
A class is a definition written in code. It lists the data members and member functions every object of that type will have, but it holds no data itself. An object is a real instance created from the class, such as one parcel going to Lahore. You can create many objects from one class, and each stores its own values.
Is C++ a fully object-oriented language?
No. C++ supports object-oriented programming, but it also allows plain procedural code. The main() function and free functions live outside any class, and you can write complete programs without classes. That is why C++ is usually described as multi-paradigm. University OOP courses use it because it makes features like access control and virtual functions visible.
What is the difference between abstraction and encapsulation?
Encapsulation hides data: members are private, and outside code must go through public functions that can check input. Abstraction hides how work is done: users see a simple interface, such as a charge() function, without knowing the calculation behind it. In C++ encapsulation uses access specifiers, while abstraction often uses abstract classes with pure virtual functions.
Why should a base class destructor be virtual?
If a base class has virtual functions, you will usually delete child objects through base pointers. Without a virtual destructor, only the base part is cleaned up, and the child destructor never runs, which can leak memory or other resources. Declaring virtual ~Base() = default; in the base class is a simple habit that prevents this problem.
What is the difference between overloading and overriding?
Overloading means two or more functions share a name but take different parameters, and the compiler chooses one at compile time. Overriding means a child class replaces a virtual function from its parent with the same signature, and the choice happens at run time. Mark overriding functions with override so the compiler catches spelling or signature mistakes.
Should I learn OOP in C++ or Python first?
If your BSCS or BSSE course uses C++, learn OOP in C++ first, because exams will test its syntax, access specifiers and virtual functions. Python teaches the same ideas with less code, which can help if you feel stuck. Once the four pillars make sense in one language, moving them to the other usually takes a few days of practice.
Sources & references
- Stack Overflow Developer Survey 2025: Technology survey.stackoverflow.co
- The State of C++ 2025 (JetBrains) lp.jetbrains.com
- LearnCpp 25.2: Virtual Functions and Polymorphism learncpp.com
- Abstract Classes (C++), Microsoft Learn learn.microsoft.com
- C++ Access Specifiers, W3Schools w3schools.com
- BSCS Program and Scheme of Studies, SMIU smiu.edu.pk
Last reviewed & updated on by Umar Qureshi. Written by the PSTA team in Abbottabad, Pakistan.
Best C++ Programming Course for Beginners in Pakistan
Turn this guide into a career: hands-on training with real projects, online across Pakistan or on-campus in Abbottabad · 12 Weeks.
Comments
No comments yet — ask a question or share your experience.