OOPS Cheatsheet

Object Oriented Programming is just a programming paradigm. It is a set of rules, ideas, concepts and a standard used in programming to solve a specific set of problems. There are other paradigms too. Here the main motive is to be able to represent real life entities or objects along with their features or characteristics and behaviors and be able to explain those to our computer and in our program. Example - A car. A car has many different attributes like manufacturer, color, price, max speed and many more. And some of their behaviors are driving, accelerating, stopping, starting, showing the amount of fuel left. With OOPS, we can represent a car (which is a real life entity) in our program.

Advantages of OOPS

OOPS makes development and maintenance easier whereas in Procedural paradigm it is not easy to manage if code grows as project size grows. OOPS provides data hiding whereas in procedural paradigm a global data can be accessed from anywhere. OOPS provides the ability to simulate real world events.

Class

How can we represent a real life entity like a car in a program or any other entity in general? By using Classes. Classes are building blocks of OOP. A class is a user defined data type. Data types like int, float, char etc are built-in data types. An entity that has state and behavior is known as an object. Example - chair, pen, table, car. Object is a physical or runtime entity, class is a logical entity. Class is just a blueprint of an object. Object is an instance of a class.

Everything inside class in CPP is private by default.

Access Modifiers

AMs are used to implement an important aspect of OOP called Data Hiding. These help in restricting the scope of a class, constructor, variable, method or a data member.

  1. Private - Not going to be accessible outside of your class. Hidden. Only accessible by the class where it is declared. In C++, by default the access modifier is private.

  2. Public - Able to access it outside of your class as well.

  3. Protected - In-between private and public. Subclass can access it and anywhere. In the case of Java if that subclass is present in another package, even then it is accessible.

  4. Default - Available in Java. No keyword required. Not accessible in a different package.

#include<iostream>
using std::string;

// A class doesn't represent data
// It's just a blueprint

class Employee {
  // Access Modifier
  public:
    // Attributes
    string name;
    string company;
    int age;
    
    // Behavior
    void introduceYourself() {
      std::cout << "Name - " << name << std::endl;
      std::cout << "Company - " << company << std::endl;
      std::cout << "Age - " << age << std::endl;
    }
};

int main() {
  // Creating an instance of Employee class
  // employee1 is an object of Employee class
  Employee employee1;
  employee1.name = "Ninjavin";
  employee1.company = "Amazon";
  employee1.age = 20;
  
  employee1.introduceYourself();
  
  Employee employee2;
  employee2.name = "Vineeta";
  employee2.company = "Super good";
  employee2.age = 19;
  
  employee2.introduceYourself();
  
  return 0;
}

A base class pointer can point to a derived class object, but we can only access base class members or virtual functions using the base class pointer.

Constructors

A constructor is a special type of method which is invoked each time an object of a class is created. Two types of constructor: Default and Parameterized. Default Constructor - A constructor which is automatically created by our compiler. It is present if we do not create our own.

  1. A constructor must not have a return type. (Not even void, you simply do not write anything)

  2. A constructor has the same name as the class it belongs to.

  3. Constructor must be public. (In most of the cases)

#include<iostream>
using std::string;

class Employee {
  public:
    string name;
    string company;
    int age;

    void introduceYourself() {
      std::cout << "Name - " << name << std::endl;
      std::cout << "Company - " << company << std::endl;
      std::cout << "Age - " << age << std::endl;
    }
    
    Employee(string _name, string _company, int _age) {
      name = _name;
      company = _company;
      age = _age;
    }
};

int main() {
  Employee employee1 = Employee("Ninjavin", "Amazon", 20);
  employee1.introduceYourself();
  
  Employee employee2 = Employee("Vineeta", "Super good", 19);
  employee2.introduceYourself();
  
  return 0;
}

Encapsulation

The idea of encapsulation is the idea of bundling or tying together data and methods that operate on that data. They are grouped together within a class. Why do we do this? We do this with the purpose of preventing anyone outside our class from being able to directly access our data and modify it.

