Understanding Encapsulation in C++ through Access Control and Member Functions
Encapsulation combines data and the methods that operate on that data into a single unit, representing real-world entities. It also enforces controlled access to these internal components.
In C++, this unit is called a class. For example, we can design a Circle class to calculate its circumference.
#include <iostream>
const double PI_VALUE = 3.14159;
class CircularShape {
public: // Public access specifier
int shape_radius; // Attribute (data member)
double computePerimeter() { // Behavior (member function)
return 2 * PI_VALUE * shape_radius;
}
};
int main() {
CircularShape my_circle; // Instantiation of an object
std::cout << "Enter the circle's radius: ";
std::cin >> my_circle.shape_radius;
std::cout << "The circumference is: " << my_circle.computePerimeter() << std::endl;
return 0;
}
In this example, class defines a new type. The class body includes access specifiers, attributes (data members), and behaviors (member functions). The process of creating an object from a class is instantiation. An object's members are accessed using the dot (.) operator.
Access Control Levels
A class designer can place members under different access specifiers to enforce encapsulation. The three levels are:
- public: Members are accessible from any where (both inside and outside the class).
- protected: Members are accessible within the class and by derived classes (subclasses), but not from outside the class hierarchy.
- private: Members are accessible only within the class itself.
The distinction between protected and private becomes significant with inheritance; a subclass can access protected members of its parent but not private ones.
#include <iostream>
#include <string>
class Human {
public:
std::string person_name;
protected:
std::string vehicle;
private:
std::string secret_code;
public:
void initializeData() {
person_name = "Alex";
vehicle = "Tractor";
secret_code = "654321";
}
void displayName() {
std::cout << person_name << std::endl;
}
private:
void displayVehicle() {
std::cout << vehicle << std::endl;
}
void displaySecret() {
std::cout << secret_code << std::endl;
}
};
int main() {
Human person_obj;
person_obj.initializeData();
person_obj.displayName(); // Allowed: public member
// person_obj.displayVehicle(); // Error: protected member not accessible
// person_obj.displaySecret(); // Error: private member not accessible
person_obj.person_name = "Jamie"; // Allowed: public attribute
person_obj.displayName();
return 0;
}
Difference Between struct and class
In C++, struct and class are fundamentally the same except for they default access specifier.
- Members of a
structdefault to public access. - Members of a
classdefault to private access.
#include <iostream>
class ExampleClass { // Members are private by default
int internal_data;
};
struct ExampleStruct { // Members are public by default
int public_data;
};
int main() {
ExampleClass obj_c;
ExampleStruct obj_s;
// obj_c.internal_data = 10; // Error: private member
obj_s.public_data = 10; // Allowed: public member
return 0;
}
Benefits of Making Member Attributes Private
Declaring all data members as private offers key advantages:
- Fine-grained access control: You can precisely define read and write permissions through public member functions.
- Data validation: In the setter functions, you can implement logic to ensure the data being assigned is valid.
#include <iostream>
#include <string>
class Employee {
private: // All data is private
std::string emp_name; // Read-Write
int emp_age = 30; // Read-Only (no public setter)
std::string emp_motto; // Write-Only (no public getter)
public: // Public interface to manage private data
// Setter for name (Write permission with validation)
void setName(const std::string& new_name) {
if (!new_name.empty()) {
emp_name = new_name;
}
}
// Getter for name (Read permission)
std::string getName() const {
return emp_name;
}
// Getter for age (Read permission only)
int getAge() const {
return emp_age;
}
// Setter for motto (Write permission only)
void setMotto(const std::string& motto) {
emp_motto = motto;
}
};
int main() {
Employee worker;
worker.setName("Casey");
// worker.emp_age = 25; // Direct access is forbidden
std::cout << "Name: " << worker.getName() << std::endl;
std::cout << "Age: " << worker.getAge() << std::endl;
worker.setMotto("Keep coding!");
// std::cout << worker.emp_motto; // Cannot read write-only member
return 0;
}