Динамическое связывание частных методов: Java против переполнения стека

Это не разрешено в Java:

class A {
public void method() {}
}

class B extends A {
private void method() {}
}

Это генерирует ошибку компиляции:

error: method() in B cannot override method() in A
attempting to assign weaker access privileges; was public

Однако это разрешено в C ++:

class A {
public:
virtual void method() {}
};

class B : public A {
private:
void method() {}
};

int main(void) {
A* obj = new B();
obj->method(); // B::method is invoked, despite it being private
}

Какая логика стоит за этим поведением в C ++?

4

Решение

Помните, что видимость method разрешается чисто во время компиляции, C ++ не имеет понятия верификатора времени выполнения. То, что видит компилятор, является виртуальным A::method, который не частный. Конкретная реализация является объявлено как private, но это имеет значение только тогда, когда эта реализация вызывается напрямую способом, видимым для компилятора, то есть, если вы пытаетесь получить к нему прямой доступ, вызывая ее через B,

Логика этого иллюстрируется следующим случаем: представьте, если B не унаследовал от A публично, но в частном порядке — это разрешено в C ++ и используется, когда само наследование является деталью реализации, например, для stack наследование класса от vector, но не желая выставлять векторный интерфейс. В этом случае это будет особенность для B::method быть недоступным, но A::method работает нормально, даже если объект B пример.

Как сказал Kerrek SB, здесь Java защищает вас от одного класса ошибок ценой удаления допустимых опций.

3

Другие решения

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

Вот пример с блокировкой и проверкой постусловия:

class base {
public:
virtual ~base() = default;

// Calls the derived class' implementation in a thread-safe manner.
// @return A value greater than 42.
int function() {
std::lock_guard<std::mutex> guard(mutex);
auto result = function_impl();
assert(result > 42);
return result;
}

private:
std::mutex mutex;
virtual int function_impl() = 0;
};

class derived : public base {
private:
virtual int function_impl() override {
return 0; // Whoops! A bug!
}
};

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

Что касается приватизации, то в противном случае общественные участники разойдутся, если кто-то вдруг сделает function_impl публика в baseэто не сломает производные классы. Я не говорю, что это очень хорошая идея, но C ++ обычно просто предполагает, что вы знаете, что делаете, поэтому это такой гибкий язык.

2

По вопросам рекламы [email protected]