Inheritance
Inheritance in object-oriented programming allows classes to derive attributes and methods automatically from another class. The derived class can redefine methods but not attributes, and additional attributes and methods can be defined. Visibility control of members in subclasses is important, with rules dictating the accessibility of superclass members in subclasses. Understanding these principles is key to designing effective class hierarchies.
Uploaded on Feb 16, 2025 | 0 Views
Download Presentation

Please find below an Image/Link to download the presentation.
The content on the website is provided AS IS for your information and personal use only. It may not be sold, licensed, or shared on other websites without obtaining consent from the author.If you encounter any issues during the download, it is possible that the publisher has removed the file from their server.
You are allowed to download the files provided on this website for personal or commercial use, subject to the condition that they are used lawfully. All files are the property of their respective owners.
The content on the website is provided AS IS for your information and personal use only. It may not be sold, licensed, or shared on other websites without obtaining consent from the author.
E N D
Presentation Transcript
principle of inheritance: the definition of the class can be derived from another one the derived class inherits (automatically) attributes and methods methods can be redefined (reprogrammed) in derived class attributes can't be redefined in derived class (data type of the attribute can't be changed) additional attributes and methods can be defined
notation: class derived_class: access base_class { new methods and attributes declaration; } newly declared class derived_class inherits from base_class access = one of the three keywords: public, private, protected access (visibility) control to inherited members
example let the class Car be defined the class PassengerCar derived from the class Car is declared: class PassengerCar: public Car { declaration; }
Terminology: class PassengerCar inherits attributes and methods from the class Car class PassengerCar is a descendant of the class Car class PassengerCar is a subclass of the class Car class Car is an ancestor of the class PassengerCar class Car is an superclass of the class PassengerCar
Visibility Control of members in the subclass relatively complicated in C++ the visibility (access) is controlled "at two places 1. in superclass 2. in subclass
Rules related to superclass: public members in superclass are always visible in subclass private members in superclass are never visible (accessible) in subclass if member of some class can be hidden for "outside world" but visible in subclass (accessible in subclass methods), they must be declared as protected in superclass
The second rule: the visibility is controlled by declaration of descendant
Rules related to subclass declaration: private: public members of superclass become private in subclass public: visibility is not changed (public members of superclass are public in subclass, protected members are protected in subclass protected: protected and public members of superclass are protected in subclass
class TVect2 { float x,y; public: int get_dimension() { return 2; } float size(){return sqrt(x*x+y*y);} float get_x() { return x; } float get_y() { return y; } void set(float px,float py){x=px; y=py;} }; class TVect3: public TVect2 { float z; public: int get_dimension() { return 3; } float size(); float get_z() { return z; } void set(float px, float py, float pz); };
class TVect3 inherited attributes x,y and methods get_x() get_y() from TVect2 the class TVect3 contains three attributes and six methods; three methods are overloaded (reprogrammed) inherited methods are public because of the declaration class TVect3: public TVect2
void TVect3::set(float px, float py, float pz) { TVect2::set(px,py); z = pz; } Note: the method set(px,py)can't be called over the object of TVect3 ancestor method is called
float TVect3::size() { return sqrt(x*x+y*y+z*z); } Error the method can't be implemented by this way! Inherited attributes are not visible in methods of TVect3 Why? Because they are private in superclass
How to solve the problem? 2 ways: 1. to use public methods get_x() and get_y() in the body of TVect3::velikost() 2. to declare attributes x,y as protected in TVect2
class TVect3: private TVect2 { protected: float z; public: TVect2 projection(); }; void main(void) { TVect3 v; v.get_x(); // is it correct or not? }
class TVect2 { protected: float x,y; public: int get_dimension() { return 2; } float size(){return sqrt(x*x+y*y);} float get_x() { return x; } float get_y() { return y; } void set(float px,float py){x=px; y=py;} };
class TVect3: public TVect2 { protected: float z; public: TVect2 projection(); int get_dimension() { return 3; } float size(); float get_z() { return z; } void set(float px, float py, float pz); };
TVect2 TVect3::projection() { TVect2 v; v.set(x,y); return v; }; void main(void) { TVect3 v; v.set(3,4,5); TVect2 v2D; v2D = v.projection(); }
void main(void) { TVect2 v1,v2; TVect3 v3,v4; v3.set(1,1,1); v1.set(0,0); v2.set(5,5); v1.get_x(); v1=v3; //OK descendant is assigned to ancestor v1.projection(); //error projection is not a member // of TVect2 v4=v2; // error ancestor can't be assigned to // descendant v4 = (TVect3)v2; // it can be done using typecasting v4.projection(); // it can return nonsense, because // additional attributes can be uninitialized }
Constructors, destructors and inheritance constructors can be defined in ancestors, descendant, only in ancestots, constructors are called in that order: ancestor, descendant destructors are called in that order: descendant, ancestor
explicit call of ancestor constructor we need to pass arguments for example descendant_constr(arg):ancestor_constr(arg) { code }
class TVect2 { private: float x,y; public: TVect2(); TVect2(float px, float py); }; class TVect3: public TVect2 { float z; public: TVect3(); TVect3(float px, float py, float pz); };
TVect2::TVect2 () { x=0; y=0; } TVect2::TVect2(float px, float py) { x=px; y=py; } TVect3:: TVect3():TVect2() { z = 0; }; ancestor constructor calling
TVect3:: TVect3 (float px, float py, float pz):TVect2(px,py) { z = pz; }; The question: Why can't be the ancestor constructor called like this? TVect3:: TVect3(float px, float py, float pz) { TVect2(px,py); z = pz; };
Useful approach: Declaration of common ancestor
class Car { public: int fabrication_year; int engine_capacity; string LP, car_make; int get_age(); }; class PassengerCar: public Car { public: int num_of_seats; };
class Truck: public Car { public: float load; };
Inheritance in UML Car +fabrication_year: int +engine_capacity: int +LP: string +car_make: string +get_age(): int Truck PassengerCar +load: float +num_of_seats: int
Assignment compatibility Rule: the descendant can be assigned to ancestor the ancestor can be assigned to descendant only using typecasting (can cause non-predicted behavior) Why ? The descendant has all attributes of the ancestor but the ancestor has not attributes defined in descendant. When the ancestor is assigned to descendant new attributes of descendant are not set.
Assignment compatibility void insert_car_into_list(Car *a); it can be passed pointer to the object as real parameter: Car PassengerCar Lorry
Multiple inheritance in C++ the class can be a descendant of several ascendants (parents) declaration: class C: public A, public B { };
the class C inherits attributes a methods of both classes A and B the problem is if the classes contain attributes and methods having the same identifier the class name must be user to distinguish the members class A { public int a; }; class B { public char a; }; class C: public A, public B { }; C c; c.A::a= 10; c.B::a = 'x';
Dynamic binding Virtual methods (Late binding)
Motivation ... class Point { protected: float x,y; public: int get_count_of_points() {return 1; } /* return count of control point of geometrical object */ };
derivation: class Line: public Point { protected: float x2,y2; public: int get_count_of_points() {return 2; } };
compatibility assignment: descendant can be assigned to ancestor void main(void) { Point b1, *b; Line u; Question: Which method is called? From class Point or from class Line? b = &b1; b-> get_count_of_points(); b = &u; b-> get_count_of_points(); }
Answer: the method from the class Point is called in both cases, because b is the pointer to the object of Point decision which method is called is done "statically" by compiler according to the type of pointer the mechanism that decides "dynamically" during runtime which methods is called, exists in C++
the mechanism is known as late binding (dynamic binding) methods which are called such way are named dynamically bound methods implementation is realized v C++ by virtual methods i. e. methods which can be bound dynamically are labeled with keyword virtual
the rule: "once virtual, always virtual the virtual methods must be labeled as virtual in all classes (superclasses and subclasses) virtual keyword is stated only in class declaration, not in implementation code! it can be declared as inline, but it is really nonsense (why?)
class Point { protected: float x,y; public: virtualint get_count_of_points(); }; class Line: public Point { protected: float x2,y2; public: virtualint get_count_of_points(); };
int Point::get_count_of_points() { return 1; } virtual keyword is not stated within implementation int Line::get_count_of_points() { return 2; }
void main(void) { Point b1, *b; Line u; b = &b1; The method from Point is called b-> get_count_of_points(); b = &u; The method from Line is called b-> get_count_of_points(); }
it works naturally if objects are created dynamically void main(void) { Point *b; b = new Point; b -> get_count_of_points(); delete b; b = new Line; b -> get_count_of_points(); delete b; }
ATTENTION: correct calling of virtual methods works only through pointers void main(void) { Point b1, b; Line u; b = b1; b.get_count_of_points(); b = u; b.get_count_of_points(); } The method from the Point is always called
How is the late binding implemented internally? address of virtual methods are stored in VMT (virtual method table) in the program it is found out during run time of the program to which class type instance pointer points and the appropriate address of the method is got out overhead causes slowdown of the program
Note: previous example with point and line is illustrative, but the programmer wouldn't implement in in reality better solution: common ancestor has declared the attribute count_of_control_points its value is set up to the correct value in constructor of the each descendant the method get_count_of_points() is not virtual and it is implemented in common ancestor
Pure virtual methods Abstract classes usual approach in object oriented analysis and programming: one class is defined which is a common ancestor of several classes
Abstract classes some virtual methods have no code in common ancestor because there is no sense to implement or it is not really possible to implement it in the common ancestor the code is implemented in descendants
methods without code are named as pure virtual methods; they are declared: virtual int method()=0; the class having at least one pure virtual method is called abstract class the abstract class can't be instanciated prevention to declare object with empty methods consequence: variables of abstract class can be declared only as pointers; they points to some instances of some descendant
Example simulation od finite state machine abstract class Automaton (ancestor) pure virtual method: response(int *in, int *out, int n) calculates output sequence out as response to the input sequence in of the length n descendants classes: Moore, Mealy the method response can't be implemented in ancestor because each type of the automaton (Moore, Mealy) calculates the output sequence by another way the method response is implemented only in descendant