Object oriented principles in C++

In the past, we showed we published the basics of object-oriented programming in C++. It should help you get started. Today we look at the oriented-oriented principles and their types. It’s basically the way we program our classes and access the objects. The main aim of using it is to tie the data and the functions that operate on them together so that no other part of the code can access this data except that function.

Object-Oriented Principles in C++

There are four object-oriented principles that you should be aware of, namely:

  • Encapsulation
  • Inheritance
  • Polymorphism
  • Abstraction

Encapsulation

Let’s start with encapsulation and see how to achieve it. Encapsulation is bundling the object’s data and procedures together. This concept can be explained through the following diagram.

Object encapsulation

An object is most likely to hide the data variables but allows the outside code to access methods that operate on the data, and this technique is known as data hiding. When the data is restricted to object’s methods, the data is protected from accidental changes. The code outside of the object need not know about the internal structure of the object’s data. If the programmer intends to change the structure of data inside the class, they will also update the class methods to accommodate those changes.

Now the question is, how do we achieve encapsulation and data hiding inside the class. Here’s an example of how we do it.

// This program demonstrates a simple class.
#include <iostream>
#include <cmath>
using namespace std;

 // Circle class declaration
 class Circle
 { 
 // radius variable made private applying the concept of data hiding
 private:
 double radius;

 public:
 void setRadius(double r)
 { radius = r; }

 double getArea()
 { return 3.14 * pow(radius, 2); }
 };

 int main()
 {
 // Define 2 Circle objects
 Circle circle1,
 circle2;

 // Call the setRadius function for each circle
 circle1.setRadius(1); // This sets circle1's radius to 1
 circle2.setRadius(2.5); // This sets circle2's radius to 2.5

 // Call the getArea function for each circle and
 // display the returned result
 cout << "The area of circle1 is " << circle1.getArea() << endl;
 cout << "The area of circle2 is " << circle2.getArea() << endl;

 }

Both the private and public access specifier work together to provide the data hiding principle. We have discussed the access specifiers in our previous articles. Encapsulation is also achieved as the data variable for a circle, and the methods calculating area is coupled inside a class called Circle.

Inheritance

In the real world, we find many specialized versions of generalized objects. An example of this would be that vehicle is a general object, and a car or bicycle is a specialized vehicle version. When an object is a specialized version of another object, there is an “is-a” relation between two objects. When two objects have an “is-a” relation, it means the second object has all the first object characteristics.

The principle of inheritance uses a subclass and a superclass. The superclass is the generalized class and the subclass is inherited from the superclass so it is the specialized class.

Let’s understand inheritance with a simple example of a university, where there are faculty, domestic staff, and students. For all of the above categories, we can have one superclass or a generalized class called person.

Basic Skeleton of the Person class
Basic Skeleton of the Person class

This class person can derive other classes like faculty and student. The code below shows inheritance between faculty and students derived from the class person.

#include<iostream>
#include <string>

 using namespace std;
// enum type used
 enum Discipline { ARCHEOLOGY, BIOLOGY, COMPUTER_SCIENCE };
 enum Classification { FRESHMAN, SOPHOMORE, JUNIOR, SENIOR };
//base class
 class Person
 {
 private:
 string name;
 public:
   // constructor
 Person() { setName(""); }
   // Parametrized constructor
 Person(string pName) { setName(pName); }
   // setter function
 void setName(string pName) { name = pName; }
   // getter function
 string getName() { return name; }
 };

// class Student is inherited from class Person
 class Student:public Person
 {
 
 private:
 // enum type variable declared
 Discipline major;
 // Person type pointer declared
 Person *advisor;
 public:
 void setMajor(Discipline d) { major = d; }
 Discipline getMajor() { return major; }
 void setAdvisor(Person *p) { advisor = p; }
 Person *getAdvisor() { return advisor; }
 };
// class Faculty is inherited from class Person
 class Faculty:public Person
 {
 private:
 Discipline department;
 public:
 void setDepartment(Discipline d) { department = d; }
 Discipline getDepartment( ) { return department; }
 };

// These arrays of string are used to print the
 // enumerated types.
 const string dName[] = {
 "Archeology", "Biology", "Computer Science"
 };

 const string cName[] = {
 "Freshman", "Sophomore", "Junior", "Senior"
 };

 int main()
 {
 // Create a Faculty object.
 Faculty prof;

 // Use a Person member function.
 prof.setName("Stephen Jones");

 // Use a Faculty member function.
 prof.setDepartment(COMPUTER_SCIENCE);
 cout << "Professor " << prof.getName()
 << " teaches in the " << "Department of ";

 // Get department as an enumerated type.
 Discipline dept = prof.getDepartment();

 // Print out the department in string form.
 cout << dName[dept] << endl;

 return 0;
 }
Code Output:
Professor Stephen Jones teaches in the Department of Computer Science

A unique feature about C++ is that it supports multiple inheritances. Multiple inheritances are when a derived class simultaneously derives 2 or more base classes.

Abstraction

