Loading...

Go Back

Next page
Go Back Course Outline

C++ Full Course


Object Oriented Programming(OOP) in C++

Object-Oriented Programming (OOP)

OOP is a programming paradigm based on the concept of "objects," which can contain data (in the form of fields or attributes) and code (in the form of procedures or methods). Key principles of OOP include encapsulation, inheritance, and polymorphism.

Classes & Objects

Classes:

A class is a blueprint or a template for creating objects. It defines the data (member variables or attributes) that an object will hold and the actions (member functions or methods) that an object can perform.

Objects:

An object is an instance of a class. It's a concrete entity that exists in memory and has the properties and behaviors defined by its class.

Member Variables/Functions:

  • Member Variables (Attributes or Fields): These are the data components of a class that hold the state of an object.
  • Member Functions (Methods): These are the functions defined within a class that operate on the object's data or perform actions related to the object.

Access Specifiers:

Access specifiers control the visibility and accessibility of class members from outside the class. C++ provides three main access specifiers:

  • public: Members declared as public are accessible from any part of the program.
  • private: Members declared as private are only accessible from within the same class. They are hidden from the outside world, providing data encapsulation.
  • protected: Members declared as protected are accessible from within the same class and from its derived classes (in the context of inheritance).

C++ Example:

        
#include <iostream>
#include <string>

class Dog {
public:
  // Public member variables
  std::string name;
  int age;

  // Public member function
  void bark() {
    std::cout << "Woof!" << std::endl;
  }

  void displayInfo() const { // 'const' indicates this function doesn't modify the object's state
    std::cout << "Name: " << name << ", Age: " << age << std::endl;
  }

private:
  // Private member variable (only accessible within the Dog class)
  std::string breed;

protected:
  // Protected member variable (accessible within Dog and its derived classes)
  bool isVaccinated;

public:
  // Public member function to set the breed (allows controlled access to private member)
  void setBreed(const std::string& b) {
    breed = b;
  }

  // Public member function to get the breed
  std::string getBreed() const {
    return breed;
  }

  // Public member function to set vaccination status
  void setVaccinated(bool vaccinated) {
    isVaccinated = vaccinated;
  }

  // Public member function to check vaccination status
  bool isVaccinatedStatus() const {
    return isVaccinated;
  }
};

int main() {
  // Creating objects (instances) of the Dog class
  Dog myDog;
  Dog yourDog;

  // Accessing public member variables and functions
  myDog.name = "Buddy";
  myDog.age = 3;
  myDog.bark();
  myDog.displayInfo();
  myDog.setBreed("Golden Retriever");
  std::cout << "Breed of myDog: " << myDog.getBreed() << std::endl;
  myDog.setVaccinated(true);
  std::cout << "Is myDog vaccinated? " << std::boolalpha << myDog.isVaccinatedStatus() << std::endl;

  yourDog.name = "Lucy";
  yourDog.age = 5;
  yourDog.bark();
  yourDog.displayInfo();
  yourDog.setBreed("Poodle");
  std::cout << "Breed of yourDog: " << yourDog.getBreed() << std::endl;
  yourDog.setVaccinated(false);
  std::cout << "Is yourDog vaccinated? " << std::boolalpha << yourDog.isVaccinatedStatus() << std::endl;

  // Trying to access private member directly (will cause a compilation error if uncommented)
  // myDog.breed = "Labrador"; // Error: 'Dog::breed' is private

  return 0;
}
        
        
    
Woof! Name: Buddy, Age: 3 Breed of myDog: Golden Retriever Is myDog vaccinated? true Woof! Name: Lucy, Age: 5 Breed of yourDog: Poodle Is yourDog vaccinated? false

