Object-Oriented Programming
#
IntroductionC++ is a combination of a procedural and an object-oriented language. Object-Oriented programming is a programming paradigm that relies on the concept of classes and objects. OOP is based on the notion that you should divide your program not into tasks, but into models of physical objects. While this seems abstract at first, it becomes clearer when you consider physical objects in terms of their classes, components, properties, and behaviors.
#
Advantages of OOP- OOPs is closer to real world model.
- Hierarchical relationship among objects can be well-represented through inheritance.
- Data can be made hidden or public as per the need. Only the necessary data is exposed enhancing the data security.
- Increased modularity adds ease to program development.
- Private data is accessible only through designed interface in a way suited to the program.
#
Classinfo
The main difference between a class
and a struct
is that a struct's member variables and methods are public
, while a class's member variables and methods are private
.
Class is a blueprint from which objects are created. In C++, new types are mostly created using a class. To write object-oriented programs, we need to create a class, as a type, and then instantiate objects as instances of that type.
info
classes dont consume any space.
A class in C++ contains
- Data memebers: are the properties/attributes of class
- Methods: are the behaviours of the class
- Constructor: It's used to create instances of class (Object)
#
Data MembersData members are the properties that are present in a class. The type of these properties can be modified by the use of special keywords called modifiers. Let us build our own student class and learn about them.
#
Static and Non Static Properties :Static properties are those that are common to all objects and belong to the class rather each specific object. So each object that we create doesnât have their copy. They are shared by all the objects of the class. We need to write the static keyword before it in order to make it static.
An important point to note is that whenever we create a new object only the non static data member copies are created and the static properties are stored within the class only! This could be considered a very memory efficient practice as static members of a class are made only once.
#
Access Modifiersinfo
When there is no access modifier for a data member, it is private by default.
The instance member functions(methods) of a class are normally set to public.
- Private: If we make any data member as private it is visible only within the class i.e. it can be accessed by and through the methods of the same class. So we can provide setters and getters function through which they can be accessed outside the class
- Protected: It is only accesible within the class and sub-class
- Public: It is accesible everywhere
tip
It is better to make a variable private and then provide getters and setters in case we wish to allow others to view and change it than making the variable public. Because by providing setter we can actually add constraints to the function and update value only if they are satisfied
#
Methods :The second part of the class definition declares the member functions of the class; that is, it declares all functions that are used to simulate the behavior of the class. The methods are called on the object name by using the dot operator.
#
Static v/s Non Static MethodsLike data members, methods of a class can also be static which means those methods belong to the class rather than the objects for the class. These methods are directly called by the class name.
As the static methods belong to a class we donât need any instance of a class to access them. An important implication of this point is that the non static properties thus canât be accessed by the static methods as there is no specific instance of the class associated with them (the non static properties are specific to each object). So, non static members and the âthisâ keyword canât be used with the static functions. Thus these methods are generally used for the static properties of the class only!
The non static methods on the other hand are called on an instance of a class or an object and can thus access both static and non static properties present in the object. The access modifiers work the same with the methods as they do with the data members. The public methods can be accessed anywhere whereas the private methods are available only within the same class. Thus private methods can be used to work with the data members that we donât wish to expose to the clients.
#
Constructor and DestructorA constructor is a member function that creates an object when it is called and initializes the data members of an object when it is executed. The declaration of the data members in the class definition does not initialize the data members; the declaration just gives the names and the types of the data members
A constructor has two characteristics: It does not have a return value, and its name is the same as the name of the class. A constructor cannot have a return value (not even void) because it is not designed to return anything; its purpose is different. It creates an object and initializes the data members. Although we will see that a constructor may also do some other tasks, such as validation of values, these tasks are also considered part of the initialization.
We can have three types of constructors in a class
- parameter constructors
- default constructors
- copy constructors
caution
The parameter constructor can be overloaded for a class but not other constructors.
Like a constructor, a destructor has two special characteristics.
- the name of the destructor is the name of the class preceded by a tilde symbol (
~
), but the tilde is added to the first name, not the last name (the last name is the same for all member functions). - Like a constructor, a destructor cannot have a return value (not even void) because it returns nothing.
- A destructor can take no arguments, which means it cannot be overloaded.
A destructor is guaranteed to be automatically called and executed by the system when the object instantiated from the class goes out of scope. In other words, if we have instantiated five objects from the class, the destructor is automatically called five times to guarantee that all objects are cleaned up. Cleanup is most important if the constructed has called resources such as files. After the program is terminated, the allocated memory is recycled.
info
A constructor is a special member function that creates and initializes an object.
A destructor is a special member function that cleans and destroys an object.
If any memory has been dynamically allocated, then the destructor of an object must free this memory by using delete or delete[], depending on the data type. If a class allocates memory dynamically, an explicit destructor should be created to ensure that this memory is freed correctly.
#
Shallow Copy vs Deep CopyShallow copy, it runs through each member variable and assigns them the corresponding value of the object currently being copied. A shallow copy of an object copies all the members. This is usually fine when all the members are values. But, when a shallow copy is enacted on a pointer to dynamic memory, only the pointer is copied, not the memory it points to.
The implicit default copy constructor does the shallow copy.
To correctly handle the copy of our dynamically allocated memory, and we know that the compiler-generated copy constructor will not do this for us; we need to write our own.
note
When is a shallow copy not enough?
When a class has dynamically allocated memory, a deep copy is usually needed.
If you need to define an explicit copy constructor, then you will almost always need to also define an explicit copy assignment operator.
#
ObjectObject is the instance of the class. which will have the copy of the data members and methods that can be applied on the object.
Lets see the example of the class and object
#
EncapsulationEncapsulation combines a class's data and the member functions to act on that data. Operations on data in a class should only be possible through the members that class provides; member data should not be directly accessible. This is also known as data-hiding.
We can apply encapsulation design to our class by using access-modifiers
.
In the above example you can see that we cannot directly access the data members but we can access via member functions (methods) like print
and constructor itself to initialize values.
#
Getters and SettersA common technique for protecting data while still allowing sensible access is to use getters and setters. Unsurprisingly, a getter gets data and a setter sets data. Getters are commonly prefixed with the word get
and setters with the word set
.
Setters allow the setting of some data. It is worth noting that a direct setter will essentially break encapsulation as it exposes the variable to be changed again. One thing that a setter allows that a publicly exposed member variable does not is the validation of the data to be set.
#
AbstractionAbstraction and encapsulation are two sides of the same coin. Encapsulating data inside a class allows functionality on that data to be abstracted away, only exposing the methods the class design needs to make the class functional to a user and hiding all the nitty-gritty implementation details the class performs on its member data.
Abstraction provides only an essential interface to the user and hides the background details.
#
InheritanceWhen declaring a class in C++, we have the ability to inherit from another class. In fact, we can inherit from multiple classes at the same timeâa feature of C++ that not all object-oriented languages share. When we inherit from another class, we gain all its members that have either public or protected privacy modifiers. Private members remain visible only to the class in which they're defined, not the inheriting class.
This is one of the fundamental concepts in OOP and allows us to build flexible, maintainable objects where common functionality can be declared only once, then implemented and extended where needed.
info
If we want to prohibit a class from being inherited from, C++11 provides us with the final
keyword.
class ClassName final {};
#
Multiple InheritanceA single derived class can inherit variables and functionality from multiple base classes to create a more complex object.
#
Diamond ProblemIt's named after the shape of its inheritance diagram. Where two classes (B, C) inherit from one Class (A) and a class(D) inherits from both classes (B and C) the resultant class will have two copies of everythin within class(A).
The result of a class inheriting from two base classes that share a common base themselves.
This can be avoided in two ways:
- First is to qualify which version of the variable you want to access (
B::DataMember = 1
) - Through the use of virtual inheritance. When we use the virtual keyword when inheriting from a class, we ensure that only one copy of our base class's member variables will be inherited by any derived classes
This avoids duplicate properties and mitigates the diamond problem.
#
Access modifiers and inheritanceThere are two areas of accessibility we need to be aware of when making use of inheritance. The first is the accessibility of the members in our base class, and the second is the access modifier we define when inheriting from a class.
Base Class | Derived access with public inheritance | Derived access with protected inheritance | Derived access with private inheritance |
---|---|---|---|
public | public | protected | private |
protected | protected | protected | private |
private | private | private | private |
The most restrictive modifier wins.
Whenever two different modifiers are combined (such as a protected base class variable being inherited privately) it's the most restrictive modifier that sticks; in that case, it would be private
Understanding how different access modifiers affect inheritance is important, and often, a cause of confusion.
All variables, regardless of the access modifier, are fully visible to the class in which they are defined. Derived classes (those that inherit from a base class) can access public and protected members. Finally, the access modifier used when inheriting from the base class determines the final visibility of the members, and therefore, how all other classes can access them.
#
Virtual FunctionsIn C++, a virtual function is one that can have its functionality overridden by a derived class. To mark a function as virtual, we simply use the virtual
keyword at the start of its declaration.
This is done, first, by declaring a function with the same signature, return type, name, and the override keyword and then by defining it.
tip
Overriding virtual functions will work just fine without override
identifier, but it's good practice to include it.
Unlike virtual, override is not a keyword. It is instead an identifier with special meaning. It has no special meaning outside of the context of virtual functions.
In our overriding function, we can make a call to the base function through our base class type. This will run the logic defined in the base version of the function before running that of the overriding function.
#
Pure Virtual Functions (or) Abstract ClassesOverriding a normal virtual function is optional; however, if we want to force our users to implement a virtual function in the derived class, we can make it pure virtual in the base class. A pure virtual function does not have an implementation in the base class, it is merely declared.
Syntax: virtual void MyFunction() = 0;
If we don't want to provide a definition in the base class, but still want to make overriding the function optional, we can give it an empty body. Since we've given it an empty body, the class does not become abstract.
Syntax: virtual void MyFunction() {};
When a class contains one or more pure virtual functions, it becomes an abstract class.This is a class that cannot be directly instantiated.