Модераторы: Daevaorn

Поиск:

Ответ в темуСоздание новой темы Создание опроса
> Виртуальный и inline метод 
:(
    Опции темы
SABROG
  Дата 24.7.2009, 20:40 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Hacker
****


Профиль
Группа: Завсегдатай
Сообщений: 2481
Регистрация: 18.9.2006

Репутация: 4
Всего: 91



Есть у меня такая бяка.

Код

class Base
{
public:
    Base()
    {
    }
    void parse()
    {
        for (int i=0; i < 2; ++i) {
            if (custom(i))
                break;
        }
    }
    virtual bool custom(int i);
};

class MyClass : public Base
{
public:
    MyClass()
    {
    }
    inline bool custom(int i)
    {
        switch (i)
        {
            case 0:
                qDebug() << i;
                return false;
                break;
            case 1:
                qDebug() << i;
                return true;
                break;
            default:
                break;
        }
        return true;
    }
};


Задача в том, чтобы сделать скажем 10-20 классов наследников от Base. И у всех этих классов будет единый код parse(), а вот метод custom() я хочу переопределить в каждом классе. Чтобы чуток убыстрить код я решил метод сделать inline. Вопрос заключается в том как поведет себя компилятор в этом случае. Ведь изначально код метода parse() - общий для всех экземляров, меняется только объект, но если идет inline подстановка кода внутрь метода каждого экземляра класса на базе Base, не получится ли так, что код блока for(){} будет продублирован 10-20 раз в исполнимом файле для каждого класса? Или компилятор просто проигнорирует ключевое слово inline


--------------------
Национальная группа Russian Federation на QtCentre.
PM MAIL   Вверх
Alexeis
Дата 24.7.2009, 20:47 (ссылка) |    (голосов:3) Загрузка ... Загрузка ... Быстрая цитата Цитата


Амеба
Group Icon


Профиль
Группа: Админ
Сообщений: 11743
Регистрация: 12.10.2005
Где: Зеленоград

Репутация: 12
Всего: 459



  Inline нестрогая директива, а virtual строгая. Поскольку они взаимоисключающие, то сработает тока virtual.


--------------------
Vit вечная память.

Обсуждение действий администрации форума производятся только в этом форуме

гениальность идеи состоит в том, что ее невозможно придумать
PM ICQ Skype   Вверх
SABROG
Дата 24.7.2009, 21:00 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Hacker
****


Профиль
Группа: Завсегдатай
Сообщений: 2481
Регистрация: 18.9.2006

Репутация: 4
Всего: 91



Спасибо. Жалко, что у компилятора (gcc) по этому поводу варнингов не выводится.

Еще один вопрос. Как компилятор разрулит ситуацию, ведь указатель на метод custom() у каждого класса будет разным, не будет ли в этом случае весь код из parse() дублироваться в новые классы?

Код

    void parse()
    {
        for (int i=0; i < 2; ++i) {
            if (custom(i))
                break;
        }
    }


Это сообщение отредактировал(а) SABROG - 24.7.2009, 21:14


--------------------
Национальная группа Russian Federation на QtCentre.
PM MAIL   Вверх
azesmcar
Дата 24.7.2009, 21:22 (ссылка) |    (голосов:2) Загрузка ... Загрузка ... Быстрая цитата Цитата


uploading...
****


Профиль
Группа: Участник Клуба
Сообщений: 6291
Регистрация: 12.11.2004
Где: Армения

Репутация: 81
Всего: 211



Цитата(Alexeis @  24.7.2009,  20:47 Найти цитируемый пост)
  Inline нестрогая директива, а virtual строгая. Поскольку они взаимоисключающие, то сработает тока virtual. 

Они не взаимоисключающие, вызов виртуальной функции может быть встроен в некоторых случаях. Но ключевое слово inline для этого писать совершенно не обязательно, позволь компилятору самому решить. Функция определена в самом классе - этого достаточно.

Цитата(SABROG @  24.7.2009,  21:00 Найти цитируемый пост)
Еще один вопрос. Как компилятор разрулит ситуацию, ведь указатель на метод custom() у каждого класса будет разным, не будет ли в этом случае весь код из parse() дублироваться в новые классы?

Нет, функция parse одна, с чего ей дублироваться?

Это сообщение отредактировал(а) azesmcar - 24.7.2009, 21:28
PM   Вверх
kamre
Дата 24.7.2009, 21:30 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 330
Регистрация: 24.3.2006

Репутация: 1
Всего: 13



Цитата(SABROG @ 24.7.2009,  20:40)
Задача в том, чтобы сделать скажем 10-20 классов наследников от Base. И у всех этих классов будет единый код parse(), а вот метод custom() я хочу переопределить в каждом классе. Чтобы чуток убыстрить код я решил метод сделать inline.

В этом случае может подойти static polymorphism и Curiously recurring template pattern. Компилятор может в принципе и заинлайнить все эти custom() функции.
PM MAIL   Вверх
Alexeis
Дата 24.7.2009, 22:18 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Амеба
Group Icon


Профиль
Группа: Админ
Сообщений: 11743
Регистрация: 12.10.2005
Где: Зеленоград

Репутация: 12
Всего: 459



Цитата(azesmcar @  24.7.2009,  20:22 Найти цитируемый пост)
Они не взаимоисключающие, вызов виртуальной функции может быть встроен в некоторых случаях.

  Каким образом? Ведь суть позднего связывания в том, что во время компиляции неизвестно какой реально метод будет вызван, поэтому и вставляется код который ищет адрес функции в VMT. Внутри метода этого класса 100% будет вызван виртуально. Инлайн возможен только если объект создан статически и тут же вызван его метод "через точку". Я таких вещей не проверял, но из курса по ООП я помню то что виртуальная функция всегда должна вызываться как виртуальная.


--------------------
Vit вечная память.

Обсуждение действий администрации форума производятся только в этом форуме

гениальность идеи состоит в том, что ее невозможно придумать
PM ICQ Skype   Вверх
azesmcar
Дата 24.7.2009, 22:21 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


uploading...
****


Профиль
Группа: Участник Клуба
Сообщений: 6291
Регистрация: 12.11.2004
Где: Армения

Репутация: 81
Всего: 211



Цитата(Alexeis @  24.7.2009,  22:18 Найти цитируемый пост)

  Каким образом? Ведь суть позднего связывания в том, что во время компиляции неизвестно какой реально метод будет вызван, поэтому и вставляется код который ищет адрес функции в VMT. Внутри метода этого класса 100% будет вызван виртуально. Инлайн возможен только если объект создан статически и тут же вызван его метод "через точку". Я таких вещей не проверял, но из курса по ООП я помню то что виртуальная функция всегда должна вызываться как виртуальная. 

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

B b;
b.f();

этот вызов вполне может быть встроен
Код

std::string name;
std::cin >> name;
base* b = factory.create(name);
b->f();

а тут разумеется встроить не сможет даже самый гениальный компилятор
(тут нужен дар предвидения  smile )

Цитата

Инлайн возможен только если объект создан статически и тут же вызван его метод "через точку". 

ну да, я как раз об этом.

Это сообщение отредактировал(а) azesmcar - 24.7.2009, 22:31
PM   Вверх
mes
Дата 24.7.2009, 22:27 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


любитель
****


Профиль
Группа: Участник Клуба
Сообщений: 7954
Регистрация: 14.1.2006

Репутация: 144
Всего: 250



Цитата(azesmcar @  24.7.2009,  20:22 Найти цитируемый пост)
Они не взаимоисключающие, вызов виртуальной функции может быть встроен в некоторых случаях. 

 smile, в тех случаях, когда компилятор может определить отсутствие полиморфного вызова.

Цитата(kamre @  24.7.2009,  20:30 Найти цитируемый пост)
В этом случае может подойти static polymorphism и Curiously recurring template pattern. 

 smile, но только с потерей преимуществ динамического полиморфизма, т.е  нельзя будет в рантайме пользовать объект через интерфейс базового класса.

Добавлено через 1 минуту и 15 секунд
Цитата(Alexeis @  24.7.2009,  21:18 Найти цитируемый пост)
Инлайн возможен только если объект создан статически и тут же вызван его метод "через точку". 

 smile 


--------------------
PM MAIL WWW   Вверх
SABROG
Дата 25.7.2009, 18:03 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Hacker
****


Профиль
Группа: Завсегдатай
Сообщений: 2481
Регистрация: 18.9.2006

Репутация: 4
Всего: 91



А вообще возможно завести контейнер, который содержал бы указатели на базовый класс и получая указатель через этот контейнер использовать методы производного класса (upcast?), без RTTI?

Просто я не совсем понимаю как это работает на Java:

Базовый класс
Код

public interface ComponentParser {
  
  /**
   * Parse some XML data using the supplied stax reader.
   * @param staxXmlReader STAX reader.
   * @throws XMLStreamException
   */
  public void parse(XMLStreamReader staxXmlReader) throws XMLStreamException;
 
}


Производный класс:

Код

public class AuthorParser implements ComponentParser{

  
  public void parse(XMLStreamReader staxXmlReader) throws XMLStreamException{
    
      // read name
      StaxUtil.moveReaderToElement("name",staxXmlReader);
      String name = staxXmlReader.getElementText();
      
      // read email
      StaxUtil.moveReaderToElement("email",staxXmlReader);
      String email = staxXmlReader.getElementText();
      
      // Do something with author data...
  }
  
 

}



Вызов переопределенного метода parse() через базовый класс, как такое возможно?
Код

          if (delegates.containsKey(element)) {
            ComponentParser parser = (ComponentParser) delegates.get(element);
            parser.parse(staxXmlReader);
          } 


delegates - контейнер map.

Это сообщение отредактировал(а) SABROG - 25.7.2009, 18:11


--------------------
Национальная группа Russian Federation на QtCentre.
PM MAIL   Вверх
mes
Дата 25.7.2009, 18:12 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


любитель
****


Профиль
Группа: Участник Клуба
Сообщений: 7954
Регистрация: 14.1.2006

Репутация: 144
Всего: 250



Цитата(SABROG @  25.7.2009,  17:03 Найти цитируемый пост)
А вообще возможно завести контейнер, который содержал бы указатели на базовый класс и получая указатель через этот контейнер использовать методы производного класса (upcast?), без RTTI?

нет.. но можно постораться сократить кол-во виртуальных вызовов.

Добавлено через 2 минуты и 20 секунд
Цитата(SABROG @  25.7.2009,  17:03 Найти цитируемый пост)
Просто я не совсем понимаю как это работает на Java:

так на Яве по умолчанию вроде ж все методы виртуальные smile


Это сообщение отредактировал(а) mes - 25.7.2009, 18:12


--------------------
PM MAIL WWW   Вверх
SABROG
Дата 25.7.2009, 18:35 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Hacker
****


Профиль
Группа: Завсегдатай
Сообщений: 2481
Регистрация: 18.9.2006

Репутация: 4
Всего: 91



Цитата(mes @  25.7.2009,  18:12 Найти цитируемый пост)
так на Яве по умолчанию вроде ж все методы виртуальные


Если я сделаю базовый класс полностью виртуальным мне это поможет? smile

Цитата(mes @  25.7.2009,  18:12 Найти цитируемый пост)
нет.. но можно постораться сократить кол-во виртуальных вызовов.


Тут я не уловил мысли. В принципе у меня один метод виртуальный, который переопределен в производном классе. Если я вызову не виртуальный метод parse() базового класса, в котором используется переопределенный виртуальный метод производного класса это будет работать?


--------------------
Национальная группа Russian Federation на QtCentre.
PM MAIL   Вверх
mes
Дата 25.7.2009, 18:40 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


любитель
****


Профиль
Группа: Участник Клуба
Сообщений: 7954
Регистрация: 14.1.2006

Репутация: 144
Всего: 250



Цитата(SABROG @  25.7.2009,  17:35 Найти цитируемый пост)

Тут я не уловил мысли.


Цитата(SABROG @  24.7.2009,  19:40 Найти цитируемый пост)
. И у всех этих классов будет единый код parse(), а вот метод custom() я хочу переопределить в каждом классе


Цитата(SABROG @  24.7.2009,  19:40 Найти цитируемый пост)
    void parse()
    {
        for (int i=0; i < 2; ++i) {
            if (custom(i))
                break;
        }
    }
  virtual bool custom(int i);


вот тут многоразовый вызов виртуального метода custom. Хотя достаточно сделать виртуальным parse (), а чтоб не повторять код в теле  функции применить шаблоны, возможно CRT Pattern.
Вообщем мысль в том, чтоб правильно организовать архитектуру, избежав лишних виртуальных вызовов.






--------------------
PM MAIL WWW   Вверх
SABROG
Дата 25.7.2009, 18:55 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Hacker
****


Профиль
Группа: Завсегдатай
Сообщений: 2481
Регистрация: 18.9.2006

Репутация: 4
Всего: 91



Цитата(mes @  25.7.2009,  18:40 Найти цитируемый пост)
вот тут многоразовый вызов виртуального метода custom. Хотя достаточно сделать виртуальным parse (),


Я на это пойтить не могу, смысл всего в том, чтобы избежать дублирования кода. Если я сделаю parse() виртуальным, то мне придется в 20 классах дублировать цикл for().

Ладно, может быть тогда посоветуете какой-нибудь другой способ, чтобы избежать такого:

Код

void Tag1()
{
    while(!atEnd) {
        ...
    }
}

void Tag2()
{
    while(!atEnd) {
        ...
    }
}

void Tag3()
{
    while(!atEnd) {
        ...
    }
}

void Tag4()
{
    while(!atEnd) {
        ...
    }
}


Пытаюсь написать класс, который было бы удобно использовать с StAX xml парсером, но если брать во внимание саму технологию, то отпарсить сложный xml файл с большой глубиной уровней задача не из легких.

Ссылка для ознакомления: 
http://www.devx.com/Java/Article/30298/0/page/2 - статья про StAX в Java

А это то, как выглядит парсинг xml'я простой структуры, всего 3 вложенных цикла while. У меня же этих циклов намного больше.

Код

        QXmlStreamReader xml(data);
        while (!xml.atEnd()) {
            xml.readNext();
            if (xml.tokenType() == QXmlStreamReader::StartElement) {
                if (xml.name() == "city") {
                    city = GET_DATA_ATTR;
                    setWindowTitle(city);
                }
                if (xml.name() == "unit_system")
                    unitSystem = xml.attributes().value("data").toString();
                // Parse current weather conditions
                if (xml.name() == "current_conditions") {
                    while (!xml.atEnd()) {
                        xml.readNext();
                        if (xml.name() == "current_conditions")
                            break;
                        if (xml.tokenType() == QXmlStreamReader::StartElement) {
                            if (xml.name() == "condition") {
                                m_conditionItem->setPlainText(GET_DATA_ATTR);
                            }
                            if (xml.name() == "icon") {
                                QString name = extractIcon(GET_DATA_ATTR);
                                if (!name.isEmpty()) {
                                    delete m_iconItem;
                                    m_iconItem = new QGraphicsSvgItem(name);
                                    m_scene.addItem(m_iconItem);
                                    m_iconItem->setParentItem(m_statusItem);
                                }
                            }
                            if (xml.name() == "temp_c") {
                                QString s = GET_DATA_ATTR + QChar(176);
                                m_temperatureItem->setPlainText(s);
                            }
                        }
                    }
                }
                // Parse and collect the forecast conditions
                if (xml.name() == "forecast_conditions") {
                    QGraphicsTextItem *dayItem  = 0;
                    QGraphicsSvgItem *statusItem = 0;
                    QString lowT, highT;
                    while (!xml.atEnd()) {
                        xml.readNext();
                        if (xml.name() == "forecast_conditions") {
                            if (dayItem && statusItem &&
                                !lowT.isEmpty() && !highT.isEmpty()) {
                                m_dayItems << dayItem;
                                m_conditionItems << statusItem;
                                QString txt = highT + '/' + lowT;
                                QGraphicsTextItem* rangeItem;
                                rangeItem = m_scene.addText(txt);
                                rangeItem->setDefaultTextColor(textColor);
                                m_rangeItems << rangeItem;
                                QGraphicsRectItem *box;
                                box = m_scene.addRect(0, 0, 10, 10);
                                box->setPen(Qt::NoPen);
                                box->setBrush(Qt::NoBrush);
                                m_forecastItems << box;
                                dayItem->setParentItem(box);
                                statusItem->setParentItem(box);
                                rangeItem->setParentItem(box);
                            } else {
                                delete dayItem;
                                delete statusItem;
                            }
                            break;
                        }
                        if (xml.tokenType() == QXmlStreamReader::StartElement) {
                            if (xml.name() == "day_of_week") {
                                QString s = GET_DATA_ATTR;
                                dayItem = m_scene.addText(s.left(3));
                                dayItem->setDefaultTextColor(textColor);
                            }
                            if (xml.name() == "icon") {
                                QString name = extractIcon(GET_DATA_ATTR);
                                if (!name.isEmpty()) {
                                    statusItem = new QGraphicsSvgItem(name);
                                    m_scene.addItem(statusItem);
                                }
                            }
                            if (xml.name() == "low")
                                lowT = toCelcius(GET_DATA_ATTR, unitSystem);
                            if (xml.name() == "high")
                                highT = toCelcius(GET_DATA_ATTR, unitSystem);
                        }
                    }
                }

            }
        }



Это сообщение отредактировал(а) SABROG - 25.7.2009, 19:03


--------------------
Национальная группа Russian Federation на QtCentre.
PM MAIL   Вверх
mes
Дата 25.7.2009, 19:19 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


любитель
****


Профиль
Группа: Участник Клуба
Сообщений: 7954
Регистрация: 14.1.2006

Репутация: 144
Всего: 250



Цитата(SABROG @  25.7.2009,  17:55 Найти цитируемый пост)
Если я сделаю parse() виртуальным, то мне придется в 20 классах дублировать цикл for().


А если сделать две Parse  ? одна для вызова виртуальная, другая шаблонная для имплементации ?

Вот в упрощенном виде :

Код

struct IBase
{
   virtual void Parse ()=0;
};

template <class T> void doParse (T& obj)
{
           for (int i=0; i < 2; ++i) 
             if (obj.custom(i))  break;
        
}

struct Der : IBase
{
    virtual void Parse ()     { doParse(*this); }
            bool Custom (int) { ... }
};





Это сообщение отредактировал(а) mes - 25.7.2009, 20:21


--------------------
PM MAIL WWW   Вверх
Любитель
Дата 28.7.2009, 14:36 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Программист-романтик
****


Профиль
Группа: Комодератор
Сообщений: 3645
Регистрация: 21.5.2005
Где: Воронеж

Репутация: 24
Всего: 92



Цитата(mes @  25.7.2009,  19:19 Найти цитируемый пост)
А если сделать две Parse  ? одна для вызова виртуальная, другая шаблонная для имплементации ?

Именно так обычно и делается. У Саттера вроде была фраза о том, чтобы делать виртуальными только прайват-методы. А паблик/проектед обёртки проверяют аргументы и выполняют некий общий, ненастраиваемый код (это правда специфика С++ - не во всех ОО-моделях прайват-методы вообще могут работать как виртуальные).


--------------------
PM MAIL ICQ Skype   Вверх
Ответ в темуСоздание новой темы Создание опроса
Правила форума "С++:Общие вопросы"
Earnest Daevaorn

Добро пожаловать!

  • Черновик стандарта C++ (за октябрь 2005) можно скачать с этого сайта. Прямая ссылка на файл черновика(4.4мб).
  • Черновик стандарта C (за сентябрь 2005) можно скачать с этого сайта. Прямая ссылка на файл черновика (3.4мб).
  • Прежде чем задать вопрос, прочтите это и/или это!
  • Здесь хранится весь мировой запас ссылок на документы, связанные с C++ :)
  • Не брезгуйте пользоваться тегами [code=cpp][/code].
  • Пожалуйста, не просите написать за вас программы в этом разделе - для этого существует "Центр Помощи".
  • C++ FAQ

Если Вам понравилась атмосфера форума, заходите к нам чаще! С уважением, Earnest Daevaorn

 
0 Пользователей читают эту тему (0 Гостей и 0 Скрытых Пользователей)
0 Пользователей:
« Предыдущая тема | C/C++: Общие вопросы | Следующая тема »


 




[ Время генерации скрипта: 0.1221 ]   [ Использовано запросов: 22 ]   [ GZIP включён ]


Реклама на сайте     Информационное спонсорство

 
По вопросам размещения рекламы пишите на vladimir(sobaka)vingrad.ru
Отказ от ответственности     Powered by Invision Power Board(R) 1.3 © 2003  IPS, Inc.