Explanation: The Dog class defines the blueprint for dog objects, including their name, age, breed, and vaccination status, as well as the ability to bark and display information.

  • name and age are public, so they can be accessed directly from outside the class.
  • bark() and displayInfo() are public member functions that operate on the object's data.
  • breed is private, ensuring that its modification is controlled through the setBreed() function, demonstrating encapsulation.
  • isVaccinated is protected, making it accessible to derived classes (though we don't have any in this example).

In main(), myDog and yourDog are objects (instances) of the Dog class. We interact with them using the public interface provided by the class. Attempting to access the private member breed directly would result in a compilation error.



Constructors & Destructors

Constructors:

A constructor is a special member function of a class that is automatically called when an object of that class is created. Its primary purpose is to initialize the object's member variables. Constructors have the same name as the class and do not have a return type (not even void).

  • Default Constructor: A constructor that takes no arguments. If you don't define any constructors in your class, the compiler will provide a default constructor (which does nothing). However, if you define any constructor, the compiler will not provide a default one, and you'll need to define it explicitly if you want to be able to create objects without arguments.
  • Parameterized Constructor: A constructor that takes one or more arguments, allowing you to initialize the object's member variables with specific values at the time of creation.
  • Copy Constructor: A special constructor that is called when a new object is created as a copy of an existing object (e.g., during initialization or when passing objects by value). If you don't define a copy constructor, the compiler provides a default one that performs a member-wise copy. However, for objects containing pointers to dynamically allocated memory, you often need to define a custom copy constructor to perform a deep copy (copying the pointed-to data) rather than a shallow copy (copying just the pointer).
  • Initializer Lists: A more efficient and sometimes necessary way to initialize member variables in a constructor. They are specified after the constructor's parameter list and before the opening brace of the constructor body, using a colon : followed by a comma-separated list of member variables and their initial values in parentheses.

Destructors:

A destructor is another special member function that is automatically called when an object is destroyed (e.g., when it goes out of scope or when delete is called on a dynamically allocated object). Its primary purpose is to perform cleanup operations, such as releasing dynamically allocated memory or closing files. Destructors have the same name as the class, preceded by a tilde (~), and they do not take any arguments or have a return type.

C++ Example:

        
#include <iostream>
#include <string>

class Rectangle {
public:
  int width;
  int height;

  // Default Constructor
  Rectangle() : width(0), height(0) {
    std::cout << "Default Constructor called for Rectangle" << std::endl;
  }

  // Parameterized Constructor
  Rectangle(int w, int h) : width(w), height(h) {
    std::cout << "Parameterized Constructor called for Rectangle (" << w << ", " << h << ")" << std::endl;
  }

  // Copy Constructor
  Rectangle(const Rectangle& other) : width(other.width), height(other.height) {
    std::cout << "Copy Constructor called for Rectangle (" << width << ", " << height << ")" << std::endl;
  }

  // Destructor
  ~Rectangle() {
    std::cout << "Destructor called for Rectangle (" << width << ", " << height << ")" << std::endl;
  }

  int area() const {
    return width * height;
  }
};

int main() {
  Rectangle rect1;             // Calls the default constructor
  Rectangle rect2(5, 10);      // Calls the parameterized constructor
  Rectangle rect3 = rect2;     // Calls the copy constructor
  Rectangle rect4(rect1);      // Also calls the copy constructor

  std::cout << "Area of rect1: " << rect1.area() << std::endl;
  std::cout << "Area of rect2: " << rect2.area() << std::endl;
  std::cout << "Area of rect3: " << rect3.area() << std::endl;
  std::cout << "Area of rect4: " << rect4.area() << std::endl;

  return 0; // Destructors for rect4, rect3, rect2, and rect1 will be called automatically here
}
        
        
    
Default Constructor called for Rectangle Parameterized Constructor called for Rectangle (5, 10) Copy Constructor called for Rectangle (5, 10) Copy Constructor called for Rectangle (0, 0) Area of rect1: 0 Area of rect2: 50 Area of rect3: 50 Area of rect4: 0 Destructor called for Rectangle (0, 0) Destructor called for Rectangle (5, 10) Destructor called for Rectangle (5, 10) Destructor called for Rectangle (0, 0)

Explanation: We define a Rectangle class with default, parameterized, and copy constructors, as well as a destructor.

  • When rect1 is created without arguments, the default constructor is called, initializing width and height to 0.
  • When rect2 is created with arguments 5 and 10, the parameterized constructor is called, initializing width to 5 and height to 10.
  • When rect3 is created by assigning rect2, the copy constructor is called, creating a new Rectangle with the same width and height as rect2.
  • Similarly, rect4 is also created using the copy constructor with rect1 as the source.
  • The destructors are called automatically when the Rectangle objects go out of scope at the end of the main function, in the reverse order of their creation.

Inheritance

Inheritance is a mechanism that allows a new class (derived or child class) to inherit properties (member variables and functions) from an existing class (base or parent class). This promotes code reuse and the creation of hierarchical relationships between classes.

Base vs. Derived Classes:

  • Base Class (Parent Class): The class whose properties are inherited.
  • Derived Class (Child Class): The class that inherits properties from the base class. It can also have its own additional members or override the inherited members.

Inheritance Types (Access Specifiers for Inheritance):

When a derived class inherits from a base class, the public, protected, and private access specifiers in the base class affect the accessibility of the inherited members in the derived class:

  • public Inheritance:
    • public members of the base class become public members of the derived class.
    • protected members of the base class become protected members of the derived class.
    • private members of the base class are inherited but remain inaccessible to the derived class.
  • protected Inheritance:
    • public and protected members of the base class become protected members of the derived class.
    • private members of the base class are inherited but remain inaccessible.
  • private Inheritance:
    • public and protected members of the base class become private members of the derived class.
    • private members of the base class are inherited but remain inaccessible.

Virtual Base Classes:

In multiple inheritance (where a class inherits from more than one base class), it's possible to have a situation where a derived class ends up with multiple copies of a common ancestor's members. Virtual base classes are used to solve this "diamond problem" by ensuring that only one instance of the common ancestor's members is inherited. This is achieved by using the virtual keyword when declaring the inheritance.

C++ Example:

        
#include <iostream>
#include <string>

// Base class
class Animal {
public:
  Animal(const std::string& n) : name(n) {
    std::cout << "Animal constructor called for " << name << std::endl;
  }
  virtual ~Animal() { // Virtual destructor
    std::cout << "Animal destructor called for " << name << std::endl;
  }

  void eat() const {
    std::cout << name << " is eating." << std::endl;
  }

protected:
  std::string name;
};

// Derived class (public inheritance)
class Dog : public Animal {
public:
  Dog(const std::string& n, const std::string& b) : Animal(n), breed(b) {
    std::cout << "Dog constructor called for " << name << " (Breed: " << breed << ")" << std::endl;
  }
  ~Dog() override { // 'override' keyword (C++11) indicates we are overriding a virtual function
    std::cout << "Dog destructor called for " << name << std::endl;
  }

  void bark() const {
    std::cout << name << " (a " << breed << ") says Woof!" << std::endl;
  }

  void displayBreed() const {
    std::cout << name << "'s breed is " << breed << std::endl;
  }

private:
  std::string breed;
};

// Another derived class (public inheritance)
class Cat : public Animal {
public:
  Cat(const std::string& n, const std::string& c) : Animal(n), color(c) {
    std::cout << "Cat constructor called for " << name << " (Color: " << color << ")" << std::endl;
  }
  ~Cat() override {
    std::cout << "Cat destructor called for " << name << std::endl;
  }

  void meow() const {
    std::cout << name << " (a " << color << " cat) says Meow!" << std::endl;
  }

  void displayColor() const {
    std::cout << name << "'s color is " << color << std::endl;
  }

private:
  std::string color;
};

int main() {
  Dog myDog("Buddy", "Golden Retriever");
  myDog.eat();
  myDog.bark();
  myDog.displayBreed();

  Cat myCat("Whiskers", "Gray");
  myCat.eat();
  myCat.meow();
  myCat.displayColor();

  // Polymorphism example (using base class pointers)
  Animal* animalPtr1 = &myDog;
  Animal* animalPtr2 = &myCat;

  std::cout << "\nPolymorphism through base class pointers:" << std::endl;
  animalPtr1->eat(); // Calls Animal::eat()
  animalPtr2->eat(); // Calls Animal::eat()

  // Note: animalPtr1->bark(); would cause a compile error because 'bark' is not a member of Animal

  return 0; // Destructors will be called automatically
}
        
        
    
Animal constructor called for Buddy Dog constructor called for Buddy (Breed: Golden Retriever) Buddy is eating. Buddy (a Golden Retriever) says Woof! Buddy's breed is Golden Retriever Animal constructor called for Whiskers Cat constructor called for Whiskers (Color: Gray) Whiskers is eating. Whiskers (a Gray cat) says Meow! Whiskers's color is Gray Polymorphism through base class pointers: Buddy is eating. Whiskers is eating. Animal destructor called for Whiskers Cat destructor called for Whiskers Animal destructor called for Buddy Dog destructor called for Buddy

Explanation:

  • Animal is the base class with a constructor, a virtual destructor (important for proper cleanup when using polymorphism), an eat() function, and a protected member name.
  • Dog and Cat are derived classes that inherit from Animal using public inheritance. They have their own constructors (calling the base class constructor using the initializer list), destructors (overriding the base class virtual destructor), and additional member functions (bark(), displayBreed(), meow(), displayColor()).
  • In main(), we create objects of Dog and Cat and demonstrate how they can use the inherited eat() function and their own specific functions.
  • The polymorphism example shows how base class pointers can point to derived class objects. When animalPtr1->eat() and animalPtr2->eat() are called, the Animal::eat() function is executed because eat() is not declared as virtual in the base class in this simplified example (we'll see virtual functions in the Polymorphism section). However, the destructors are called correctly due to the virtual keyword in the base class destructor.


Polymorphism

Polymorphism (meaning "many forms") allows objects of different classes to be treated as objects of a common base class type. This enables you to write more flexible and extensible code. C++ supports polymorphism through virtual functions and function overriding.

Virtual Functions:

A virtual function is a member function declared in the base class using the virtual keyword. The purpose of a virtual function is to allow derived classes to provide their own implementation (override) of the function. When a virtual function is called through a base class pointer or reference that is actually pointing to a derived class object, the derived class's implementation of the function is executed (this is called runtime polymorphism or dynamic dispatch).

Override (override):

The override keyword (introduced in C++11) can be used in a derived class when you intend to override a virtual function from the base class. It's not strictly necessary for overriding to work, but it provides an extra layer of safety by allowing the compiler to check if you are actually overriding a base class virtual function. If the signature doesn't match, the compiler will issue an error.

Final (final):

The final keyword can be used in two ways:

  • With a virtual function: It prevents derived classes from overriding that virtual function further down the inheritance hierarchy.
  • With a class: It prevents the class from being inherited from.

Abstract Classes:

An abstract class is a class that contains at least one pure virtual function. A pure virtual function is declared in the base class with the syntax = 0. Abstract classes cannot be instantiated (you cannot create objects of an abstract class directly). Their purpose is to define an interface that derived classes must implement. Any derived class that inherits from an abstract class must provide definitions for all of the pure virtual functions; otherwise, the derived class will also be abstract.

C++ Example:

        
#include <iostream>
#include <string>

// Abstract base class
class Shape {
public:
  Shape(const std::string& n) : name(n) {
    std::cout << "Shape constructor called for " << name << std::endl;
  }
  virtual ~Shape() {
    std::cout << "Shape destructor called for " << name << std::endl;
  }

  virtual double area() const = 0; // Pure virtual function

  virtual void display() const {
    std::cout << "Shape: " << name << ", Area: " << area() << std::endl;
  }

protected:
  std::string name;
};

// Concrete derived class
class Circle : public Shape {
public:
  Circle(double r) : Shape("Circle"), radius(r) {
    std::cout << "Circle constructor called with radius " << radius << std::endl;
  }
  ~Circle() override {
    std::cout << "Circle destructor called" << std::endl;
  }

  double area() const override {
    return 3.14159 * radius * radius;
  }

  void display() const override {
    std::cout << "Circle: Radius = " << radius << ", Area = " << area() << std::endl;
  }

private:
  double radius;
};

// Another concrete derived class
class Rectangle : public Shape {
public:
  Rectangle(double w, double h) : Shape("Rectangle"), width(w), height(h) {
    std::cout << "Rectangle constructor called with width " << width << " and height " << height << std::endl;
  }
  ~Rectangle() override {
    std::cout << "Rectangle destructor called" << std::endl;
  }

  double area() const override {
    return width * height;
  }

  void display() const override {
    std::cout << "Rectangle: Width = " << width << ", Height = " << height << ", Area = " << area() << std::endl;
  }

private:
  double width;
  double height;
};

int main() {
  // Shape s; // Error: Cannot create an object of an abstract class

  Circle c(5.0);
  Rectangle r(4.0, 6.0);

  c.display();
  r.display();

  std::cout << "\nPolymorphism through base class pointers:" << std::endl;
  Shape* shapePtr1 = &c;
  Shape* shapePtr2 = &r;

  shapePtr1->display(); // Calls Circle::display()
  shapePtr2->display(); // Calls Rectangle::display()

  std::cout << "\nAreas through base class pointers:" << std::endl;
  std::cout << "Area of shapePtr1 (Circle): " << shapePtr1->area() << std::endl; // Calls Circle::area()
  std::cout << "Area of shapePtr2 (Rectangle): " << shapePtr2->area() << std::endl; // Calls Rectangle::area()

  return 0; // Destructors will be called automatically
}
        
        
    
Shape constructor called for Circle Circle constructor called with radius 5 Shape constructor called for Rectangle Rectangle constructor called with width 4 and height 6 Circle: Radius = 5, Area = 78.5398 Rectangle: Width = 4, Height = 6, Area = 24 Polymorphism through base class pointers: Circle: Radius = 5, Area = 78.5398 Rectangle: Width = 4, Height = 6, Area = 24 Areas through base class pointers: Area of shapePtr1 (Circle): 78.5398 Area of shapePtr2 (Rectangle): 24 Shape destructor called for Rectangle Rectangle destructor called Shape destructor called for Circle Circle destructor called

Explanation:

  • Shape is an abstract base class because it has a pure virtual function area() = 0. You cannot create objects of Shape.
  • Circle and Rectangle are concrete derived classes that inherit from Shape and provide their own implementations (override) of the area() and display() virtual functions using the override keyword.
  • In main(), we create objects of Circle and Rectangle.
  • We then demonstrate polymorphism by using base class pointers (Shape*) to point to Circle and Rectangle objects. When we call the display() and area() functions through these base class pointers, the correct derived class implementations are executed at runtime. This is the power of dynamic dispatch.
  • The virtual destructor in the Shape class ensures that when we delete a derived class object through a base class pointer, the derived class's destructor is also called, preventing memory leaks.

Static Members

Static members (variables and functions) belong to the class itself rather than to any specific object of the class. There is only one copy of a static member shared by all objects of the class.

  • Static Variables: These are class-level variables. They are initialized outside the class definition (usually in the source file).
  • Static Functions: These are class-level functions. They can access only static member variables of the class (and any global variables or other static local variables). They do not have a this pointer because they are not associated with any particular object. Static functions are often used for utility purposes related to the class or to manage static data.

C++ Example:

        
#include <iostream>

class Counter {
public:
  Counter() {
    count++;
  }
  ~Counter() {
    count--;
  }

  static int getCount() {
    return count;
  }

private:
  static int count; // Static member variable
};

// Initialize the static member variable outside the class definition
int Counter::count = 0;

int main() {
  std::cout << "Initial count: " << Counter::getCount() << std::endl;

  Counter c1;
  std::cout << "Count after c1 creation: " << Counter::getCount() << std::endl;

  Counter c2;
  std::cout << "Count after c2 creation: " << Counter::getCount() << std::endl;

  {
    Counter c3;
    std::cout << "Count inside block: " << Counter::getCount() << std::endl;
  } // c3 destructor is called here

  std::cout << "Count after block ends: " << Counter::getCount() << std::endl;

  return 0; // c2 and c1 destructors are called here
}
        
        
    
Initial count: 0 Count after c1 creation: 1 Count after c2 creation: 2 Count inside block: 3 Count after block ends: 2

Explanation:

  • count is a static private member variable of the Counter class. It is initialized to 0 outside the class definition.
  • Each time a Counter object is created, the constructor increments the static count. Each time a Counter object is destroyed, the destructor decrements count.
  • getCount() is a static public member function that returns the current value of count. You can call it using the class name (Counter::getCount()) without needing to create an object of the Counter class.
  • The output shows how the single static count variable is shared and updated across all instances of the Counter class.

Friend Functions/Classes

The friend keyword allows a non-member function or another class to access the private and protected members of a class. This can be useful in specific scenarios where you need closer cooperation between different parts of your code while still maintaining some level of encapsulation.

  • Friend Function: A regular (non-member) function that is declared as a friend inside a class. It can then access the private and protected members of objects of that class. Friend functions are not called using the object and the dot operator (like member functions).
  • Friend Class: A class that is declared as a friend inside another class. All member functions of the friend class can then access the private and protected members of objects of the class that declared it as a friend. Friendships are not mutual or transitive. If class A is a friend of class B, it doesn't mean class B is a friend of class A, and if class A is a friend of B, and B is a friend of C, it doesn't mean A is a friend of C.

C++ Example:

            
    #include <iostream>
    
    class Box {
    private:
      double width;
    
    public:
      Box(double w) : width(w) {}
    
      // Friend function declaration
      friend void printWidth(const Box& b);
    };
    
    // Definition of the friend function (not a member of Box)
    void printWidth(const Box& b) {
      // This function can access the private member 'width' of Box objects
      std::cout << "Width of the box: " << b.width << std::endl;
    }
    
    class AnotherClass {
    public:
      void modifyBox(Box& b, double newWidth) {
        // Cannot access b.width directly because AnotherClass is not a friend (yet)
        // b.width = newWidth; // Error: 'Box::width' is private
      }
    };
    
    class FriendClass {
    public:
      void modifyBox(Box& b, double newWidth) {
        // Can access b.width because FriendClass is declared as a friend of Box
        b.width = newWidth;
        std::cout << "FriendClass modified box width to: " << b.width << std::endl;
      }
    };
    
    class BoxWithFriendClass {
    private:
      double height;
    
      // Declare FriendClass as a friend
      friend class FriendClass;
    
    public:
      BoxWithFriendClass(double h) : height(h) {}
    
      void displayHeight() const {
        std::cout << "Height of the box: " << height << std::endl;
      }
    };
    
    int main() {
      Box myBox(15.0);
      printWidth(myBox);
    
      FriendClass fc;
      BoxWithFriendClass boxWithFriend(20.0);
      boxWithFriend.displayHeight();
      fc.modifyBox(boxWithFriend, 25.0);
      boxWithFriend.displayHeight();
    
      return 0;
    }
            
            
        
Width of the box: 15 Height of the box: 20 FriendClass modified box width to: 25 Height of the box: 25

Explanation:

  • In the Box class, printWidth is declared as a friend function. This allows printWidth to access the private member width of Box objects. printWidth is a regular function, not a member of Box, so it's called directly with a Box object as an argument.
  • The AnotherClass cannot directly access the private width of a Box object because it is not declared as a friend.
  • In the BoxWithFriendClass, FriendClass is declared as a friend. This means that all member functions of FriendClass can access the private members (like height) of BoxWithFriendClass objects. The modifyBox function in FriendClass demonstrates this by directly modifying the height.

Friendships should be used judiciously as they can weaken the encapsulation of a class. They are typically employed when there is a close logical relationship between classes or functions that warrants this level of access.

This detailed explanation covers the fundamental concepts of Object-Oriented Programming in C++. Understanding and applying these principles will enable you to design and implement more complex, modular, and maintainable software systems. Remember to practice these concepts with various examples to solidify your grasp.

Encapsulation and Inheritance in C++

Encapsulation and inheritance are two fundamental pillars of Object-Oriented Programming (OOP) in C++. They help in organizing code, promoting reusability, and managing complexity.

Encapsulation

Encapsulation is the mechanism of bundling data (attributes) and the methods (functions) that operate on that data into a single unit called a class. It also involves controlling the access to the internal details of an object, often through access modifiers.

Benefits of Encapsulation

  • Data Hiding: Protects the internal state of an object by restricting direct access from outside the class.
  • Modularity: Makes the code more organized and easier to understand.
  • Flexibility: Allows you to change the internal implementation of a class without affecting the code that uses it (as long as the public interface remains the same).
  • Code Reusability: Well-encapsulated classes can be reused in different parts of the program or in other programs.

Access Modifiers

C++ provides access modifiers to control the visibility of class members:

  • public: Members are accessible from anywhere.
  • private: Members are accessible only from within the same class.
  • protected: Members are accessible from within the same class and its derived classes.

Example of Encapsulation

#include <iostream>
#include <string>

class BankAccount {
private:
    std::string accountNumber;
    double balance;

public:
    // Constructor to initialize the account
    BankAccount(std::string accNum, double initialBalance) : accountNumber(accNum), balance(initialBalance) {}

    // Public methods to interact with the account
    std::string getAccountNumber() const {
        return accountNumber;
    }

    double getBalance() const {
        return balance;
    }

    void deposit(double amount) {
        if (amount > 0) {
            balance += amount;
            std::cout << "Deposit of $" << amount << " successful. New balance: $" << balance << std::endl;
        } else {
            std::cout << "Invalid deposit amount." << std::endl;
        }
    }

    void withdraw(double amount) {
        if (amount > 0 && balance >= amount) {
            balance -= amount;
            std::cout << "Withdrawal of $" << amount << " successful. New balance: $" << balance << std::endl;
        } else {
            std::cout << "Insufficient balance or invalid withdrawal amount." << std::endl;
        }
    }
};

int main() {
    BankAccount account1("123456", 1000.0);
    std::cout << "Account Number: " << account1.getAccountNumber() << std::endl;
    std::cout << "Current Balance: $" << account1.getBalance() << std::endl;

    account1.deposit(500.0);
    account1.withdraw(200.0);
    account1.withdraw(2000.0); // Attempt to withdraw more than balance

    // account1.balance = -500; // Error! 'balance' is private and cannot be accessed directly

    return 0;
}
Account Number: 123456
Current Balance: $1000
Deposit of $500 successful. New balance: $1500
Withdrawal of $200 successful. New balance: $1300
Insufficient balance or invalid withdrawal amount.

In this example, the BankAccount class encapsulates the accountNumber and balance (private data members) along with the methods (deposit, withdraw, getBalance, getAccountNumber - public member functions) that operate on this data. The private members cannot be accessed directly from outside the class, ensuring the integrity of the account's state.

Inheritance

Inheritance is a mechanism in OOP where a new class (derived class or subclass) inherits properties and behaviors (data members and member functions) from an existing class (base class or superclass). This promotes code reusability and the creation of hierarchical relationships between classes.

Key Concepts of Inheritance

  • Base Class (Superclass): The class whose properties are inherited.
  • Derived Class (Subclass): The class that inherits from the base class. It can add new members or override the inherited ones.
  • public, protected, and private Inheritance: Control the access level of inherited members in the derived class.
    • public inheritance: Public members of the base class remain public in the derived class, and protected members remain protected.
    • protected inheritance: Public and protected members of the base class become protected in the derived class.
    • private inheritance: Public and protected members of the base class become private in the derived class.

Example of Inheritance

#include <iostream>
#include <string>

// Base class: Shape
class Shape {
public:
    Shape(std::string color) : color_(color) {}
    std::string getColor() const { return color_; }
    virtual double area() const { return 0.0; } // Virtual function for potential overriding
    virtual void display() const {
        std::cout << "Color: " << color_ << ", Area: " << area() << std::endl;
    }

protected:
    std::string color_;
};

// Derived class: Circle
class Circle : public Shape {
private:
    double radius_;

public:
    Circle(std::string color, double radius) : Shape(color), radius_(radius) {}
    double area() const override { return 3.14159 * radius_ * radius_; }
    void display() const override {
        std::cout << "Circle - Color: " << color_ << ", Radius: " << radius_ << ", Area: " << area() << std::endl;
    }
};

// Derived class: Rectangle
class Rectangle : public Shape {
private:
    double width_;
    double height_;

public:
    Rectangle(std::string color, double width, double height) : Shape(color), width_(width), height_(height) {}
    double area() const override { return width_ * height_; }
    void display() const override {
        std::cout << "Rectangle - Color: " << color_ << ", Width: " << width_ << ", Height: " << height_ << ", Area: " << area() << std::endl;
    }
};

int main() {
    Circle circle("Red", 5.0);
    Rectangle rectangle("Blue", 4.0, 6.0);

    circle.display();
    rectangle.display();

    std::cout << "Circle Area: " << circle.area() << std::endl;
    std::cout << "Rectangle Area: " << rectangle.area() << std::endl;

    return 0;
}
Circle - Color: Red, Radius: 5, Area: 78.5398
Rectangle - Color: Blue, Width: 4, Height: 6, Area: 24
Circle Area: 78.5398
Rectangle Area: 24

In this example:

  • Shape is the base class with a color_ attribute and virtual functions area() and display().
  • Circle and Rectangle are derived classes that inherit from Shape using public inheritance. They inherit the color_ attribute and the getColor() method.
  • Both Circle and Rectangle override the area() and display() methods to provide specific implementations for their shapes.

Inheritance allows us to reuse the common properties and behaviors of the Shape class in the Circle and Rectangle classes, while also allowing them to have their own unique characteristics and implementations.

Go Back

Next page