Abstraction
- Enabling us to ignore the inner details of data structure/feature/functionality and focus on the higher-level functionality is the power of abstraction.
- It is one of the Object Oriented Concepts.
- So, it generalises the specific features. Eg: BlackBox testing
Note: Throughout this post, you can refer this github link
Concrete Class (Concrete Data Type CDT)
- Concrete class is a derived class that has all the missing functionality.
- The class which is instantiated with the keyword new is called concrete class
- It doesn't have any pure virtual function.
- Also, It doesn't hide the inner data structure.
Abstract Class (Abstract Data Type ADT)
- Abstract class is basically defined to be a base class.
- A class which can't / shouldn't be instantiated with the keyword new is called abstract class.
- In C++, abstract class should have at least one pure virtual function. It also means it can be overriden in the sub class. (Eg: Shape class has contains as pure virtual function in link)
virtual void function() = 0;
- Abstract class does hide the inner details/data structure.
Pure Virtual
- Pure virtual function is declared within a base class and defined in the derived class.
- If there is no overriding function, compiler will not allow to have pure virtual.
- Pure virtual function is not implemented in the base class and must be implemented in derived class. Click here for example (function contains)
- After this pure virtual function, compiler guarantees that class with pure virtual can't be instantiated.
Virtual + Pure = Abstract (Modelling of an object)
If the class has just normal object (not pointer), pure and virtual will not work or runtime polymorphism will not work.
Overriding
Same function name and signature in derived class, overrides base class. Virtual applies to overriding.
#include <iostream> using namespace std; class Army // Class declaration { public: virtual void warFighting() = 0; virtual void treatingPatients() {cout << __func__ << endl;} ; }; class ArmyHospital : public Army { public: string getName() { cout << " ArmyHospital " << __func__ << endl; return "Shirley"; } void treatingPatients() override { cout << " ArmyHospital " << __func__ << endl; } }; class ArmySchool : public ArmyHospital { public: string getName() { cout << "ArmySchool " << __func__ << endl; return "Rose"; } void warFighting() override { cout << "ArmySchool " << __func__ << endl; } }; int main() { ArmyHospital *hos = new ArmySchool(); hos->getName(); //ArmySchool cout << "\n"; //ArmyHospital hello = *hos; //hello.warFighting(); // Army cout << "\n"; Army *off = hos; off->warFighting(); // ArmySchool off->treatingPatients(); // Army Hospital cout << "\n" ; // error: invalid conversion from ‘Army*’ to ‘ArmySchool*’ [-fpermissive] //ArmySchool *sch = off; ArmySchool *sch = dynamic_cast<ArmySchool *>(off); sch->getName(); // ArmySchool return 0; }
Output:
ArmyHospital getName
ArmySchool warFighting
ArmyHospital treatingPatients
ArmySchool getName
Up Casting
Derived *dp =...
Base *bp = static_cast <Base*> (dp);
Use static cast or done implicitly.
Up-casting is implicit in C++, and is used a lot when you deal with virtual dispatching. In other words, you have a pointer to Base
from which you can access the common interface of a whole hierarchy of classes, and the selection can be done at runtime. This assumes that your interface functions are marked virtual
.
It is extremely useful in these situations in which the dispatch is done at runtime. Simply said, upcasting allows one to treat a derived class as a base class (via its common interface).
Down Casting
Base *bp =...
Derived *dp = static_cast <Derived *> (bp);
Undefined behavior if bp doesn't point to Derived.
Down-casting is less useful, and IMO should be avoided whenever one can. In general is a sign of bad design, as one rarely needs to convert a Base
object to a derived one. It can be done (and the result checked) via dynamic_cast
, like
Derived *dp = dynamic_cast <Derived *> (bp);
Use dynamic cast for run-time checking
Comments
Post a Comment