Talking about Bluetooth, we usually have it on our mobile. When we switch on Bluetooth, I am able to connect to another mobile or bluetooth enabled device but I'm not able to access the other mobile features like dialing a number, accessing inbox etc. This is because Bluetooth features are given some level of abstraction. Just like a pill "encapsulates" or contains the medication inside of its coating, the principle of encapsulation works in a digital way to form a protective barrier around the information that separates it from the rest of the code. Programmers can replicate this object throughout different parts of the program or other programs.

How do we access encapsulated properties of a class? Through its methods. And these methods are implemented as getters and setters.

#include<iostream>
using std::string;

class Employee {
  private:
    // Encapsulated Properties : can't be accessed directly
    string name;
    string company;
    int age;

  public:
    void setName(string _name) {
      name = _name;
    }

    string getName() {
      return name;
    }

    void setCompany(string _company) {
      company = _company;
    }

    string getCompany() {
      return company;
    }

    void setAge(int _age) {
      if (_age >= 18)
        age = _age;
    }

    int getAge() {
      return age;
    }
    
    void introduceYourself() {
      std::cout << "Name - " << name << std::endl;
      std::cout << "Company - " << company << std::endl;
      std::cout << "Age - " << age << std::endl;
    }
    
    Employee(string _name, string _company, int _age) {
      name = _name;
      company = _company;
      age = _age;
    }
};

int main() {
  Employee employee1 = Employee("Ninjavin", "Amazon", 20);
  employee1.introduceYourself();
  
  Employee employee2 = Employee("Vineeta", "Super good", 19);
  employee2.introduceYourself();
  
  employee1.setAge(40);
  std::cout << employee1.getAge() << std::endl; // prints 40
  
  return 0;
}

Abstraction

Abstraction means hiding complex things behind a procedure that makes those things look simpler. Example - Our smartphone. All our smartphones can take pictures. For us, it's as simple as clicking on the Camera icon and pressing the click button. But behind the scenes it's way more complex than this. While everything is so easy and simple on our side, all the complexity is hidden from us by the company producing smartphones. And most importantly we do not even need to know about those complexities.

*C++ can simulate Java interface using abstract classes.

#include<iostream>
using std::string;

// this class will serve as a contract
class AbstractEmployee {
  // make this obligatory which means
  // we will force any class signing contract 
  // to implement this method
  
  // Virtual Function or Abstract Function
  virtual void askForPromotion() = 0;
};

class Employee:AbstractEmployee {
  private:
    string name;
    string company;
    int age;
  
  public:
    void setName(string _name) {
      name = _name;
    }
    string getName() {
      return name;
    }
    void setCompany(string _company) {
      company = _company;
    }
    string getCompany() {
      return company;
    }
    void setAge(int _age) {
      if (_age >= 18) {
        age = _age;
      }
    }
    int getAge() {
      return age;
    }
    
    void introduceYourself() {
      std::cout << "Name - " << name << std::endl;
      std::cout << "Company - " << company << std::endl;
      std::cout << "Age - " << age << std::endl;
    }
    
    Employee(string _name, string _company, int _age) {
      name = _name;
      company = _company;
      age = _age;
    }
    
    void askForPromotion() {
      if (age>30)
        std::cout << name << " got promoted." << std::endl;
      else
        std::cout << name << " can't get a promotion." << std::endl;
    }
};

int main() {
  Employee employee1 = Employee("Ninjavin", "Amazon", 40);
  Employee employee2 = Employee("Vineeta", "Super good", 19);
  
  employee1.askForPromotion();
  employee2.askForPromotion();
  
  return 0;
}

Inheritance

When one object acquires all the properties and behaviours of the parent object it is called Inheritance.

  • Provides code reusability.

  • Used to achieve runtime polymorphism.

There is a base class. And then there is a derived class. Base class has certain attributes and behaviours. And if the derived class decides to inherit some of the base class attributes, behaviours it is called inheritance. Then the derived class has additional members specific to the class too. Example - Suppose a car is a base class. Then we can have electric cars, conventional cars as derived class. An electric car has all the properties a car has. But it also has some additional properties. It will have a method charge attribute for battery status. Conventional cars will have methods like tanks.

A base class pointer can point to a derived class object, but we can only access base class members or virtual functions using the base class pointer.

