Back to Curriculum

Classes and Object-Oriented Programming

📚 Lesson 3 of 10 ⏱️ 50 min

Classes and Object-Oriented Programming

50 min

Classes are the foundation of object-oriented programming in C++, encapsulating data and functions that operate on that data into a single unit. A class defines a blueprint for creating objects, specifying what data members (attributes) and member functions (methods) each object will have. Classes enable data abstraction, hiding implementation details while exposing a clean interface for interaction.

Constructors are special member functions that initialize objects when they are created. They have the same name as the class and no return type. C++ supports default constructors, parameterized constructors, copy constructors, and move constructors (C++11+). Destructors are called automatically when objects are destroyed, making them perfect for resource cleanup. The destructor is named with a tilde (~) followed by the class name.

Access specifiers (public, private, protected) control member visibility and encapsulation. Public members are accessible from anywhere, private members are only accessible within the class, and protected members are accessible within the class and derived classes. Proper encapsulation protects data integrity by preventing direct access to internal state, requiring controlled access through member functions.

Inheritance enables code reuse by allowing derived classes to inherit members from base classes. C++ supports single inheritance (one base class) and multiple inheritance (multiple base classes), though multiple inheritance should be used carefully. Inheritance relationships create an "is-a" relationship, where derived classes are specialized versions of base classes.

Polymorphism allows objects of different types to be treated through the same interface. Virtual functions enable runtime polymorphism through function overriding. When a base class function is declared virtual, derived classes can override it, and the correct version is called based on the actual object type, not the pointer or reference type. This is essential for creating flexible, extensible designs.

Abstract classes and pure virtual functions define interfaces that derived classes must implement. A class with at least one pure virtual function cannot be instantiated directly—it serves as a base for other classes. This pattern enables polymorphism and ensures that derived classes implement required functionality, creating robust class hierarchies.

Key Concepts

  • Classes encapsulate data and functions into single units.
  • Constructors initialize objects, destructors clean up resources.
  • Access specifiers (public, private, protected) control encapsulation.
  • Inheritance enables code reuse through class hierarchies.
  • Virtual functions enable runtime polymorphism.

Learning Objectives

Master

  • Creating classes with constructors and destructors
  • Understanding access specifiers and encapsulation
  • Implementing inheritance and class hierarchies
  • Using virtual functions for polymorphism

Develop

  • Object-oriented design thinking
  • Understanding encapsulation and data hiding
  • Designing extensible class hierarchies

Tips

  • Use initialization lists in constructors: MyClass(int x) : member(x) {}.
  • Make destructors virtual in base classes for proper cleanup.
  • Prefer composition over inheritance when "has-a" relationship fits better.
  • Use override keyword (C++11) to explicitly mark overridden functions.

Common Pitfalls

  • Not making base class destructors virtual, causing resource leaks.
  • Forgetting to call base class constructors in derived classes.
  • Breaking encapsulation by making data members public unnecessarily.
  • Using inheritance for code reuse when composition would be better.

Summary

  • Classes provide encapsulation and object-oriented structure.
  • Constructors and destructors manage object lifecycle.
  • Access specifiers enforce encapsulation and data hiding.
  • Inheritance and polymorphism enable flexible, extensible designs.

Exercise

Create a class hierarchy with inheritance and polymorphism.

#include <iostream>
#include <string>

// Base class
class Animal {
protected:
    std::string name;
    
public:
    Animal(const std::string& n) : name(n) {}
    
    virtual void makeSound() const {
        std::cout << "Some animal sound" << std::endl;
    }
    
    virtual ~Animal() = default;
};

// Derived class
class Dog : public Animal {
public:
    Dog(const std::string& n) : Animal(n) {}
    
    void makeSound() const override {
        std::cout << name << " says: Woof!" << std::endl;
    }
};

class Cat : public Animal {
public:
    Cat(const std::string& n) : Animal(n) {}
    
    void makeSound() const override {
        std::cout << name << " says: Meow!" << std::endl;
    }
};

int main() {
    Animal* animals[] = {
        new Dog("Buddy"),
        new Cat("Whiskers"),
        new Animal("Unknown")
    };
    
    for (auto animal : animals) {
        animal->makeSound();
        delete animal;
    }
    
    return 0;
}

Exercise Tips

  • Try different inheritance types: public, protected, private inheritance.
  • Add virtual destructor: virtual ~Animal() = default; for proper cleanup.
  • Use override keyword: void makeSound() const override {}.
  • Experiment with abstract classes: virtual void func() = 0; (pure virtual).

Code Editor

Output