2016-03-12 3 views
4
#include <iostream> 
class A{ 
    public: 
     void k(){ 
      std::cout << "k from A"; 
     } 
}; 
class B:public A{ 
    public: 
     int k(){ 
      std::cout << "k from B"; 
      return 0; 
     } 
}; 
int main(){ 
    B obj; 
    obj.k(); 

    return 0; 
} 

Я пробовал то же самое в Java.Почему скрытие виртуальных функций/методов не разрешено в Java или C++

class X{ 
    public void k(){ 
     System.out.println("k From X"); 
    } 
} 
public class Y{ 
    public int k(){ 
     System.out.println("k From Y"); 
     return 0; 
    } 
} 

Java также показывает ошибку, когда я пробовал использовать другой тип возврата в подклассе. (Я думаю, потому что по умолчанию все методы экземпляра по умолчанию являются виртуальными). Я ожидал, что int k() должен скрыть void k() и int k() должен вызывать объект Y.

Так что я думаю, что это проблема с виртуальным. почему дочерний класс должен использовать тот же тип возврата, если функция объявлена ​​как виртуальная?

Если это проблема полиморфного поведения. Тогда я думаю, что объекта достаточно, чтобы определить вызов функции. Пример:

class X{ 
    public void k(){ 
     System.out.println("k From X"); 
    } 
} 
public class Y extends X{ 
    public int k(){ 
     System.out.println("k From Y"); 
     return 0; 
    } 
    public static void main(String[] args){ 
     X obj=new Y(); 
     obj.k(); // object Y found now just call k() from Y. 
    } 
} 

Может ли какой-либо орган объяснить? почему мы не можем изменить тип возвращаемого значения в подклассе или дочернем классе?

+0

Int к и ничтожной к не имеют один и тот же метод подписи? –

+0

@Yassin Мой последний пример также показывает ошибку в Eclips. – UnKnown

+0

@UnKnown Поскольку, если вы собираетесь переопределить, вы должны сохранить одну и ту же подпись. Если вы хотите перегрузить, список параметров должен отличаться. –

ответ

4

Вы правильно догадались, «полиморфный» является ключевым словом :) Полиморфизм означает, что, если Y подкласс X, то Y эффективно являетсяX, и может быть использован в качестве X в любом месте.

Теперь, это означает, что, если X имеет метод void k(), то Y должны также иметь такой же метод (в противном случае вы не сможете использовать его в качестве X). Но вы не можете иметь два разных метода с одной и той же сигнатурой, поэтому Y.k() также должен возвращать void (иначе это был бы другой метод).

В случае C++ не виртуальные функции не являются полиморфными: A.k и B.k - это два совершенно разных метода в этом случае, и поэтому ограничений нет.

Проще говоря, давайте изменим ваш пример немного: предположим, что вы определили X.k вернуть int и Y.k() в void. Представьте себе такую ​​функцию:

 int plusOne(X x) { 
     return x.k() + 1 
    } 

Это должно скомпилировать и работать, не так ли? Но как насчет plusOne(new Y())? Это должно также работать, потому что Y - X ... но, если бы можно было вернуть Y.k(), чтобы вернуть plusOne?

+0

Только одно. Я объявляю метод родительского метода окончательным, и после этого подкласса также требовал такой же тип возврата. Зачем? (конечные методы не являются виртуальными)? – UnKnown

+0

Нет, они все еще виртуальны, их просто невозможно переоценить. – Dima

+0

Прочитать комментарий в ответе dasblinkenlight. – UnKnown

2

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

Все не-частные/неконфиденциальные Java-методы являются виртуальными, поэтому к ним применяется тот же аргумент.

Что касается типа объекта, достаточного для определения способа вызова, вы можете не обязательно знать тип во время компиляции. Компилятор должен определить способ поиска метода статическим типом, включая виртуальный поиск. Для этого требуется, чтобы компилятор знал подпись и тип возврата.

+0

* «Все не приватные Java-методы являются виртуальными» * - Все не частные, а не конечные * Java-методы являются виртуальными. –

+0

@ChristianHackl Вы правы, спасибо! – dasblinkenlight

+0

'Для этого требуется, чтобы компилятор знал подпись и тип возврата.' Зачем возвращать тип? Вы можете объяснить, пожалуйста? – UnKnown

3

Если это проблема с полиморфным поведением. Тогда я думаю, что объекта достаточно, чтобы определить функцию вызова

Динамический полиморфизм происходит во время выполнения, но тип возвращаемого значения определяется во время компиляции.

Почему мы не можем изменить тип возвращаемого значения в подкласс или дочерний класс?

Подумайте о следующем примере (Чтобы быть удобно для объяснения я изменил свой образец кода немного о типе возвращаемого)

class A{ 
    public: 
     virtual int k(){ // returns int 
      std::cout << "k from A"; 
      return 0; 
     } 
}; 
class B:public A{ 
    public: 
     std::string k(){ // returns std::string 
      std::cout << "k from B"; 
      return std::string(); 
     } 
}; 
int main(){ 
    A* pa = new B; 
    int r = pa->k(); // r is supposed to be int, the type is deduced at compile time 

    delete pa; 
    return 0; 
} 

Вызов виртуальной функции f() с помощью базового класса указатель (или ссылки) , предполагается, что возвращается int, но в соответствии с результатом динамической отправки B::k() будет называться фактически, но он вернет совершенно другой тип (то есть std::string). Это противоречиво и плохо сформировалось.

Из стандарта C++, $ 10.3/7 Виртуальные функции [класс.Виртуальный]

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

0

Я понятия не имею, почему вы разместили этот вопрос снова, когда я уже gave you the answer here. Но вот правильный ответ.

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

struct Base { 
    virtual Base* f() { return nullptr; } 
}; 

struct Derived : Base { 
    Derived* f() override { return nullptr; } 
}; 

И в Java:

class Base { 
    Base f() { return null; } 
} 

class Derived extends Base { 
    @Override 
    Derived f() { return null; } 
}