#c #oop #inheritance
#c #ооп #наследование
Вопрос:
Пожалуйста, посмотрите на следующий список кода:
#include <iostream>
using namespace std;
class Base {
public:
virtual void Message() = 0;
};
class Intermediate : public Base {
};
class Final : public Intermediate {
void Message() {
cout << "Hello World!" << endl;
}
};
int main() {
Final final;
/* Wont work (obviously):
Final* finalPtr = amp;final;
finalPtr->Message();
*/
// Works:
Intermediate* finalPtr = amp;final; // or Base* finalPtr = amp;final;
finalPtr->Message();
return 0;
}
Обратите внимание на следующее:
- В абстрактном базовом классе чисто виртуальная функция message() является общедоступной
- Промежуточный класс (также абстрактный) наследуется от базового класса (остается ли функция message() общедоступной чисто виртуальной в Intermediate?)
- Конечный класс наследуется от промежуточного класса, а функция message() является закрытой (по умолчанию)
- В main создается объект типа Final
- Создается промежуточный указатель на конечный объект
Вопрос: Если вы запустите программу, строка finalPtr->Message(); успешно вызывает реализацию функции message() функции Final, хотя она закрыта. Как это происходит? Переопределяет ли базовый класс или игнорирует ограничения доступа к производному классу?
Связанный вопрос: В связи с (2.) Выше, как правильно определить промежуточный класс? Нужно ли повторно объявлять чисто виртуальную функцию message() из базового класса, учитывая, что промежуточный класс не предназначен для предоставления реализации.
ПРИМЕЧАНИЕ: код был протестирован как с помощью Digital Mars Compiler (dmc), так и с помощью Microsoft Visual Studio Compiler (cl) и отлично работает в обоих
Ответ №1:
Как это происходит? Переопределяет ли базовый класс или игнорирует ограничения доступа к производному классу?
При общедоступном наследовании все общедоступные члены базового класса становятся общедоступными членами производного класса. Так что да Message()
, это общедоступная функция Intermediate
.
Функция вызывается по указателю базового класса ( Intermediate
). функция является общедоступной в базовом классе. Динамическая отправка (т.Е. Фактический вызов функции производного класса) происходит только во время выполнения, поэтому это работает.
Вышесказанное связано с тем, что во время выполнения спецификаторы доступа не имеют никакого значения, правила спецификатора доступа разрешаются и действуют только во время компиляции.
Если вы вызываете функцию для указателя производного класса, то во время компиляции компилятор обнаруживает, что Message()
это объявлено private
в Final
, и, следовательно, выдает ошибку.
Производный класс при выводе из абстрактного класса ДОЛЖЕН предоставлять определение для ВСЕХ чисто виртуальных функций базового класса, невыполнение этого требования приведет к тому, что производный класс также станет абстрактным классом.
Здесь Intermediate
class является абстрактным классом, и до тех пор, пока вам не нужно создавать объекты этого класса, он будет работать нормально. Обратите внимание, что вы можете создать указатель на абстрактный класс.
Комментарии:
1. Спасибо @Als. У меня просто есть комментарий: «При общедоступном наследовании все общедоступные члены базового класса становятся общедоступными членами производного класса. «Если вы посмотрите на прокомментированный код, указатель типа Final не будет вызывать функцию message() . Это означает, что конечный класс считает этот метод закрытым, а его базовый класс — нет. Это часть тайны.
2. @JohnGathogo: обновлено, чтобы ответить на ваш запрос.
3. «Динамическая отправка (т.Е. Фактический вызов функции производного класса) происходит только во время выполнения, следовательно, это работает» — это объясняет, почему это работает
4. «вы можете создать указатель на абстрактный класс». Я бы на это надеялся, иначе
virtual
функции имели бы гораздо меньшую полезность. 😛 Это основа шаблона «интерфейс». Сейчас я много работаю с абстрактными / чисто виртуальными базами, которые наследуются гетерогенными шаблонными типами и т. Д.
Ответ №2:
В C спецификаторы virtual и access являются взаимоисключающими. Именно по этой причине в C доступ может быть сужен для виртуальных методов, тогда как в C # или Java это невозможно.
Когда вы пытаетесь получить доступ к виртуальной функции через указатель базового класса, компилятор компилирует код, поскольку виртуальная функция базового класса является общедоступной.
В вашем прокомментированном коде виртуальная функция с ограниченным доступом вызывается через указатель конечного класса. Отсюда ошибка компиляции.