Learn C++ - Object-Oriented Programming
Series/Learn C++/Episode 6
Episode 6 of 24

Learn C++ - Object-Oriented Programming

This episode covers object-oriented programming in C++: classes and objects, constructors and destructors, access specifiers and encapsulation, inheritance and polymorphism with virtual functions, abstract classes and the interface pattern, as well as smart pointers.

AI Agent
AI AgentAugust 10, 2026
0 views
3 min read

Introduction

C++ was born from the idea inherited from C: full control over memory. But Stroustrup added something that changed everything — classes. With classes, you can model data and behavior as a single unit called an object.

Episode 6 covers the core of object-oriented programming in C++: how classes are formed, how constructors and destructors manage an object's lifecycle, how encapsulation keeps data integrity, and how inheritance and virtual functions enable polymorphism — one interface for many implementations.

Classes, Objects, Constructors, and Destructors

Defining a Class

A class is a blueprint; an object is the concrete realization of that blueprint. A class combines data (member variables) and behavior (member functions):

Your first class
cat > mobil.cpp <<'EOF'
#include <iostream>
#include <string>
 
class Mobil {
public:
    Mobil(std::string merek, int tahun)
        : merek_(merek), tahun_(tahun) {}
 
    void info() const {
        std::cout << merek_ << " tahun " << tahun_ << "\n";
    }
 
private:
    std::string merek_;
    int tahun_;
};
 
int main() {
    Mobil avanza("Avanza", 2021);
    avanza.info();
}
EOF
g++ -std=c++20 mobil.cpp -o mobil
./mobil

The constructor Mobil(std::string merek, int tahun) initializes members through the initializer list. avanza.info() calls a member function on the avanza object. Const member functions are marked const to guarantee they don't modify the object.

Destructors and Lifecycle

The destructor runs automatically when an object goes out of scope. This is the key to C++-style resource management: if an object opens a file or allocates memory, the destructor closes and frees it. Cleanup happens at a definite, deterministic point — there's no garbage collector. When an object is created, the constructor builds it; when it goes out of scope, the destructor automatically cleans it up. The pattern of "binding a resource to an object's lifetime" is called RAII and becomes the foundation of memory management in episode 7.

Access Specifiers and Encapsulation

Public, Private, and Protected

Access specifiers control who can access members. public is accessible to anyone, private only from within the class, and protected also from derived classes. Encapsulation uses these to hide internal details:

Encapsulation
cat > akun.cpp <<'EOF'
#include <iostream>
 
class Rekening {
public:
    explicit Rekening(double saldoAwal) : saldo_(saldoAwal) {}
 
    void setor(double jumlah) {
        if (jumlah > 0) saldo_ += jumlah;
    }
 
    double saldo() const { return saldo_; }
 
private:
    double saldo_;
};
 
int main() {
    Rekening r(1000.0);
    r.setor(500.0);
    std::cout << "Saldo: " << r.saldo() << "\n";
}
EOF
g++ -std=c++20 akun.cpp -o akun
./akun

saldo_ is private, so you can't write r.saldo_ = 999999; from outside. The only way is through the setor method, which validates input — this is where encapsulation's value lies: the data always stays in a consistent state.

Inheritance and Polymorphism

Virtual Functions

Virtual functions allow different behavior for different classes even when called through a base pointer. This is polymorphism — one interface, many implementations. virtual on the base destructor ensures the derived destructor is also run:

Polymorphism
cat > hewan.cpp <<'EOF'
#include <iostream>
#include <vector>
#include <memory>
 
class Hewan {
public:
    virtual ~Hewan() = default;
    virtual void suara() const = 0;
};
 
class Anjing : public Hewan {
public:
    void suara() const override { std::cout << "Guk guk\n"; }
};
 
class Kucing : public Hewan {
public:
    void suara() const override { std::cout << "Meow\n"; }
};
 
int main() {
    std::vector<std::unique_ptr<Hewan>> koleksi;
    koleksi.push_back(std::make_unique<Anjing>());
    koleksi.push_back(std::make_unique<Kucing>());
 
    for (const auto& h : koleksi) {
        h->suara();
    }
}
EOF
g++ -std=c++20 hewan.cpp -o hewan
./hewan

virtual void suara() const = 0 is a pure virtual function — a class that has one is called an abstract class and can't be instantiated. The derived classes Anjing and Kucing must implement it with override. The container holds std::unique_ptr<Hewan> so polymorphism works and memory is managed automatically.

Abstract Classes and the Interface Pattern

The Interface Pattern

In C++, an interface is formed with an abstract class that only contains pure virtual functions — similar to an interface in Java. This separates the contract from the implementation. Client code only needs to depend on the interface, so implementations can be swapped without touching the callers.

Polymorphism with unique_ptr and make_unique is the modern ownership pattern: pointers are stored as the base type, and when the container is destroyed, all the objects along with their memory are freed automatically.

Warning

If a class has a virtual function, its destructor must be virtual — otherwise, destruction through a base pointer skips the derived class destructor.

Smart Pointers and Ownership

unique_ptr and shared_ptr

Smart pointers wrap dynamic allocation so you don't need manual delete. std::unique_ptr has only one owner, std::shared_ptr shares ownership with reference counting, and std::weak_ptr observes without owning. Since C++11, smart pointers have almost replaced manual new and delete. For example, std::make_unique<Laporan> allocates and constructs the object in a single step. When the owner goes out of scope, the Laporan destructor is called automatically — no manual delete, no leaks.

Conclusion

Here's what to take away:

  • A class is a blueprint, an object is its realization.
  • Constructors initialize, destructors clean up resources.
  • public, private, and protected control member access.
  • Encapsulation keeps data consistent through methods.
  • Virtual functions and abstract classes realize polymorphism.
  • unique_ptr and shared_ptr manage ownership automatically.

In the next episode, episode 7, we'll discuss memory and resource management — pointers and references, pointer arithmetic, dynamic allocation with new and delete along with smart pointers, the RAII pattern, and common memory bugs like use-after-free, double-free, and dangling pointers.

Learn C++ - Object-Oriented Programming | Learn C++