#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.
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.
}
}
Может ли тело объяснить? почему мы не можем изменить тип возвращаемого значения в подклассе или дочернем классе?
Вы правильно догадались, «полиморфный» — ключевое слово 🙂
Полиморфизм означает, что если 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
делать с этим?
Если это проблема полиморфного поведения. Тогда я думаю, что объекта достаточно, чтобы определить функцию вызова
Динамический полиморфизм происходит во время выполнения, но тип возвращаемого значения определяется во время компиляции.
почему мы не можем изменить тип возвращаемого значения в подклассе или дочернем классе?
Подумайте о следующем примере (для удобства объяснения я немного изменил ваш пример кода о типе возвращаемого значения)
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 Виртуальные функции [class.virtual]
Тип возврата переопределяющей функции должен быть идентичным
тип возврата переопределенной функции или ковариантный с
классы функций.
Виртуальные функции C ++ являются частью интерфейс вашего класса, в то время как не виртуальный может быть частью реализация также. Когда вы расширяете класс, вы сообщаете компилятору, что вы собираетесь соблюдать все его интерфейсы, включая сигнатуры его виртуальных функций.
Все не частные / не финальные методы Java являются виртуальными, поэтому к ним применяется один и тот же аргумент.
Поскольку тип объекта достаточен для определения того, какой метод вызывается, вы не обязательно знаете тип во время компиляции. Компилятор должен определить способ поиска метода по статическому типу, включая виртуальный поиск. Это требует, чтобы компилятор знал сигнатуру и тип возвращаемого значения.
Я понятия не имею, почему вы снова опубликовали этот вопрос, когда я уже дал вам ответ здесь. Но вот правильный ответь снова.
Методы Java и виртуальные функции C ++ Можно быть скрытым, но разные типы возврата являются разрешено при переопределении, пока они совместимый. Это только противоречивый возвращаемые типы, которые запрещены. Например, в C ++:
struct Base {
virtual Base* f() { return nullptr; }
};
struct Derived : Base {
Derived* f() override { return nullptr; }
};
И на Яве:
class Base {
Base f() { return null; }
}
class Derived extends Base {
@Override
Derived f() { return null; }
}