Как в Закрытый член функции вызван вне класса, можно написать следующий код:
#include <iostream>
class A {
public:
virtual void f() { std::cout << "A::f()"; }
};
class B : public A {
private:
void f() override { std::cout << "B::f()"; }
};
void g(A &g) { g.f(); }
int main() {
A a;
g(a);
a.f();
B b;
g(b);
b.f(); // compilation failure
}
Конечно, компилятор отказывается компилировать последнюю строку, потому что статический анализ кода показывает, что B::f()
определяется, но личное.
Что меня серьезно беспокоит, так это отношение к концептуальному обобщению / специализации. Обычно считается, что вы должны иметь возможность манипулировать экземпляром подтипа, по крайней мере, так же, как вы управляете экземпляром супертипа.
Это основа Принцип подстановки Лискова. В данном примере это соблюдается, когда g()
вызывается либо с аргументом типа A
или один из типа B
, Но последняя строка не принимается, и кажется, что в таком случае принцип замещения каким-то образом нарушается (рассмотрим вызов по имени, как в макроопределении #define h(x) (x.f())
).
Даже если можно считать, что принцип Лискова не нарушен (макросы не являются реальной частью языка, так что все в порядке), тот факт, что последняя строка дает ошибку времени компиляции, по крайней мере, означает, что объекты типа B
нельзя манипулировать как A
может быть. Чтобы B
это не специализация A
даже если вывод public
,
Таким образом, в C ++, используя public
деривация не гарантирует, что вы эффективно реализуете специализацию. Вам нужно учитывать больше свойств кода, чтобы быть уверенным, что у вас есть «правильная» специализация.
Я ошибся ? Может ли кто-нибудь дать мне оправдание? Мне нужен хороший семантический аргумент, я имею в виду, по крайней мере, что-то гораздо более сложное, чем у Страуструпа, например: «C ++ старается не ограничивать вас, вы можете использовать его, если хотите, и нет, если не хотите». Я думаю, что язык должен основываться на разумной модели, а не на огромном списке возможных трюков.
Задача ещё не решена.