Basic Mobile functionality is to send a message, dial and receive a call. So the brands of mobile are using this basic functionality by extending the mobile class functionality and adding their own new features to their respective brands.

Inheritance is private by default.

class Developer:public Employee
#include<iostream>
using std::string;

class AbstractEmployee {
  virtual void askForPromotion() = 0;
};

class Employee:AbstractEmployee {
  private:
    string company;
    int age;
  
  protected:
    string name;
  
  public:
    void setName(string _name) {
      name = _name;
    }
    string getName() {
      return name;
    }
    void setCompany(string _company) {
      company = _company;
    }
    string getCompany() {
      return company;
    }
    void setAge(int _age) {
      if (_age >= 18)
        age = _age;
    }
    int getAge() {
      return age;
    }
    
    void introduceYourself() {
      std::cout << "Name - " << name << std::endl;
      std::cout << "Company - " << company << std::endl;
      std::cout << "Age - " << age << std::endl;
    }
    
    Employee(string _name, string _company, int _age) {
      name = _name;
      company = _company;
      age = _age;
    }
    
    void askForPromotion() {
      if (age>30)
        std::cout << name << " got promoted." << std::endl;
      else
        std::cout << name << " can't get a promotion." << std::endl;
    }
};

class Developer:public Employee {
  public:
    string favLanguage;
    
    Developer(string _name, string _company, int _age, string _favLanguage):Employee(_name, _company, _age) {
      favLanguage = _favLanguage;
    }
    
    void fixedBug() { 
      std::cout << name << " fixed all the bugs using " << favLanguage << "." << std::endl;
    }
};

class Teacher:public Employee {
  public: 
    string subject;
    
    void prepareLesson() {
      std::cout << name << " is preparing " << subject << " lesson." << std::endl;
    }
    
    Teacher(string _name, string _company, int _age, string _subject):Employee(_name, _company, _age) {
      subject = _subject;
    }
};

int main() {
  Developer d = Developer("Ninjavin", "Amazon", 36, "C++");
  Teacher t = Teacher("Jack", "South Point", 35, "History");
  d.fixedBug();
  d.askForPromotion();
  t.prepareLesson();
  return 0;
}

Polymorphism

Poly means β€˜many’ and morph means β€˜forms’. It describes the ability of an object or a method to have many forms. The most common use of polymorphism in programming is when a parent class reference is used to refer to an object of a child class. Let's say Samsung mobile has a 5MP camera available i.e. – it has the functionality of CameraClick(). Now the same mobile is having Panorama mode available in camera, so functionality would be the same but with mode.

In C++, we use function overloading and function overriding to achieve polymorphism.

#include<iostream>
using std::string;

class AbstractEmployee {
  virtual void askForPromotion() = 0;
};

class Employee:AbstractEmployee {
  private:
    string company;
    int age;
  
  protected:
    string name;
  
  public:
    void setName(string _name) {
      name = _name;
    }
    string getName() {
      return name;
    }
    void setCompany(string _company) {
      company = _company;
    }
    string getCompany() {
      return company;
    }
    void setAge(int _age) {
      if (_age >= 18) {
        age = _age;
      }
    }
    int getAge() {
      return age;
    }
    
    void introduceYourself() {
      std::cout << "Name - " << name << std::endl;
      std::cout << "Company - " << company << std::endl;
      std::cout << "Age - " << age << std::endl;
    }
    
    Employee(string _name, string _company, int _age) {
      name = _name;
      company = _company;
      age = _age;
    }
    
    void askForPromotion() {
      if (age>30)
        std::cout << name << " got promoted." << std::endl;
      else
        std::cout << name << " can't get a promotion." << std::endl;
    }
    
    // Virtual Function - checks if this Function
    // is implemented in derived classes
    virtual void work() {
      std:: cout << name << " is checking emails." << std::endl;
    }
};

class Developer:public Employee {
  public:
    string favLanguage;
    
    Developer(string _name, string _company, int _age, string _favLanguage):Employee(_name, _company, _age) {
      favLanguage = _favLanguage;
    }
    
    void fixedBug() { 
      std::cout << name << " fixed all the bugs using " << favLanguage << "." << std::endl;
    }
    
