2015-09-15 4 views
4
#include <iostream> 

struct A { 
    void init() 
    { 
     internal_init(); 
    } 
    virtual void internal_init() 
    { 
     std::cout << "internal of A" << std::endl; 
    } 
}; 

struct B: public A { 
    void internal_init() 
    { 
     init(); 
     std::cout << "internal of B" << std::endl; 
    } 
}; 

int main(){ 
    B instance; 
    std::cout << "internal of A" << std::endl; 
    instance.internal_init(); 
    return 0; 
} 

Сначала программа отправляется B::internal_init(), как и ожидалось. Затем, до A::init() (думаю, так как B происходит от A, а B не имеет init()). Теперь что?вызов виртуального метода без указания на объект?

какой internal_init() он выберет? поскольку он идет до B::internal_init(), программа войдет в бесконечный цикл, и я не понимаю, почему.

  • Что происходит, когда я звоню internal_init()?
  • Почему он вызывает internal_init() пользователя "B part" экземпляра? Это о «виртуальном»? Если да, то как получилось? Виртуальные функции имеют место, когда мы используем полиморфизм (который, как и начинающий, как и я, понимает, работает с указателями базового класса, которые указывают на объекты производного класса).
+1

Обратите внимание, что ** ** первый звоните в 'internal_init' является не полиморфный. Но внутри 'init()' вызов * является * полиморфным, потому что (невидимый) указатель 'this' всегда является указателем ... Если вы хотите этого избежать, вам нужно написать' A :: internal_init() '(тем самым вызывая неполиморфный вызов) в 'init'. – leemes

+2

Это действительно хороший пример кода, который вы получили здесь. Он демонстрирует, что ** полиморфные вызовы могут быть скрыты за неполиморфными вызовами **. Это необходимо для написания не виртуальных «шаблонных методов» (в смысле [шаблон шаблона «метод шаблона»] (https: //en.wikipedia.org/wiki/Template_method_pattern), чтобы не путать с шаблонами C++). Там не виртуальная функция вызывает виртуальные подфункции, которые реализуют детали более масштабного алгоритма. Эти детали могут быть реализованы в подклассах, тогда как алгоритм большего масштаба фиксируется базовым классом. – leemes

ответ

7

С instance является B

instance.internal_init(); 

Позвоню B s internal_init(). Затем в internal_init() вы вызываете init();. Теперь функции-члены имеют неявный параметр, который является этим указателем.

Поэтому, когда мы вызываем Ainit(), этот указатель фактически является B. В init() мы вызываем internal_init(); с помощью этого указателя на B. Поскольку internal_init() является виртуальным и у нас есть указатель на B, механизм виртуального поиска вызовет Binternal_init().

Это снова зацикливается и в конечном итоге приведет к переполнению segfault или stack.

+0

Не могли бы вы объяснить, почему: «В init() мы вызываем inner_init(), используя этот указатель на B так **, он будет вызывать B inner_init(). **"? –

+3

Потому что это полиморфный вызов. Прочитайте строку 'internal_init()' as 'this-> internal_init()' (это просто синтаксическая стенография) с 'this' типа' A * ', но фактически указывая на экземпляр' B'. Здесь виртуальный вызов разрешен для 'B :: internal_init()' во время выполнения. – leemes

+1

@AlexGoft Я обновил свой ответ. Дайте мне знать, если это очистит вас. – NathanOliver

2

Во-первых, struct B наследует все функции struct A из-за struct B: public A. Функция internal_initA переопределяется в B, потому что вы используете одну и ту же сигнатуру функции и ключевое слово virtual в A.

Так что теперь звонки: instance.internal_init(); который называет internal_init() из B, который вызывает A::init, который вызывает B::internal_init(), и т.д., пока ошибка сегментации не дается. Чтобы предотвратить это (и я думаю, что это то, что вы хотите), вы можете явно вызвать internal_init() из A в B вместо вызова init():

struct B: public A { 
    virtual void internal_init() 
    { 
     A::internal_init(); 
     std::cout << "internal of B" << std::endl; 
    } 
}; 
Смежные вопросы