Множественное наследование




Лекция №7. Механизмы наследования.

Наследование – это механизм об объектно-ориентированного программирования позволяющий описать новый класс на основе уже существующего (Родительского), при этом свойства и функционал родительского класса используется новым.

Другими словами, класс-наследник реализует уже существующую спецификацию базового(родительского) класса.

Наследование позволяет:

· Повысить коэффициент повторного использования кода,

· Логически правильно строить структуру программного обеспечения.

Виды наследования.

Простое наследование:

Классы, от которых происходит наследования, называются базовыми и родительскими. Классами-наследниками или производными классами называют те, которые произошли от базового(родительского) класса.

Важное замечание: При наследовании не наследуется конструктор класса. И если в базовом классе есть конструктор с параметрами, то производный класс должен вызвать в своем конструкторе один из конструкторов базового класса. Если не вызвать конструктор базового класса – компилятор выдаст ошибку.

Механизм наследования выглядит следующим образом:

class Human

{

public:

std::string name; // имя

int age; // возраст

Human(std::string n, int a)

{

name = n; age = a;

}

void display() // Метод, который выводит информацию о Human на экран.

{

std::cout << "Name: " << name << "Age: " << age << std::endl;

}

};

class Student: public Human // Механизм наследования в котором мы наследуем

//весь public из класса Human.

{

public:

Student(std::string n, int a, std::string c):Human(n, a)

private:

std::string SMK; // Переменная, куда мы можем записать название колледжа.

 

};

int main()…

Абстрактные классы – это класс, содержащий хотя бы один абстрактный метод (метод, который не имеет реализации) описанный в программе. Данные классы не предполагают создания экземпляров класса.

Для того, чтобы в производном классе можно было переопределить метод, мы используем оператор virtual, чтобы сделать метод виртуальным(полиморфным). При этом производный класс может и не переопределять функцию, а использовать унаследованный функционал.

Virtual void dosomething()

{

std::cout << " do something " << std::endl;

}

 

Для того, чтобы переопределить виртуальный метод в производном классе мы используем оператор override:

void dosomething() override;

{

std::cout << " do something else " << std::endl;

}

На практике, абстрактные классы являются реализацией полиморфизма в ООП.

Интерфейс

В языке С++ не предоставлено отдельного ключевого слова для обозначения интерфейса, тем не менее реализация интерфейса реализуется путем создания чистого абстрактного класса, в котором присутствуют только декларации методов (указаны только заголовки). Такие классы часто называют абстрактными базовыми классами.

Множественное наследование

Множественное наследование происходит, когда подкласс имеет два или более родительских класса.

Проблемы множественного наследования:

Множественное наследование требует тщательного проектирования, так как может привести к непредвиденным последствиям. Большинство таких последствий вызваны неоднозначностью в наследовании. В данном примере Hero наследует метод say() от обоих родителей и неясно какой метод должен быть вызван.

#include <iostream>

using namespace std;

class Creature {

public:

string kind;

string name;

Creature(string n, string k) { //создаем существо

name = n;

kind = k;

cout << "It’s Alive!!!" << endl;

cout <<"Kind of Creature: "<< kind << endl;

cout <<"Name of Creature: "<< name << endl;

}

virtual void say(){

cout << "Creature:"<< "Make a sound" << endl;

 

}

~Creature() { //уничтожаем существо:о

cout << "It’s dead:(((" << endl;

}

};

class Human: public Creature{

public:

string name;

string message;

Human(string n, string k):Creature(n,k) { //вызываем конструктор Creature

}

void say(){

cout << "Human:"<< "Say something" << endl;

}

~Human() {

}

};

class Alien: public Creature {

public:

string name;

string message;

Alien(string n, string k):Creature(n,k) { //вызываем конструктор Creature

}

void say(){

cout << "Alien:"<< "Say something like alien" << endl;

}

~Alien() {

}

};

 

class Hero: public Human,public Alien

