Object-Oriented Programming and Classes

Object-Oriented Programming

An object is a data structure which has associated data (variables) and behaviors (procedures). Objects work on their own data, communicating with outside units through an interface. Objects encapsulate related concepts and keep them unified. They may hide data from code outside the object. Generally speaking, an object would contain the procedures required to update and maintain its state. In most object-oriented programming languages, objects are abstract concepts that are represented in code by classes.

Object-oriented programming (OOP) grew from programming fields that simulated real-world, physical objects and it may be easiest to grasp by thinking of that model. These objects well defined and their interactions could be managed through the interface that each object provided. A user interface is a good example. We have various objects such as figures, text, menus, and so forth. The figures might be one-dimensional (lines, arrows, etc.) or two-dimensional (rectangles, ellipses, etc). They have characteristics such as length/radius, they have a state such as current size, color and location of their centers, and they have behaviors such as moving or rotating. Text has location and the content of the string that it displays. All these objects interact with each other, as the user takes various actions or the program executes.

For another example, return to the Employee struct. The struct collected data about an employee into a unified type. However, actions may be associated with an employee. Salaries could be raised, addresses could be updated, employees could join or quit the company, and so forth. We would implement procedures to carry out these actions and include them in the object.

Classes in C++

We define a class with the keyword class. A variable that is a member of the class is often called an attribute. A method is a procedure that is a member of a class. An invocation of a method may be called sending a message. The class definition contains full declarations of attributes, but only the prototypes of the methods. For now we will declare all members public so that they will be directly accessible by code that uses the class.

class MyClass{
   public:
     double var1, var2;
     int var3;
     double function1(double x, double y);
     double function2(double x);
};

The methods are defined using the scoping operator as class::function.

double MyClass::function1(double x,double y) {
    return var1*x+var2*y;
}
double MyClass::function2(double x) {
   var1=x*var2;
   return;
}

Notice that function2 does not return anything explicitly. This is because it acts upon a member of the class. Such methods should not return their results. In contrast, function1 returns a value to the outside caller.

An instance of a class is a variable of that class. Objects are represented in code by instances and sometimes the terms are used somewhat interchangeably.

MyClass A, B

A and B are instances of MyClass.

As for structs, we reference the members through the . operator.

   A.var1=10.;
   A.var2=11.;
   A.var1=7;
   A.function1(2.,9.);

Constructors and Destructors

A class always has a constructor and a destructor. If not provided by the programmer, the compiler will try to fill them in. The constructor is automatically called when an instance of the class is created. The constructor is invoked when a variable of the class type is declared. For a declaration of type pointer-to-class the constructor is called by the new operator. The constructor has the same name as the class. The destructor is called when the object is released. The destructor has the same name as the class but preceded by ~. Explicit destructors are often not required.

class MyClass{
   public:
      double var1, var2;
      int var3;
      MyClass(double v1, double v2, int v3);
      ~MyClass(); // destructors never have arguments
      double function1(double x, double y);
      double function2(double x);
};

MyClass::MyClass(double v1, double v2, int v3) {
   var1=v1; var2=v2; var3=v3;
   return;
}

MyClass::~MyClass(){
//Not much to do in this example, usually would not implement
}

double MyClass::function1(double x,double y) {
   return var1*x+var2*y;
}

double MyClass::function2(double x) {
   var1=x*var2;
   return;
}

Like a struct, a class is a scoping unit. Variables belonging to the class are in scope only within the class (specifically, its instance).

this

An instance variable is always passed to a method. This variable stands for the current instance, that on which the method is invoked. Some languages such as Python require that it be the first argument to any method. In Python it is conventionally represented by self.

def a_method(self,x,y):
   self.z=x+y

C++ does not pass the instance variable explicitly. If you need access to it in a method, use the this pointer. Since it is a pointer, it requires the arrow operator.

One example where it might be required is using the same variable name for an argument and an attribute.

MyClass::MyClass(x,y,z) {
   this->x=x;
   this->y=y;
   this->z=z;
}

Exercise

Convert the Employee struct to a class. Write a constructor for it. Incorporate an updateSalary method that takes a raise percentage (or fraction, your choice), computes the new salary, and updates the employee attribute. Why does it not have to take the employee’s current salary as a parameter?

Example Solution

#include <iostream>
#include <string>
#include <vector>

using namespace std;

class Employee {

   public:

   int     ID;
   std::string  name, manager, department;
   float   salary;
   
   Employee(int, std::string, std::string, std::string, float);
   void updateSalary(float);
};

Employee::Employee(int ID, std::string name, std::string manager, std::string department, float salary){
    this->ID=ID;
    this->name=name;
    this->manager=manager;
    this->department=department;
    this->salary=salary;
}

void Employee::updateSalary(float raise) {
    //takes percent
    salary+=salary*raise/100.;
}

int main() {

   vector<Employee> staff;
   int ID;
   string name, manager, department;
   float salary;

   staff.push_back(Employee(1234,"Fred Flintstone","Slate","Heavy Equipment Operations",45000.));
   staff.push_back(Employee(1235,"Barney Rubble","Slate","Engineering",43000.));

   for (int i; i<staff.size(); ++i) {
      if (staff[i].name=="Barney Rubble") {
          staff[i].updateSalary(3.);
      }
   }

   cout<<staff[1].name<<" now has a salary of "<<staff[1].salary<<endl;

   return 0;

}

Previous
Next