2010-03-02 6 views
1

Я читаю Thinking in C++ от Bruce Eckel. В главе 15 (Том 1) под заголовком «Поведение виртуальных функций внутри конструктора», он идетВызов виртуальных функций внутри функций-членов

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

Здесь пытаюсь объяснить, что при вызове виртуальной функции внутри конструктора объекта, полиморфизм не выставлялся т.е. текущего класса функция Брюса будет называться только и это не будет каким-то другой вариантом производного класса этого функция. Это действительно так, и я могу это понять, поскольку конструктор для класса не будет знать заранее, если он работает для него или для создания какого-то другого дервившегося объекта. Более того, если он это сделает, он будет вызывать функции на частично созданном объекте, что является катастрофическим.

Хотя мое замешательство внезапно возникло из-за первого предложения, где он заявляет об обычной функции-члене, где он говорит, что виртуальный вызов будет разрешен @ время выполнения. Но подождите, внутри любой функции-члена класса, когда вы вызываете другую функцию (будь то виртуальная или не-виртуальная), ее собственная версия класса будет вызвана, не так ли? Например.

class A 
{ 
    virtual void add() { subadd(); } 
    virtual subadd() { std::cout << "A::subadd()\n"; } 
}; 

class B : public A 
{ 
    void add() { subadd(); } 
    void subadd() { std::cout << "B::subadd()\n"; } 
}; 

В приведенном выше коде, в A::add(), когда вызов subadd() сделан, он будет всегда вызов A::subadd() и то же самое справедливо и для B, а, верно? Итак, что он подразумевает под «виртуальным вызовом, разрешен во время выполнения, потому что объект не может знать, принадлежит ли он классу, в котором находится функция-член, или какой-то класс, полученный из него»?

Объясняет ли он это в отношении вызова с помощью указателя базового класса? (Я действительно так подозреваю). В этом случае он не должен писать «Внутри обычной функции-члена»; из моего понимания до сих пор любой вызов функции-члена изнутри другой функции-члена того же класса не является полиморфным, пожалуйста, исправьте меня, если я ошибаюсь.

+3

Теперь это хороший пример для «просто попробуйте». – MSalters

+0

Я пробовал перед публикацией и показывает только соответствующие функции-члены, как я уже писал выше. – legends2k

+0

Если вы хотите, чтобы B был получен из A, вы не сказали компилятору. –

ответ

6

Вы ошибаетесь - другой производный класс может переопределить некоторые из виртуальных функций, что означает, что статический вызов был бы неправильным. Таким образом, чтобы расширить свой пример:

class C : public B 
{ 
public: 
    // Not overriding B::add. 
    void subadd() { std::cout << "C::subadd\n"; } 
}; 

A *a = new C; 
a->add(); 

Это динамически вызывает B::add, который, в свою очередь, динамически вызывает C::subadd. Статический вызов B::subadd был бы неправильным, так как динамический тип C и C переопределяет функцию.

В вашем примере дублирование A::add как B::add не нужно - оба будут вызывать subadd полиморфно независимо от динамического типа объекта.

+0

Спасибо, что показал этот пример, это заставило меня понять ясно. 'A :: add()' называется 'C :: subadd()' когда я его пробовал. Теперь я получаю его, любой вызов виртуальной функции (будь то в любом месте) не может быть статичным, но должен быть динамическим. +1 и спасибо! – legends2k

+0

«Теперь я получаю его, любой вызов виртуальной функции (будь то где-то в любом месте) не может быть статическим» Неправильно. Если компилятор знает точный тип, ему не нужно делать виртуальный вызов. –

+0

Согласен, это сделает статический вызов, если он точно знает, что объект имеет тип A, и полиморфизм не нужен. Например. 'A a; a.subadd(); ' – legends2k

3

, когда вы вызываете другую функцию (будь то виртуальная или не виртуальная), ее собственная версия класса будет вызываться, не так ли?

Извините, неправильно. Исключение составляет конструктор. В противном случае вы имеете дело с полностью построенным объектом, для которого существует полный полиморфизм. В вашем более позднем примере (если B наследуется от A), то, что называется, является B::subadd() (просто возьмите код для тестового диска: это действительно лучший способ узнать, как работает язык программирования).

+0

А: Я не читал код, а то, что вы намеревались. Неужели вы намеревались B наследовать от A? –

+0

Да, да, извините, я не написал его должным образом в сообщении, исправленном сейчас. Спасибо :) – legends2k

1

Звонок subadd(); в ваших примерах действительно является вызовом this->subadd();. В A::add() тип this равен A*, но this может указывать на объект C (при условии, что C также имеет значение A).При компиляции A::add() компилятор не может знать, какие виртуальные функции переопределены в class C. Вызов this->subadd(); в A::add() может фактически вызвать C::subadd(). Таким образом, у компилятора нет выбора, кроме как включить виртуальный вызов, который будет разрешен во время выполнения, когда он знает, на что указывает this->.

+0

Согласен, но когда «A *» указывает на объект C; только если вне класса, выполняется определенное присвоение и вызывается (для достижения полиморфизма). Я прошу о вызове функции-члена, например, 'obj.member();', где member() вызывает 'submember()', который является виртуальным в иерархии. – legends2k

+0

Спасибо за объяснение о 'this' здесь, так как я был немного смущен тем, что' this' всегда является 'Class *', но может быть 'Caller *', как вы сказали, 'this' является' A * 'for объект 'C', если он вызывается из указателя' A'. – legends2k