вопрос о виртуальной функции - PullRequest
0 голосов
/ 15 января 2010
#include "stdafx.h"
#include <iostream>
#include <vector>
#include <string>

class Helper 
{
public:
    Helper() { init(); }
    virtual void print() {
        int nSize = m_vItems.size();
        std::cout << "Size : " << nSize << std::endl;
        std::cout << "Items: " << std::endl;
        for(int i=0; i<nSize; i++) {
            std::cout << m_vItems[i] << std::endl;
        }
    }
protected:
    virtual void init() { m_vItems.push_back("A"); }
    std::vector<std::string> m_vItems;
};

class ItemsHelper : public Helper
{
public:
    ItemsHelper() { }
protected:
    virtual void init() { 
        Helper::init();
        m_vItems.push_back("B");
    }
};

int _tmain(int argc, _TCHAR* argv[]) {
    ItemsHelper h;
    h.print();
}

Это вывод, что размер вектора равен 1. Я ожидал, что размер будет равен 2, потому что в функции ItemsHelper :: init я вызвал функцию базового класса Helper::init(), затем я добавил второй элемент в вектор. Проблема в том, что ItemsHelper :: init не вызывается, вместо этого вызывается функция init базового класса.

Я хочу, чтобы вызывалась функция ItemsHelper :: init, и я могу сделать это, вызывая функцию init в ctor ItemsHelper, а не в базовом классе. НО, вопрос в , есть ли лучший способ добиться этого и при этом сохранить вызов init () в базовом классе? Потому что, если я захочу создать объект Helper вместо ItemsHelper, функция init никогда не будет вызвана.

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

Ответы [ 4 ]

7 голосов
/ 15 января 2010

В конструкторе базового класса производный класс еще не создан, поэтому переопределенная функция в производном классе еще не доступна. Где-то есть запись FAQ ... которую я не могу найти.

Самое простое решение - просто поместить .push_back("A") часть init в конструктор Helper и .push_back("B") в конструктор ItemsHelper. Это похоже на то, что вы пытаетесь сделать, и исключает ненужную init виртуальную функцию.

4 голосов
/ 15 января 2010

Имейте в виду, что виртуальные функции не работают "конструктивно" в конструкторах!

Helper() { init(); }

Здесь init() всегда будет вызывать «init» текущего класса (Helper), даже если он помечен как виртуальный.

РЕДАКТИРОВАТЬ: Есть аналогичный вопрос на SO.

3 голосов
/ 15 января 2010

В общем случае (если вы полностью не понимаете, как конструкторы и виртуальные функции определены для работы), вы не должны вызывать виртуальные функции в конструкторах, поскольку вы, как правило, не получите «самую виртуальную» версию функции.Быстрая версия того, как виртуальные функции работают в конструкторах, заключается в том, что при вызове виртуальной функции вы получите один уровень для «текущего» уровня класса heirarchy, который в настоящее время создается.

См. Следующеестатьи для деталей:

2 голосов
/ 15 января 2010

Проблема в том, что виртуальные функции работают не так, как вы думаете, как в конструкторах. При создании ItemsHelper сначала создается базовый класс Helper. В своем конструкторе тип объекта - Helper, поэтому вызов init вызывает Helper :: init (). Затем вызывается конструктор ItemsHelper. Невозможно вызвать функцию производного класса из конструктора базового класса. Лучшее, что вы можете сделать, это вызвать init () после создания объекта ItemsHelper.

...