    void work() {
      std:: cout << name << " is writing " << favLanguage << " code." << std::endl;
    }
};

class Teacher:public Employee {
  public: 
    string subject;
    
    void prepareLesson() {
      std::cout << name << " is preparing " << subject << " lesson." << std::endl;
    }
    
    Teacher(string _name, string _company, int _age, string _subject):Employee(_name, _company, _age) {
      subject = _subject;
    }
    
    void work() {
      std:: cout << name << " is teaching " << subject << " lesson." << std::endl;
    }
};

int main() {
  Developer d = Developer("Ninjavin", "Amazon", 36, "C++");
  Teacher t = Teacher("Jack", "South Point", 35, "History");
  
  d.work(); // Ninjavin is writing C++ code.
  t.work(); // Jack is teaching History lesson.
  
  Employee *e1 = &d;
  Employee *e2 = &t;
  
  e1->work(); // Ninjavin is writing C++ code.
  e2->work(); // Jack is teaching History lesson.
  
  return 0;
}

C++ Pointers

#include<iostream>
using namespace std;

int main() {
  int n = 5;
  cout << n << endl;
  cout << &n << endl; // returns address of n
  // Assigning the address of n variable to a pointer
  int *ptr = &n;
  cout << ptr << endl; // return same value as &n
  *ptr = 10;
  cout << n << endl; // returns 10
  cout << ptr << endl; // still the same
  
  return 0;
}

Use of pointers

  1. To pass values by reference to a function

  2. Return multiple values from a function

  3. Combination of arrays

  4. For Dynamic Memory Allocation

  5. In Polymorphism

Void Pointers

A special type of pointer that can hold the address of a variable of any data type (be it float, char, int, struct). Void pointers cannot be directly dereferenced.

#include<iostream>
using namespace std;

void printNum(int *nPtr) {
  cout << *nPtr << endl;
}
void printLetter(char *lPtr) {
  cout << *lPtr << endl;
}

void print(void *ptr, char type) {
  switch(type) {
    case 'i': cout << *((int*)ptr) << endl; break;
    case 'c': cout << *((char*)ptr) << endl; break;
    default: break;
  }
}

int main() {
  int n=5;
  char letter = 'a';
  
  // printNum(&n); // returns 5
  // printLetter(&letter); // returns a
  
  print(&n, 'i');
  print(&letter, 'c');
  
  return 0;
}

Pointers and Arrays

int arr[5] = {0, 1, 2, 3, 4};

If we cout << arr << endl; this will return the address of the first number (0 in this case). This means arr behaves as a pointer and the square brackets behave as a dereferencing.

#include<iostream>
using namespace std;

int main() {
  int luckyNumber[5] = {2, 3, 5, 7, 9};
  
  cout << luckyNumber << endl; // returns address of the first element of the array
  cout << &luckyNumber[0] << endl; // same as luckyNumber
  
  cout << luckyNumber[2] << endl; // returns 5
  cout << *(luckyNumber+2) << endl; // should return 5 too
  
  for (int i=0 ; i<5 ; i++) {
    cout << *(luckyNumber + i) << " ";
  }
  cout << endl;
  
  return 0;
}

Return multiple values from a function using pointers

#include<iostream>
using namespace std;

int getMin(int numbers[], int size) {
  int min = numbers[0];
  for (int i=1 ; i<size ; i++) {
    if (numbers[i] < min)
      min = numbers[i];
  }
  return min;
}

void getMinAndMax(int numbers[], int size, int *min, int *max) {
  for (int i=1 ; i<size ; i++) {
    if (numbers[i] < *min)
      *min = numbers[i];
    if (numbers[i] > *max)
      *max = numbers[i];
  }
}

int main() {
  int numbers[5] = {5, 4, -1, 29, 6};
  // cout << getMin(numbers, 5) << endl;
  
  int min = numbers[0];
  int max = numbers[0];
  
  getMinAndMax(numbers, 5, &min, &max);
  
  cout << max << endl;
  cout << min << endl;
  
  return 0;
}

Dynamic Arrays

An array is a type of collection that stores elements in continuous memory. It stores elements one after the other.

#include<iostream>
using namespace std;