Abstraction is basically an overview of something. It includes only the definition of the object without the implementation. An example of abstraction would be a washing machine. Many people know what a washing machine is. They also understand how to operate it, but only the technical people know the construction, organization, and components of a washing machine; this feature is known as abstraction.

Abstraction is widely used in the development, whether it’s a small program or large-scale applications. The sqrt function is an example where programmers know that sqrt is used to find the square root, but they might not know the implementation.

The question that arises in our minds is how we use abstraction in classes. An abstract method appears in the base class, and its implementation is provided in the derived classes.

#include<iostream>
using namespace std;
// base class
class Shape {
   public:
      virtual int area() = 0; // Pure Virtual Function : Function with declaration but no implementation
};
// derived class square inherits shape
class Square:public Shape {
   private:
  	  int length;
   public:
  	 // calculating area for square
      int area() {
         return length * length;
      }
};
// derived class rectangle inherits shape
class Rectangle:public Shape {
   private:
  	  int length;
  	  int breadth;
   public:
  // calculating area for rectangle
      int area() {
         return length * breadth;
      }
};
// derived class circle inherits shape
class Circle:public Shape {
   private:
  	  int radius;
  	  
   public:
  // calculating area for circle
      int area() {
         return 3.14 * radius * radius;
      }
};

int main() {
  // created a pointer of shape type
   Shape *shape;
  // created an object of square type
   Square s;
   shape = &s;
  // calculating area of a square 
  // invokes the area function present inside the square class
   shape->area();
}

We can make a class abstract in C++ by declaring a pure virtual function, as shown above, a pure virtual function is also called the abstract function. The instance of an abstract class cannot be created; thus, we can only create a pointer of the abstract class.

Important Points about Abstract classes

  • When a class contains a pure virtual function, it is an abstract class
  • The definition of a pure virtual function should end with 0
  • Abstract base class cannot be instantiated
  • Pure virtual functions must be overridden in the derived classes that are to be instantiated

Polymorphism

Polymorphism means more than one form. Function overloading, operator overloading and overriding are a depiction of polymorphism. A good example of polymorphism is the code demonstrated in abstraction where the function declaration area is the same as every class but calculates the area of different shapes.

#include<iostream>
#include <string>

 using namespace std;
// enum type used
 enum Discipline { ARCHEOLOGY, BIOLOGY, COMPUTER_SCIENCE };
 enum Classification { FRESHMAN, SOPHOMORE, JUNIOR, SENIOR };
//base class
 class Person
 {
 private:
 string name;
 public:
   // constructor
 Person() { setName(""); }
   // Parametrized constructor
 Person(string pName) { setName(pName); }
   // setter function
 void setName(string pName) { name = pName; }
   // getter function
 string getName() { return name; }
 };

// class Student is inherited from class Person
 class Student:public Person
 {
 
 private:
 // enum type variable declared
 Discipline major;
 // Person type pointer declared
 Person *advisor;
 public:
 // Constructor.
 Student(string sname, Discipline d, Person *adv): Person(sname) // Base constructor initialization
{
 major = d;
 advisor = adv;
 }
 
 void setMajor(Discipline d) { major = d; }
 Discipline getMajor() { return major; }
 void setAdvisor(Person *p) { advisor = p; }
 Person *getAdvisor() { return advisor; }
 };
// class Faculty is inherited from class Person
 class Faculty:public Person
 {
 private:
 Discipline department;
 public:
 void setDepartment(Discipline d) { department = d; }
 Discipline getDepartment( ) { return department; }
 };

class TFaculty: public Faculty
{
private:
 string title;
public:
 // This Constructor allows the specification of a title.
 TFaculty(string fname, Discipline d, string title)
 : Faculty(fname, d)
 {
 setTitle(title);
 }
 void setTitle(string title) { this->title = title; }
 // Override the getName function. POLYMORPHISM
 string getName( ){ return title + " " + name; }
};

// These arrays of string are used to print the
 // enumerated types.
 const string dName[] = {
 "Archeology", "Biology", "Computer Science"
 };

 const string cName[] = {
 "Freshman", "Sophomore", "Junior", "Senior"
 };
 int main()
 {
 // New constructor allows specification of title.
 TFaculty prof("Indiana Jones", ARCHEOLOGY, "Dr.");
 Student st("Sean Bolster", ARCHEOLOGY, &prof);

 // Use the new TFaculty version of getName.
 cout << prof.getName() << " teaches "
 << dName[prof.getDepartment()] << "." << endl;

 // This call uses the Person version of getName.
 Person *pAdvisor = st.getAdvisor();
 cout << st.getName() <<"\'s advisor is "
 << pAdvisor->getName() << ".";

 return 0;
 }
Code Output
Dr. Indiana Jones teaches Archeology.
Sean Bolster's advisor is Indiana Jones.

Overriding can only be done when inheritance exists. However, overloading can be done in the same class when methods have different parameter lists.

Conclusion

This article has discussed the four main pillars of object-oriented programming applicable to all object-oriented languages. Follow our tutorials to develop a deeper understanding of C++ through proper code implementation.

Similar Posts

Leave a Reply

Your email address will not be published. Required fields are marked *