Добавил:
Developer Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Магистратура Языки программирования С,C++ / Доклад на тему №8 Наследование, перегрузка, переопределение.docx
Скачиваний:
3
Добавлен:
17.03.2024
Размер:
1.31 Mб
Скачать

1.5 Проблема ромбовидного наследования: Конструкторы и деструкторы

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

Листинг №1.5.1 – Программный код

#include <iostream>

using namespace std;

class Device {

public:

Device() {

cout << "Device constructor called" << endl;

}

};

class Computer : public Device {

public:

Computer() {

cout << "Computer constructor called" << endl;

}

};

class Monitor : public Device {

public:

Monitor() {

cout << "Monitor constructor called" << endl;

}

};

class Laptop : public Computer, public Monitor {};

int main() {

Laptop Laptop_instance;

return 0;

}

1.6 Интерфейс

С++, в отличии от некоторых ООП языков, не предоставляет отдельного ключевого слова для обозначения интерфейса (interface). Тем не менее, реализация интерфейса возможна путем создания чистого абстрактного класса (pure abstract class) — класса в котором присутствуют только декларации методов. Такие классы также часто называют абстрактными базовыми классами (Abstract Base Class — ABC).

Листинг №1.8.1 – Программный код

#include <iostream>

using namespace std;

class Device {

public:

virtual void turn_on() = 0;

};

class Laptop : public Device {

public:

void turn_on() {

cout << "Device is on." << endl;

}

};

int main() {

Laptop Laptop_instance;

Laptop_instance.turn_on();

// Device Device_instance;

// will cause compile time error

return 0;

}

Несмотря на то, что наследование — фундаментальный принцип ООП, его стоит использовать с осторожностью. Важно думать о том, что любой код, который будет использоваться скорее всего будет изменен и может быть использован неочевидным для разработчика путем.

1.7 Вывод по наследованию

Наследование предоставляет множество преимуществ, но должно быть тщательно спроектировано во избежание проблем, возможность для которых оно открывает. В контексте наследования, С++ предоставляет широкий спектр инструментов, который открывает массу возможностей для программиста.

2 Неявное приведение типов

Приведение типов — это явление, при котором тип данных преобразуется из одного типа в другой, чтобы упростить вычисление исходного кода.

Неявное приведение типов – это автоматическое приведение типа, выполняемое компилятором, когда требуется один тип данных, но предоставляется другой тип. Подавляющее большинство преобразований типов в C++ являются неявными преобразованиями типов. Например, неявное преобразование типа происходит во всех следующих случаях:

Листинг № 2.1 – Программный код

double d{ 3 }; // значение int 3 неявно преобразуется в тип double

d = 6; // значение int 6 неявно преобразуется в тип double

double division{ 4.0 / 3 }; // значение int 3 неявно преобразуется в тип double

if (5) // значение int 5 неявно преобразуется в тип bool

void doSomething(long l)

{

}

doSomething(3); // значение int 3 неявно преобразуется в тип long

Когда вызывается преобразование типа (неявно или неявно), компилятор определяет, может ли он преобразовать значение из текущего типа в запрашиваемый тип. Если допустимое преобразование может быть найдено, компилятор выдаст новое значение запрашиваемого типа. Обратите внимание, что преобразование типов не меняет значение или тип преобразуемого значения или объекта.

Если компилятор не может найти приемлемое преобразование, то компиляция завершится ошибкой. Преобразование типов может завершиться неудачей по любому количеству причин. Например, компилятор может не знать, как преобразовать значение между исходным и запрашиваемым типами.

В других случаях инструкции могут запрещать определенные типы преобразований.

Листинг № 2.2 – Программный код

// Инициализация с фигурными скобками запрещает преобразования,

// которые приводят к потере данных

int x{ 3.5 };

Стандарт языка C++ определяет, как различные базовые типы (и в некоторых случаях составные типы) могут быть преобразованы в другие типы. Эти правила преобразования называются стандартными преобразованиями.

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

  • числовые продвижения;

  • числовые преобразования;

  • арифметические преобразования;

  • другие преобразования (включая различные преобразования указателей и ссылок).

Когда требуется преобразование типа, компилятор увидит, есть ли стандартные преобразования, которые он может использовать для преобразования значения в запрашиваемый тип. В процессе преобразования компилятор может применить ноль, одно или несколько стандартных преобразований.

3 Protected

Парадигма объектно-ориентированного программирования предусматривает регулировать уровень доступность к данным и методам класса. Одни элементы класса могут быть общедоступны, другие — скрыты.

Уровень доступности определяется с помощью специальных ключевых слов, которые называются модификаторами доступа.

Модификаторы доступа применяются в следующих случаях:

  • В случае объявления элементов класса;

  • В случае наследования классов.

Одним из модификатора доступа является ключевое слово protected, о котором я расскажу поподробнее.

Protected открывает доступ тем классам, которые унаследованы от данного. Так же доступ имеют те классы и методы, которые обозначены, как “friend” или дружественными.

Рассмотрим примеры. Поле класса, которое отмечено, как protected недоступен из внешнего недружественного метода из экземпляра класса. При этом к нему есть доступ из внутреннего метода, как к члену класса.

Рисунок 3.1. Доступ из экземпляра класса

Если в классе объявлен дружественный метод или дружественный класс, то элемент класса будет из них доступен.

Рисунок 3.2. Доступ из дружественного класса или метода

Элемент класса доступен из унаследованного класса:

Рисунок 3.3. Доступ из унаследованного класса

Элемент класса недоступен из экземпляров унаследованного класса.

Рисунок 3.4. Нет доступа из экземпляров унаследованного класса

В C++ существует protected-наследование.

Модификатор доступа protected ограничивает доступ из экземпляров унаследованных классов и не ограничивает доступ из методов унаследованных классов. Если два класса A, B образуют иерархию наследования, и класс A объявлен базовым с модификатором protected, то действуют следующие правила:

  • все protected — и public-элементы класса A доступны из методов класса B (рисунок 3.5);

  • все protected — и public-элементы класса A доступны из методов класса, унаследованного от класса B (класса C)

  • нет доступа к любому элементу класса A из экземпляров производных классов (рисунок 3.6).

Рисунок 3.5. Элементы базового класса, объявленные как protected или public, доступны из методов унаследованного класса

Рисунок 3.6. Нет доступа к членам базового класса A из экземпляров любых унаследованных классов (B, C, D и т.д.)

При protected наследовании данные, которые были в родительском классе public и protected становятся protected.

Листинг №1.8.1 – Программный код изменения модификаторов полей при protected наследовании

Следовательно, использование protected наиболее полезно, когда вы собираетесь наследоваться от ваших собственных классов, и количество производных классов не слишком велико. Таким образом, если вы вносите изменения в реализацию базового класса и в результате обновления производных классов не будет длиться вечно, поскольку их количество ограничено.