2013-11-24 2 views
4

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

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++?

+0

Java оптимизирован для использования на 90%. C++ оптимизируется для 10%. – Mehrdad

+4

Java более ручная. Это сдерживает вас больше, чем C++, в надежде, что большинство «нормальных» программ соответствуют ограничениям и, таким образом, дает вам меньше возможностей для совершения ошибок. –

+0

С ++ и Java - разные языки для разных целей. – Leonidos

ответ

3

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

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

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

+0

Итак, в вашем примере, где класс 'B' наследует наследуемый от класса' A': вы описываете ситуацию, когда 'B' может найти необходимость переопределить какой-либо публичный' метод' 'A', но также может не захотеть публично публиковать «метод»? – Robz

+0

@ Robz Точно. Виртуальный метод будет вызываться только тогда, когда к объекту обращаются через его базу (и от других методов, если они вызывают его в своей реализации). – user4815162342

2

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

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

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++ обычно предполагает, что вы знаете, что делаете, следовательно, это такой гибкий язык.

+0

, так что разница в том, что в C++ производный класс может переопределять частный метод, тогда как Java запрещает его? –

+0

@BryanChen Это был вопрос, если я правильно понял. – rightfold

+1

Я не вижу, как это связано с фактическим * скрытием * публичной функции-члена в производном типе. Исходная ':: base :: function_impl' была уже закрыта для начала. В вопросе спрашивается, почему первоначально функция * public * member была бы законна, чтобы быть закрытой в производном типе. – bitmask