int main() {
  // Creating array at runtime
  int size;
  cin >> size;

  int *arr = new int[size];
  
  for (int i=0 ; i<size ; i++) {
    cin >> arr[i];
  }
  
  for (int i=0 ; i<size ; i++) {
    cout << arr[i] << " ";
  }
  cout << endl;
  
  delete[] arr;
  arr = NULL;
  
  return 0;
}

Memory Leak

Static and Dynamic Binding

Binding means associating the call of a function with the definition of that function.

Static Binding - All of the information necessary in order to perform that association is available at compile time, so that association happens at compile time. Another name for this is Compile-time binding, or early binding. Faster. This happens by default. Achieved by normal function calls or function overloading (same function name with different data types or number of parameters).

#include<iostream>
using namespace std;

float sum(float a, float b) {
  return a+b;
}

float sum(float a, float b, float c) {
  return a+b+c;
}

int main() {
  // We associate this function call to the
  // first one at compile time
  cout << sum(1, 2) << endl;
  cout << sum(1, 2, 3) << endl;
  return 0;
}

Dynamic Binding - Performed during run time because all of the information needed to perform this binding is not available at compile time. Late binding or run time binding. Slower. It is very flexible and allows us to decide which function definition we want to invoke at run time. Happens when we use virtual function or function overriding (child class same function as parent class).

#include<iostream>
#include<list>
using namespace std;

class User {
  public:
    virtual void getPermissions() {
      cout << "Users can see limited info!" << endl;
    }
};

class Superuser:public User {
  void getPermissions() {
    cout << "Superusers can see all the info!" << endl;
  }
};

int main() {
  User u;
  Superuser s;
  
  list<User*> users;
  users.push_back(&u);
  users.push_back(&s);

  for (User* userPtr: users) {
    userPtr->getPermissions();
  }
  /*
    Users can see limited info!
    Superusers can see all the info!
  */
  return 0;
}

Operator Overloading

#include<iostream>
#include<string>
using namespace std;

struct YouTubeChannel{
  string Name;
  int SubscribersCount;
  
  YouTubeChannel(string name, int subscribersCount) {
    Name = name;
    SubscribersCount = subscribersCount;
  }
};

ostream& operator<<(ostream& COUT, YouTubeChannel &ytChannel) {
  COUT<<"Name: " << ytChannel.Name << endl;
  COUT << "Number of subscribers: " << ytChannel.SubscribersCount << endl;
  return COUT;
}

int main() {
  YouTubeChannel yt1 = YouTubeChannel("Vineeta Jain", 75000);
  YouTubeChannel yt2 = YouTubeChannel("Ninjavin Jain", 80000);
  cout << yt1 << yt2;
  return 0;
}
operator<<(cout, yt1); // We can also call the operator like this

We use pass by reference when we have parameters which are not cheap to copy. Examples of parameters which are cheap to copy are numbers, but parameters like string, user-defined data type, a list, a vector are not very cheap to copy. So those are passed by reference.

Function Overloading

#include<iostream>
using namespace std;

int sum(int a, int b);
double sum(double a, double b);
float sum(float a, float b, float c);

int main() {
  cout << sum(3, 5) << endl;
  cout << sum(3.5, 5.2) << endl;
  cout << sum(1.2, 1.5, 1.2) << endl;
  return 0;
}

int sum(int a, int b) {
  return a+b;
}

double sum(double a, double b) {
  return a+b;
}

float sum(float a, float b, float c) {
  return a+b+c;
}

Structures

#include<iostream>
#include<string>
using namespace std;

struct Smartphone{
  string name;
  int storageSpace;
  string color;
  float price;
};

// Nested Structures
struct Person {
  string name;
  int age;
  Smartphone smartphone;
};

void printSmartphoneDetails(Smartphone smartphone) {
  cout << "Name: " << smartphone.name << endl;
  cout << "Storage Space: " << smartphone.storageSpace << endl;
  cout << "Color: " << smartphone.color << endl;
  cout << "Price: " << smartphone.price << endl;
  cout << endl;
}

void printPersonDetails(Person p) {
  cout << "Name: " << p.name << endl;
  cout << "Age: " << p.age << endl;
  printSmartphoneDetails(p.smartphone);
}

