В нашей предыдущей статье мы рассказали вам о наследовании в C++. В зависимости от способа создания класса или от того, сколько базовых классов наследует класс, существуют следующие типы наследования:
- Одиночное наследование
- Множественное наследование
- Многоуровневое наследование
- Иерархическое наследование
- Гибридное наследование
Типы наследования
Ниже приводится графическое представление различных типов наследования:
Мы рассмотрим каждый тип наследования с примерами ниже в данной статье:
1) Одиночное наследование
При одиночном наследовании класс происходит только от одного базового класса. Это означает, что существует только один подкласс, производный от одного суперкласса.
Одиночное наследование обычно объявляется следующим образом:
class subclassname : accessspecifier superclassname { //class specific code; }; |
Ниже приведен полный пример работы одиночного наследования:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
#include <iostream> #include <string> using namespace std; class Animal { string name=""; public: int tail=1; int legs=4; }; class Dog : public Animal { public: void voiceAction() { cout<<"Barks!!!"; } }; int main() { Dog dog; cout<<"Dog has "<<dog.legs<<" legs"<<endl; cout<<"Dog has "<<dog.tail<<" tail"<<endl; cout<<"Dog "; dog.voiceAction(); } |
Вывод данных:
Dog has 4 legs Dog has 1 tail Dog Barks!!! |
В программе есть класс Animal в качестве базового класса, из которого мы создали подкласс dog. Класс dog наследует все члены класса Animal и может быть расширен для включения собственных свойств, как видно из выходных данных.
Одиночное наследование является простейшей формой наследования.
2) Множественное наследование
Графически, множественное наследование можно представить так:
Множественное наследование — это тип наследования, при котором класс является производным от более чем одного класса. Как показано на приведенной выше диаграмме, класс C является подклассом, у которого есть класс A и класс B в качестве его родителя.
В реальной жизни ребенок получает наследование от отца и матери. Это можно рассматривать как пример множественного наследования.
Приведенная ниже программа отлично демонстрирует работу множественного наследования:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 |
#include <iostream> using namespace std; //пример множественного наследования class student_marks { protected: int rollNo, marks1, marks2; public: void get() { cout << "Enter the Roll No.: "; cin >> rollNo; cout << "Enter the two highest marks: "; cin >> marks1 >> marks2; } }; class cocurricular_marks { protected: int comarks; public: void getsm() { cout << "Enter the mark for CoCurricular Activities: "; cin >> comarks; } }; //Результатом является сочетание оценок по предмету и оценок за активную учебную деятельность class Result : public student_marks, public cocurricular_marks { int total_marks, avg_marks; public: void display() { total_marks = (marks1 + marks2 + comarks); avg_marks = total_marks / 3; cout << "\nRoll No: " << rollNo << "\nTotal marks: " << total_marks; cout << "\nAverage marks: " << avg_marks; } }; int main() { Result res; res.get(); //чтение меток тем res.getsm(); //чтение отметок об активной учебной деятельности res.display(); //отображение общего количества оценок и средних оценок } |
Вывод данных:
Enter the Roll No.: 25 Enter the two highest marks: 40 50 Enter the mark for CoCurricular Activities: 30Roll No: 25 Total marks: 120 Average marks: 40 |
В приведенном выше примере есть три класса, т.е. student_marks, cocurricular_marks и Result. Класс student_marks считывает предметную оценку учащегося. Класс cocurricular_marks считывает оценки учащегося за внеклассные занятия.
Класс Result вычисляет total_marks для учащегося вместе со средними оценками.
В этой модели класс Result является производным от student_marks и cocurricular_marks, поскольку мы вычисляем Result по предмету, а также по оценкам внеклассной деятельности.
Ниже, графически, представлена «Проблема ромба»:
На схеме указан дочерний класс, наследующий два класса: Father (отец) и Mother (мать). Эти два класса, в свою очередь, наследуют класс Person (человек).
Как показано на рисунке, класс Child (ребенок) дважды наследует признаки класса Person, т.е. один раз от отца и второй раз от матери. Это порождает двусмысленность, поскольку компилятор не понимает, что ему делать.
Поскольку этот сценарий возникает только тогда, когда получается ромбовидное наследование, эта проблема получила название «Проблема ромба».
Проблема ромба, реализованная в C++, приводит к ошибке неоднозначности при компиляции. Мы можем решить эту проблему, сделав корневой базовый класс виртуальным. Вы сможете прочитать об этом в нашей следующей статье «Полиморфизм в С++«.
3) Многоуровневое наследование
Схема многоуровневого наследования представлена ниже:
При многоуровневом наследовании класс является производным от другого производного класса. У этого наследования может быть сколько угодно уровней, если только наша реализация не запутается. На приведенной выше диаграмме класс C является производным от класса B. Класс B, в свою очередь, является производным от класса A.
Давайте посмотрим на пример многоуровневого наследования:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 |
#include <iostream> #include <string> using namespace std; class Animal { string name=""; public: int tail=1; int legs=4; }; class Dog : public Animal { public: void voiceAction() { cout<<"Barks!!!"; } }; class Puppy:public Dog{ public: void weeping() { cout<<"Weeps!!!"; } }; int main() { Puppy puppy; cout<<"Puppy has "<<puppy.legs<<" legs"<<endl; cout<<"Puppy has "<<puppy.tail<<" tail"<<endl; cout<<"Puppy "; puppy.voiceAction(); cout<<" Puppy "; puppy.weeping(); } |
Вывод данных:
Puppy has 4 legs Puppy has 1 tail Puppy Barks!!! Puppy Weeps!!! |
В данном программе мы изменили пример одиночного наследования таким образом, что появился новый класс Puppy, который наследуется от класса Dog, который, в свою очередь, наследуется от класса Animal. Отсюда мы можем видеть, что класс Puppy получает и использует свойства и методы обоих вышестоящих классов.
4) Гибридное наследование
Схема гибридного наследования изображена ниже:
Гибридное наследование обычно представляет собой комбинацию более чем одного типа наследования. В приведенном выше представлении есть множественное наследование (B, C и D) и многоуровневое наследование (A, B и D), что и дает нам получение гибридного наследования.
Давайте рассмотрим пример гибридного наследования:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 |
#include <iostream> #include <string> using namespace std; //Гибридное наследование = многоуровневое + множественное class student{ //First base Class int id; string name; public: void getstudent(){ cout << "Enter student Id and student name"; cin >> id >> name; } }; class marks: public student{ //derived from student protected: int marks_math,marks_phy,marks_chem; public: void getmarks(){ cout << "Enter 3 subject marks:"; cin >>marks_math>>marks_phy>>marks_chem; } }; class sports{ protected: int spmarks; public: void getsports(){ cout << "Enter sports marks:"; cin >> spmarks; } }; class result : public marks, public sports{//Производный класс от множественного наследования// int total_marks; float avg_marks; public : void display(){ total_marks=marks_math+marks_phy+marks_chem; avg_marks=total_marks/3.0; cout << "Total marks =" << total_marks << endl; cout << "Average marks =" << avg_marks << endl; cout << "Average + Sports marks =" << avg_marks+spmarks; } }; int main(){ result res;//объект// res.getstudent(); res.getmarks(); res.getsports(); res.display(); return 0; } |
Вывод данных:
Enter student Id and student name 25 Ved Enter 3 subject marks:89 88 87 Enter sports marks:40 Total marks =264 Average marks =88 Average + Sports marks =128 |
В программе есть четыре класса, т.е. «Student» (ученик), «Marks» (оценки), «Sports» (спорт) и «Result» (результат). Оценки выставляются классом ученика. Класс Result является производным от оценок и видов спорта, поскольку мы рассчитываем результат по предметным оценкам, а также по спортивным оценкам.
Выходные данные формируются путем создания объекта класса Result, который приобрел свойства всех трех классов.
Обратите внимание, что и при гибридном наследовании реализация может привести к «проблеме ромба», которую можно решить с помощью ключевого слова «virtual», но об этом в следующей статье.
5) Иерархическое наследование
При иерархическом наследовании от одного базового класса наследуется более одного класса, как показано в приведенной выше схеме. Это и делает данную структуру иерархической.
Ниже приведен пример, демонстрирующий иерархическое наследование:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 |
#include <iostream> using namespace std; //пример иерархического наследования class Shape // shape class -> base class { public: int x,y; void get_data(int n,int m) { x= n; y = m; } }; class Rectangle : public Shape // наследование класса Shape { public: int area_rect() { int area = x*y; return area; } }; class Triangle : public Shape // наследование класса Shape { public: int triangle_area() { float area = 0.5*x*y; return area; } }; class Square : public Shape // наследование класса Shape { public: int square_area() { float area = 4*x; return area; } }; int main() { Rectangle r; Triangle t; Square s; int length,breadth,base,height,side; //площадь прямоугольника std::cout << "Enter the length and breadth of a rectangle: "; cin>>length>>breadth; r.get_data(length,breadth); int rect_area = r.area_rect(); std::cout << "Area of the rectangle = " <<rect_area<< std::endl; //площадь треугольника std::cout << "Enter the base and height of the triangle: "; cin>>base>>height; t.get_data(base,height); float tri_area = t.triangle_area(); std::cout <<"Area of the triangle = " << tri_area<<std::endl; //площадь квадрата std::cout << "Enter the length of one side of the square: "; cin>>side; s.get_data(side,side); int sq_area = s.square_area(); std::cout <<"Area of the square = " << sq_area<<std::endl; return 0; } |
Вывод данных:
Enter the length and breadth of a rectangle: 10 5 Area of the rectangle = 50 Enter the base and height of the triangle: 4 8 Area of the triangle = 16 Enter the length of one side of the square: 5 Area of the square = 20 |
Приведенный выше пример является классическим примером класса Shape. В программе есть базовый класс Shape и три класса, то есть прямоугольник, треугольник и квадрат, производные от него.
У нас есть метод для чтения данных в классе Shape, в то время как каждый производный класс имеет свой собственный метод для вычисления площади. В основной функции мы считываем данные для каждого объекта, а затем вычисляем площадь.
Итог
По сравнению с другими языками программирования язык C++ поддерживает все типы наследования. На самом деле, можно сказать, что C++ имеет очень хорошую поддержку наследования. Благодаря этому, мы можем более эффективно моделировать проблемы в реальном времени.
В этой статье мы рассмотрели все типы наследования, поддерживаемые C++.
В нашей следующей статье мы поговорим о роли полиморфизма в C++, и приведем соответствующие примеры.
С Уважением, МониторБанк