Understanding Access Specifiers in C++: Public, Private, and Protected

Access specifiers in C++ are used to control the visibility and accessibility of class members (variables and methods).

In this article, we will explore the three key access specifiers in C++: public, private, and protected, and how they shape the way your classes are used and maintained. These access specifiers define how the members of a class can be accessed and who can access them.

Public Access Specifier

The public specifier is used to allow class members to be accessed from any part of your program. When you declare a class member as public, you are essentially making it accessible to everyone. Public members of a class can be accessed from anywhere in the program using the direct member access operator (.) with the object of that class.

Here are some use cases where it is appropriate to use the public access specifier:

  • Use public data members when you intend to make them accessible for both read and write operations.
  • Use public member functions when you want to enable users to interact with the class's behavior.

Private Access Specifier

The private access specifier is used to limit the access to class members solely within the class itself. Private members are hidden from the outside world, ensuring the integrity of your class's data and protecting it from unintended modifications.

Here are some use cases where it is appropriate to use the private access specifier:

  • Use private data members for handling sensitive data that should not be modified directly.
  • Use Private member functions to encapsulate the core behavior and operations of your class, preventing external manipulation.

Protected Access Specifier

The protected access specifier is used to restrict direct access to class members. It's similar to private access but with a subtle difference. The protected members cannot be accessed from outside the class, but they can be accessed by derived classes, enabling a controlled level of access for subclasses.

Here are some use cases where it is appropriate to use the protected access specifier:

  • Use protected members when you want to provide derived classes with access to specific aspects of the base class without exposing them to the world.
  • Use protected member functions when you want derived classes to inherit and override specific member functions while maintaining data encapsulation.

Example

Here's a simple example that demonstrates the use of access specifiers in C++:

#include <iostream>
#include <string>

class Customer {
private:
    double bankBalance; // Private variable
    std::string bankAccountNumber; // Private variable

public:
    std::string firstName; // Public variable
    std::string lastName; // Public variable
    std::string email; // Public variable

protected:
    std::string id; // Protected variable

public:
    // Public method to set customer details
    void setCustomerDetails(std::string first, std::string last, std::string email, std::string id) {
        firstName = first;
        lastName = last;
        this->email = email; // Using "this" to differentiate from the member variable "email"
        this->id = id; // Using "this" to differentiate from the member variable "id"
    }

    // Public method to set bank balance (private member)
    void depositBalance(double balance) {
        bankBalance = balance;
    }

    // Public method to display customer details
    void displayCustomerDetails() {
        std::cout << "Customer: " << firstName << " " << lastName << std::endl;
        std::cout << "Email: " << email << std::endl;
        std::cout << "Bank Account Number: " << bankAccountNumber << std::endl;
        std::cout << "ID: " << id << std::endl;
        std::cout << "Bank Balance: $" << bankBalance << std::endl;
    }

private:
    // Private method to set bank account number (private member)
    void setBankAccountNumber(std::string account) {
        bankAccountNumber = account;
    }

protected:
    // Protected method to display the customer's ID (protected member)
    void displayCustomerID() {
        std::cout << "Customer ID: " << id << std::endl;
    }
};

int main() {
    Customer customer;

    // Set customer details using the method
    customer.setCustomerDetails("Danny", "Abc", "danny.abc@example.com", "D12");

    // Set private member (bankBalance)
    customer.depositBalance(1500.0);

    // setBankAccountNumber method is private and is not accessible
    //customer.setBankAccountNumber("7890123456");

    // Display customer details
    customer.displayCustomerDetails();

    // displayCustomerID is protected and is not accessible directly
    //customer.displayCustomerID();

    return 0;
}

In this example, we define a Customer class with various access specifiers for class members. Private members, such as the bankBalance and bankAccountNumber variables, and the setBankAccountNumber method, are inaccessible from outside the class, ensuring data privacy. Public members, including firstName, lastName, email, and methods like setCustomerDetails, can be accessed externally for managing customer information. Additionally, the protected member, id, is accessible within the class and by derived classes, supporting inheritance. Access specifiers maintain data integrity and promote encapsulation and modular design in object-oriented programming.