int main() {
  Smartphone s1;
  s1.name = "Galaxy A10s";
  s1.storageSpace = 32;
  s1.color = "Black";
  s1.price = 10999.99;
  
  printSmartphoneDetails(s1);
  
  Person p;
  p.name = "Ninjavin";
  p.age = 20;
  p.smartphone = s1;
  
  printPersonDetails(p);
  
  return 0;
}

File Handling

Using namespace std

Namespaces are used to resolve conflicts that we get when two or more things have the same name.

#include<iostream>
using std::cout;
using std::endl;
using std::string;

namespace namespace1 {
  int age = 25;
  string name = "Ninjavin";
}

namespace namespace2 {
  int age = 26;
}

int main() {
  cout << namespace1::age << endl; // 25
  cout << namespace2::age << endl; // 26
  
  cout << namespace1::name << endl; // Ninjavin
  
  return 0;
}

Further Reading:

  1. Why doesn’t C support function overloading?

  2. What is the difference between global int and static int declaration?

Why is n++ faster than n=n+1?

n++ is a unary operation and needs just one variable whereas n=n+1 is a binary operation that adds overhead to take more time. But nowadays in modern compilers, n=n+1 gets converted into n++ when it goes into the optimized binary, and it is equivalently efficient.

What are the advantages of macro over function?

Difference between struct and union in C.

Call by reference in functions.

Pass by reference in functions.

Question 1

#include<iostream>
using namespace std;
class Base1 {
 public:
     Base1() { cout << "Base1's constructor called" << endl;  }
};
  
class Base2 {
 public:
     Base2() { cout << "Base2's constructor called" << endl;  }
};
  
class Derived: public Base1, public Base2 {
   public:
     Derived() {  cout << "Derived's constructor called" << endl;  }
};
  
int main()
{
   Derived d;
   return 0;
}

Output:

Base1's constructor called
Base2's constructor called
Derived's constructor called

When a class inherits from multiple classes, constructors of base classes are called in the same order as they are specified in inheritance. Destructors are always called in reverse order of constructors.

β€˜this’ Pointer

β€˜this’ refers to the current instance of the class.

  1. It can be used to pass the current object as a parameter to another method.

  2. It can be used to refer to the current class instance variable.

  3. It can be used to declare indexers.

static keyword

β€˜static’ is a keyword or modifier that belongs to the type not instance. So instance is not required to access the static members. In C++, static can be field, method, constructor, class, properties, and more.

Advantage: Memory Efficient - Since we do not need to create an instance for accessing the static members, it saves memory. Moreover, it belongs to the time so it will not get memory each time when an instance is created.

Java Signed Right Shift >> Unsigned Right Shift >>> instanceof operator Buffered Reader final keyword - We can use this keyword with variable, method and class. With a variable, when we want that variable stays constant and its value doesn’t change. With method, when we want that the method doesn’t override. With class, when we want that class doesn’t get inherited. Threads in Java Interfaces in Java

Exception handling in Java An exception is an unwanted or unexpected event, which occurs during the execution of a program i.e. at run time, that disrupts the normal flow of the program.

C Essentials %p format specifier returns address of the pointer variable

Java Essentials There is no sizeof operator in Java.

  • operator precedence is more than == operator. Operator overloading is not performed in Java. Calling a non-static function inside a static function is not allowed. Parameters are passed by value in Java. Objects are not passed in Java, only references are passed. Unlike C++, we can’t have inline functions in Java. Java doesn’t support default arguments in functions. delete(x,y) deletes the elements from the string at position β€˜x’ to position β€˜y-1’. str.toUpperCase() returns str in uppercase but does not change the original string β€˜str’. str.substring(x, y) returns string from position β€˜x’ (inclusive) to position β€˜y’ (exclusive). Unlike C++, in Java all non primitive objects must be explicitly allocated and these objects are allocated on heap. In Java, fields of classes and objects that do not have an explicit initializer and elements of arrays are automatically initialized with the default value for their type (false for boolean, 0 for all numerical types, null for all reference types). Local variables in Java must be definitely assigned before they are accessed, or it is a compile error.

Last updated