2010-01-15 5 views
0
#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 :: инициализации я назвал функцию базового класса Helper::init(), затем добавить второй элемент в вектор. Проблема в том, что ItemHelper :: init не получает вызов, вместо этого вызывается функция init базового класса.функция вопрос виртуального

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

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

ответ

7

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

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

+0

Yeap, я согласен, что это лучшее решение !! Держите это просто, мне это нравится! Спасибо! – cchampion

+0

Я думаю, что запись в C++ FAQ, которую вы искали, это: http://www.parashift.com/c++-faq-lite/strange-inheritance.html # faq-23.5 – jamesdlin

4

Помните, что виртуальные функции не работают как «ожидаемые» в конструкторах!

Helper() { init(); } 

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

EDIT: Существует similar question на SO.

2

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

+0

Эй, я кое-что узнаю! С тех пор, почему он работает так, спасибо! – cchampion

3

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

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

Смежные вопросы