You get a bonus - 1 coin for daily activity. Now you have 1 coin

2.6. Inclusion and inheritance

Lecture



Let class B be a derived class of class D.

class B {...};

class D: public B {...};

The word public in the heading of class D says open inheritance. Open inheritance means that the derived class D is a subtype of class B, i.e. the object D is and the object B. Such inheritance is an is-a relation or they say that D is a type of B. Sometimes it is also called interface inheritance. With open inheritance, a variable of a derived class can be considered as a type variable of the base class. A pointer whose type is “pointer to base class” may point to objects that have the type of derived class. Using inheritance we build class hierarchy.

Consider the following class hierarchy.

class person {

protected:

char * name;

int age;

public:

person (char *, int);

virtual void show () const;

// ...

};

class employee : public person {

protected:

int work;

public:

employee) char *, int, int);

void show () const;

// ...

};

class teacher : public employee {

protected:

int teacher_work;

public:

teacher (char *, int, int, int);

void show () const;

// ...

};

We define pointers to objects of these classes.

person * pp;

teacher * pt;

Create objects of these classes.

person a (Petrov, 25);

employee b (Korolev, 30.10);

pt = new teacher (“Timofeev”, 45.23,15);

View these objects.

pp = & a;

pp-> show (); // calls person :: show for object a

pp = & b;

pp-> show (); // calls employee :: show for object b

pp = pt;

pp-> show (); // calls teacher :: show for * pt object

Here the pointer of the base class pp points to objects of the derived classes employee, teacher, i.e. it is assignment compatible with pointers to objects of these classes. When calling the show function with the pp pointer, the show function of the class that pp actually points to the object is called. this is achieved by declaring the show function virtual, with the result that we have late binding.

Now suppose that class D has a member of class B.

class D {

public:

B b;

// ...

};

In turn, class B has a member of class C.

class B {

public:

C s;

// ...

};

Such an inclusion is called a has-a relationship. Using inclusion we build a hierarchy of objects.

In practice, there is the problem of choosing between inheritance and inclusion. Consider the classes “Airplane” and “Engine”. For beginners, it often occurs to make “Airplane” a derivative of “Engine”. This is not true because the plane is not an engine, it has an engine. One way to see this is to wonder if a plane can have multiple engines. Since this is possible, we should use inclusion, not inheritance.

Consider the following example:

class B {

public:

virtual void f ();

void g (); };

class D {

public:

B b;

void f (); };

void h (D * pd) {

B * pb;

pb = pd; // # 1 Error

pb-> g (); // # 2 is called B :: g ()

pd-> g (); // # 3 Error

pd-> bg (); // # 4 is called B :: g ()

pb-> f (); // # 5 is called B :: f ()

pd-> f (); // # 6 is called D :: f ()

}

Why are the lines # 1 and # 3 wrong?

In line # 1 there is no D * to B * conversion.

In line # 3, D has no member g ().

Unlike open inheritance, there is no implicit conversion from a class to one of its members, and a class containing a member of another class does not replace the virtual functions of that class.

If for class D to use open inheritance

class D: public B {

public:

void f ();};

that function

void h (D * pd) {

B * pb = pd;

pb-> g (); // call B :: g ()

pd-> g (); // is called :: g ()

pb-> f (); // call D :: f ()

pd-> f (); // call D :: f ()

}

does not contain errors.

Since D is a derived class of B, an implicit conversion from D to B is performed. The result is an increased dependence between B and D.

There are cases where you like inheritance, but you cannot allow such conversions.

For example, we want to reuse the base class code, but do not intend to treat the objects of the derived class as instances of the base class. All we want from inheritance is code reuse. The solution here is closed inheritance. Closed inheritance is not a subtype relation, or an is-a relationship. We will call it a like-a relationship (similar to) or implementation inheritance , as opposed to interface inheritance. Closed (as well as protected) inheritance does not create a type hierarchy. From the design point of view, closed inheritance is tantamount to inclusion, except for the issue of substitution of functions. An important application of this approach is the open inheritance from an abstract class, and at the same time a closed (or protected) inheritance from a specific class to represent the implementation.

Example 2.6.1

Example 2.6.1

// Binary search tree

// File tree.h

// Generalized tree

typedef void * Tp; // type of generalized pointer

int comp (Tp a, Tp b);

class node {// node

private:

friend class tree;

node * left;

node * right;

Tp data;

int count;

node (Tp d, Tp * l, Tp * r): data (d), left (l), right (r), count (1) {}

friend void print (node ​​* n);

};

class tree {// tree

public:

tree () {root = 0;}

void insert (Tp d);

Tp find (Tp d) const {return (find (root, d));}

void print () const {print (root);}

protected:

node * root; //root

Tp find (node ​​* r, Tp d) const;

void print (node ​​* r) const;

};

Binary tree nodes contain a generalized pointer to data. It will match the pointer type in the derived class. The count field contains the number of duplicate data entries. For a specific derived class, we need to write a function comp to compare the values ​​of a particular derived type. The insert () function places nodes in a tree.

void tree :: insert (TP d)

{node * temp = root;

node * old;

if (root == 0) {root = new node (d, 0,0); return;}

while (temp! = 0) {

old = temp;

if (comp (temp-> data, d) == 0) {(temp-> count) ++; return;}

if (comp (temp-> data, d)> 0) temp = temp-> left;

else temp = temp-> right;}

it (comp (old-> data, d)> 0) old-> left = new (d, 0,0);

else old-> right = new node (d, 0,0);

}

The Tp find (node ​​* r, Tp d) function searches the subtree with root r for the information represented by d.

Tp tree :: find (node ​​* r, Tp d) const

{if (r == 0) return 0;

else if (comp (r-> data, d) == 0) return (r-> data);

else if (comp (r-> data, d)> 0) return (find (r-> left, d));

else return (find (r-> right, d));

}

The print () function is a standard recursion for traversing a binary tree.

void tree :: print (node ​​* r) const

{if (r! = 0) {

print (r-> left);

:: print (r);

print (r-> right);

}

Each node uses an external function :: print ().

Now create a derived class that stores data pointers to char as data members.

// File s_tree.cpp

#include “tree.h”

#include

class s_tree : private tree {

public:

s_tree () {}

void insert (char * d) {tree :: insert (d);}

char * find (char * d) const {return (char *) tree :: find (d));

voif print () const {tree :: print ();}

};

In the s_tree class, the insert function uses an implicit char * to void * conversion.

The comp comparison function is as follows

int comp (Tp a, Tp b)

{return (strcmp ((char *) a, (char *) b));}

An external function is used to display the values ​​stored in the node.

print (node ​​* n) {

cout << (char *) (n-> data) << endl;

cout << n-> cout << endl;

}

Here, to explicitly cast the void * type to char *, we use the cast operation of the (type_name) expression. More reliable is the use of the operator static _cast < char *> (Tp)


Comments


To leave a comment
If you have any suggestion, idea, thanks or comment, feel free to write. We really value feedback and are glad to hear your opinion.
To reply

C ++ (C plus plus)

Terms: C ++ (C plus plus)