{

public:

int Health;

Hero(int h, string n, string k): Human(n,k), Alien(n,k)

{

Health = h;

cout << "Your hero with: "<< h << endl;

}

~Hero() {

cout << "Your hero is dead." << endl;

}

 

};

 

int main()

{

 

Hero MyHero(100,"QWERTY","Human");

MyHero.Human::say(); //Что бы не было ошибки нужно указать, что say из Human/Alien

 

}

 

Проблема ромба


Проблема ромба (Diamond problem)- классическая проблема в языках, которые поддерживают возможность множественного наследования. Эта проблема возникает когда классы B и C наследуют A, а класс D наследует B и C.

 

К примеру, классы A, B и C определяют метод say(). Если say() будет вызываться классом D, неясно какой метод должен быть вызван — метод класса A, B или C. Разные языки по-разному подходят к решению ромбовидной проблем. В C ++ решение проблемы оставлено на усмотрение программиста.

Ромбовидная проблема — прежде всего проблема дизайна, и она должна быть предусмотрена на этапе проектирования. На этапе разработки ее можно разрешить следующим образом:

· вызвать метод конкретного суперкласса;

· обратиться к объекту подкласса как к объекту определенного суперкласса;

· переопределить проблематичный метод в последнем дочернем классе (в коде — say() в подклассе Alien).

Проблема ромба с конструкторами и деструкторами.
Поскольку в С++ при инициализации объекта дочернего класса вызываются конструкторы всех родительских классов, возникает и другая проблема: конструктор базового класса Creature будет вызван дважды. Для того, чтобы избежать подобных ситуаций требуется внимательное составление плана работы над программой. Все конструкторы и методы должны логично быть описаны. В следующем варианте был оптимизирован код, в котором класс Creature содержит только важные данные для конкретного существа. Все остальное по иерархии влияния было перенесено в тело других классов.

Виртуальное наследование предотвращает появление множественных объектов базового класса в иерархии наследования. Таким образом, конструктор базового класса Creature будет вызван только единожды, а обращение к методу say() без его переопределения в дочернем классе не будет вызывать ошибку при компиляции.

#include <iostream>

using namespace std;

class Creature {

public:

int HP; //у существа есть важный параметр

Creature() { //создаем существо

HP = 100;

cout << "Creature have:"<< HP <<"HP" << endl;

}

void say(){ //просим произвести звук.

cout << "Creature:"<< "Make a sound" << endl;

}

~Creature() { //уничтожаем существо:о

cout << "It’s dead:(((" << endl;

}

};

class Human: public virtual Creature{ //наследуем say и параметр HP

public:

Human() {

}

void say() { //добавляем функционал. Человек умеет говорить.

cout << "Human:"<< "Say something" << endl;

}

~Human() {

}

};

class Alien: public virtual Creature { //наследуем say и параметр HP

public:

Alien() {

}

void say() {//добавляем функционал. Просим сказать что-то по инопланетянски.

cout << "Alien:"<< "Say something like alien" << endl;

}

~Alien() {

}

};

class Hero: public Human,public Alien

{

public:

string name;

string kind;

Hero(string n, string k)

{

name = n;

kind = k;

cout << "Your hero with name: " << n << endl;

cout << "Your hero with kind: " << k << endl;

}

void heat(){

HP = HP - 100;

cout << "HP: " << HP << endl;

}

void say() {

cout << "Hero:"<< "Say something like Hero" << endl;

}

~Hero() {

cout << "Your hero is dead." << endl;

}

};

int main()

{

Hero MyHero("QWERTY","Human");

MyHero.Human::say();

MyHero.heat();

 

}

 



Поделиться:




Поиск по сайту

©2015-2024 poisk-ru.ru
Все права принадлежать их авторам. Данный сайт не претендует на авторства, а предоставляет бесплатное использование.
Дата создания страницы: 2021-11-26 Нарушение авторских прав и Нарушение персональных данных


Поиск по сайту: