C++ Правильно ли я понимаю полиморфизм?
Bar и Box являются производными классами Foo, а Foo имеет виртуальную функцию F (), а Bar и Box имеют функцию F (). Из того, что я понимаю, полиморфизм правильно позволяет Bar.F () вместо Box.F () или Box.F () вместо Bar.F () переопределять Foo.F (), используя некоторую подпрограмму времени выполнения, не зная, является ли ваш объект Бар или Коробка. Это что-то вроде этого:
Foo * boxxy = new Box;
boxxy->F();
Последняя строка будет вызывать правильное F () (в данном случае Box.F ()) независимо от того, является ли boxxy Box, Bar или Foo (в этом случае вызывается реализация виртуального Foo.F ()).
Я правильно понимаю? А что изменится, если boxxy будет указателем Box? И что произойдет, если производный класс не имеет переопределения для F ()? Наконец, чтобы избежать реализации функции для базового класса, но при этом разрешить полиморфизм, вы просто пишете пустое тело функции и объявляете его виртуальным? Благодарю.
5 ответов
Почти правильно - рассмотрим это дерево наследования:
Foo
/ \
Bar Box
Если вы сейчас сделаете указатель так:
Bar* barry = new Box();
Вы получите хорошую ошибку компилятора, так как Box
не может быть преобразован в Bar
,:)
Так что это только Foo<->Bar
а также Foo<->Box
, никогда Bar<->Box
, Далее, когда boxxy
это Box
указатель, он будет вызывать только Box::F
функция, если это предусмотрено.
И наконец, чтобы заставить подклассы реализовать определенную функцию, вы объявляете ее pure virtual
, как это:
virtual void Func() = 0;
// note this --- ^^^^
Теперь подклассы (в этом случае Bar
а также Box
), должны реализовать Func
иначе они не скомпилируются.
Да, правильная функция F () будет вызываться в зависимости от типа объекта, который вы создали с помощью указателя Foo.
Если boxxy был указателем Box, вы можете вызвать только его F () или один из F () его производного класса, если только вы не выполнили dynamic_cast для его родительского класса, а затем вызвали F().
Чтобы избежать необходимости реализации в базовом классе, вы определяете его как чисто виртуальный, например:
class Foo
{
public:
virtual void F() = 0; //notice the = 0, makes this function pure virtual.
};
Если вы объявите Foo, как это
class Foo
{
private:
Foo() {};
public:
void func() const { std::cout << "Calling Foo::func()" << std::endl; }
};
и бар, как это
class Bar : public Foo
{
private:
Bar() {};
public:
void func() const { std::cout << "Calling Bar::func()" << std::endl; }
};
затем
Foo* bar = new Bar();
bar->func();
вызовет Foo::func().
Если вы объявите Foo, как это
class Foo
{
private:
Foo() {};
public:
virtual void func() const { std::cout << "Calling Foo::func()" << std::endl; } // NOTICE THE virtual KEYWORD
};
затем
Foo* bar = new Bar();
bar->func();
вызовет Bar::func().
А что изменится, если boxxy будет указателем Box?
Это позволит получить доступ к методам Box, не унаследованным от Foo. Указатель Box не может указывать на объекты Bar, поскольку Bar не является производным от Box.
И что произойдет, если производный класс не имеет переопределения для F()?
Он унаследует реализацию F () от базового класса.
Наконец, чтобы избежать реализации функции для базового класса, но при этом разрешить полиморфизм, вы просто пишете пустое тело функции и объявляете его виртуальным?
Это будет работать, но это не правильный способ сделать полиморфизм. Если вы не можете придумать конкретную реализацию для виртуальной функции базового класса, чтобы сделать эту функцию чисто виртуальной, не реализуйте ее как пустую функцию.
- Я правильно понимаю? Да, если функция была объявлена как виртуальная функция.
- А что изменится, если boxxy будет указателем Box? Зависит от того, является ли функция виртуальной или нет. Виртуальная функция всегда будет вызывать правильную производную функцию; не виртуальная функция будет вызывать версию, основанную на типе указателя.
- И что произойдет, если производный класс не имеет переопределения для F()? Он будет использовать определение базового класса.
- Наконец, чтобы избежать реализации функции для базового класса, но при этом разрешить полиморфизм, вы просто пишете пустое тело функции и объявляете его виртуальным? Вы также можете объявить это чисто виртуальным:
virtual void F() = 0
, Любой класс, который намеревается создать экземпляр объекта, значительно переопределяет эту функцию и дает ей правильную реализацию.