Форум программистов «Весельчак У»
  *
Добро пожаловать, Гость. Пожалуйста, войдите или зарегистрируйтесь.
Вам не пришло письмо с кодом активации?

  • Рекомендуем проверить настройки временной зоны в вашем профиле (страница "Внешний вид форума", пункт "Часовой пояс:").
  • У нас больше нет рассылок. Если вам приходят письма от наших бывших рассылок mail.ru и subscribe.ru, то знайте, что это не мы рассылаем.
   Начало  
Наши сайты
Помощь Поиск Календарь Почта Войти Регистрация  
 
Страниц: [1]   Вниз
  Печать  
Автор Тема: Виртуальные функции. Что это такое? Часть 1  (Прочитано 41027 раз)
0 Пользователей и 1 Гость смотрят эту тему.
Alf
Гость
« : 10-03-2005 09:17 » 

При чтении статьи возникла пара вопросов.

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

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

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

Если мы перегрузили функцию в производном классе, в базовом такой сигнатуры нет. Значит, вызвать функцию с другим набором параметров через указатель на базовый класс не удастся без приведения типов, получим ошибку еще на этапе компиляции. То, что тип возвращаемого значения остался тем же, дела не меняет (возвращаемое значение не входит в сигнатуру).
Записан
Гром
Птычк. Тьфу, птычник... Вот!
Готовлюсь к пенсии

il
Offline Offline
Пол: Мужской
Бодрый птах


« Ответ #1 : 10-03-2005 09:21 » 

Хм... Надо автору переслать...
Записан

А птичку нашу прошу не обижать!!!
Alf
Гость
« Ответ #2 : 10-03-2005 09:25 » 

А это не из наших автор?
Записан
Гром
Птычк. Тьфу, птычник... Вот!
Готовлюсь к пенсии

il
Offline Offline
Пол: Мужской
Бодрый птах


« Ответ #3 : 10-03-2005 09:26 » 

Это новый - недавно прислал набор статей - первые две части вышли...
В воскресенье третью часть выдам.
Записан

А птичку нашу прошу не обижать!!!
valker
Гость
« Ответ #4 : 10-03-2005 14:23 » 

Цитата
Чистая – в данном случае означает буквально пустая функция.
Цитата
Чистая виртуальная функция абсолютно ничего не делает и недоступна для вызовов.

Автору статьи предлагаю откомпилировать и запустить программу:

Код:
#include <iostream>

using namespace std;

class A
{
public:
    virtual void f(void) const = 0 {
        cout << "A::f()\n";
    }
    virtual ~A(){}
};

class B : public A {
    void f(void) const {
        cout << "Call A function from B - ";
        A::f();
    }
};

int main(int argc, _TCHAR* argv[])
{
    A * p = new B;
    p->f();
    delete p;
return 0;
}

А для остальных просто скажу, что наличие хотя бы одной чисто-виртуальной функции просто заставляет компилятор запретить непосредственное инстанциирование объектов данного класса.
Следовательно не имеет смысла делать в классе более одной чисто-виртуальной функции.
А иметь тело чисто-виртуальной функции можно, что и продемонстрировано в моём примере.

Спасибо.
Записан
Гром
Птычк. Тьфу, птычник... Вот!
Готовлюсь к пенсии

il
Offline Offline
Пол: Мужской
Бодрый птах


« Ответ #5 : 10-03-2005 14:41 » 

А в чем противоречие???
Если автор утверждает что функция пустая чисто виртуальная ничего не делает, то так оно и есть вызов чисто виртуальной ф-ии без тела невозможен...

Не вижу у автора чего бы то ни было, что заставляет подумать об обратном.

Фраза
Цитата
Следовательно не имеет смысла делать в классе более одной чисто-виртуальной функции.
навевает мысль что критикующий делает виртуальные функции только с целью запретить "инстанциирование". Предлагаю критику подумать, для чего вообще то можено использовать виртуальные функции, кроме этой цели.

У меня на производстве была оригинальная задача - иметь стандартный набор тестов, который можно было бы пересобирать - добавляя свои, используя стандартный API. Т.е. любой мог написать свой класс и перегрузив мои функции  получить в результате ту же библиотеку с тем же названием но с своими тестами.

В том классе практически все функции, кроме инита и служебных были виртуальными, и я вижу таки смысл иметь в классе более одной виртуальной функции...

П.С. Кстати совершенно непонятно причем тут деструктор, который автор приводит в примере.
За исключением него чисто виртуальных функций в примере не вижу...
Записан

А птичку нашу прошу не обижать!!!
Михалыч
Команда клуба

ru
Offline Offline
Пол: Мужской

« Ответ #6 : 10-03-2005 17:49 » 

Однако, спасибо всем заметившим, прочитавшим и покритиковавшим! Отлично
Добрая, конструктивная критика, как говорится даже кошке приятна Улыбаюсь
Не пинайте ногами в живот, я не крутой программист и не матерый писатель.
Как говорится - не стреляйте в пианиста, он играет как может Улыбаюсь
Теперь по существу...

to Alf
Цитата
Насколько помнится из Страуструпа, тип возвращаемого значения не входит в сигнатуру любой функции (не только виртуальной). Поэтому не вижу смысла оговаривать это особо. Компилятор выругается независимо от виртуальности функции.

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


Цитата
Если мы перегрузили функцию в производном классе, в базовом такой сигнатуры нет. Значит, вызвать функцию с другим набором параметров через указатель на базовый класс не удастся без приведения типов, получим ошибку еще на этапе компиляции. То, что тип возвращаемого значения остался тем же, дела не меняет (возвращаемое значение не входит в сигнатуру).

Не согласен. В смысле не согласен в данном конкретном контексте Улыбаюсь Речь идет не о перегрузке функции, а о "якобы перегрузке", которая таковой в результате не является. Речь идет о переопределении или скрытии (не знаю как правльнее, или понятнее выразится - терминов очень много) другой функцией. Хотя, может быть я просто не очень удачно выразил мысль. А Вы смотрели вторую часть статьи, где именно эта ситуация проиллюстрирована на живом примере?
Это тоже пример того как можно ошибиться при написании виртуальных функций.
Это тоже весьма распространенная "по началу" ошибка. Только вот понять ее уже не так просто. Именно поэтому она и проиллюстрирована в примере.

to Гром и valker
Цитата
Следовательно не имеет смысла делать в классе более одной чисто-виртуальной функции.

Тут Гром абсолютно прав. Не только имеет смысл, но иногда просто необходимо сделать несколько чистых виртуальных функций, задавая заранее "пустой" набор интерфейсов к классу. Действительно чистые виртуальные функции можно конечно рассматривать как средство запрета инстанцирования, но ведь этим все не ограничивается?

Цитата
П.С. Кстати совершенно непонятно причем тут деструктор, который автор приводит в примере.
За исключением него чисто виртуальных функций в примере не вижу...

Так это попытка проиллюстрировать все уже сказанное в статье, о применении виртуальных функций вообще и ошибках в частности. В том числе и чистую виртуальную функцию, пусть и в виде деструктора, об этом кстати речь еще будет впереди... Части сами по себе, без начала и продолжения бесполезны, ну, может вся статья не очень удачно разбита на части...
« Последнее редактирование: 10-03-2005 17:59 от Михалыч » Записан

Поживем - увидим... Доживем - узнаем... Выживу - учту  Улыбаюсь
Гром
Птычк. Тьфу, птычник... Вот!
Готовлюсь к пенсии

il
Offline Offline
Пол: Мужской
Бодрый птах


« Ответ #7 : 10-03-2005 17:54 » 

Есть подозрение, что нашему полку прибыло.
Я конечно не знаю, будет ли Михалыч Улыбаюсь к нам захаживать постоянно и есть ли у него время на то, что бы стать постоянным автором и пишущим на форумах, но то, что мы получили приятного собеседника - факт.

valker - насчет тебя надеюсь сказать то же после того, как увижу тебя тут еще раз.
Мы не только рады поспорить конструктивно, но и рады новым знающим личностям, не боящимся с первого же захода начать спор на таком уровне.

Записан

А птичку нашу прошу не обижать!!!
Михалыч
Команда клуба

ru
Offline Offline
Пол: Мужской

« Ответ #8 : 10-03-2005 18:05 » 

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

Поживем - увидим... Доживем - узнаем... Выживу - учту  Улыбаюсь
valker
Гость
« Ответ #9 : 11-03-2005 10:38 » 


to Гром и valker
Цитата
Следовательно не имеет смысла делать в классе более одной чисто-виртуальной функции.

Тут Гром абсолютно прав. Не только имеет смысл, но иногда просто необходимо сделать несколько чистых виртуальных функций, задавая заранее "пустой" набор интерфейсов к классу. Действительно чистые виртуальные функции можно конечно рассматривать как средство запрета инстанцирования, но ведь этим все не ограничивается?

Цитата
П.С. Кстати совершенно непонятно причем тут деструктор, который автор приводит в примере.
За исключением него чисто виртуальных функций в примере не вижу...


to Михалыч:
Какие ещё цели у чисто-виртуальных функций (ЧВФ)?
Учтите, что мы уже выяснили, что ЧВФ может иметь тело.

По-моему, ЧВФ используется для:
1. Задания интерфейса (тем самым запрещая инстанциирование данного класса, он становится интерфейсом)
2. Для предоставления "реализации по-умолчанию".
Пример:
class A {
public:
 void f(void) const = 0 {
   cout << "default implementation\n";
 }
 ~A(){}
};
class B : public A {
public:
 void f(void) const {
  // решили использовать реализацию по-умолчанию.
   A::f();
 }
};
class C : public A {
public:
 void f(void) const {
  // пишем свою реализацию.
 }
};

Вопрос. В чём различие, между "Абстрактным классом" и "Интерфейсом"?

to Гром:
1. в абстрактном классе ЧВФ используется для запрета инстанциирования.
2. в интерфейсе ЧВФ используется для формирования собственно интерфейса
В своём первом посте я указал на ошибку в статье, что по словам автора ЧВФ не может иметь тела - а она может, и не может быть вызвана т.е. - может.

В первой реакции на моё сообщение вы пишете про виртуальные функции (ВФ), я же писал про чисто-виртуальные функции (ЧВФ). Единственное различие между ними, что наличие в классе хотя бы одной ЧВФ запрещает инстанциирование данного класса, а если нет ни одной ЧВФ, то инстанциирование возможно.
Вы, видимо, применяете ЧВФ исключительно для формирование интерфейсов, что вполне допустимо, однако С++ разрешает также указывать реализации у ЧВФ. Класс, у которой ЧВФ имеет реализацию перестаёт быть интерфейсом.
Записан
Гром
Птычк. Тьфу, птычник... Вот!
Готовлюсь к пенсии

il
Offline Offline
Пол: Мужской
Бодрый птах


« Ответ #10 : 11-03-2005 10:47 » 

Итак.
Цитата
Какие ещё цели у чисто-виртуальных функций (ЧВФ)?
Учтите, что мы уже выяснили, что ЧВФ может иметь тело.

Цитата
Цитата Ян Джойнер.
Чистые виртуальные функции (pure virtual functions) предоставляют механизм для сохранения функции в виде неопределенной и абстрактной. Класс, содержащий такую абстрактную функцию, не может быть порожден напрямую, а неабстрактный класс-потомок должен определить ее. Синтаксис чистой виртуальной функции в Си++ выглядит так: virtual void fn() = 0.

Итак мы выяснили, что чисто виртуальной функцией называется функция не имеющяя тело. Только такие функции называются чисто виртуальными.

Цитата
Бьерн Страуструп дает такое объяснение странному виду записи =0: «Курьезный синтаксис =0 был выбран как очевидная альтернатива введению ключевых слов pure или abstract, поскольку в то время я не видел возможности вводить еще одно ключевое слово. Я предполагал, что в Release 2.0 абстрактные классы не войдут. Чтобы не рисковать с задержкой в принятии решения и не вступать в длительные дискуссии, я использовал традиционное соглашение Си и Си++ о том, что 0 носит значение «отсутствует»».

Думаю, что дальнейший спор мы будем проводить после того, как valker сможет доказать свою компетентность в теме виртуальных функций выше авторов приведенных выше цитат.
Извиняюсь за столь резкое, возможно, выражение, но все же я люблю говорить о предметах обсуждения только после принятия определеных положений и исходных точек.
Для меня Джойнер и Страуструп - есть исходные точки.
Записан

А птичку нашу прошу не обижать!!!
valker
Гость
« Ответ #11 : 11-03-2005 11:16 » 

Цитата
Какие ещё цели у чисто-виртуальных функций (ЧВФ)?
Учтите, что мы уже выяснили, что ЧВФ может иметь тело.

Цитата
Цитата Ян Джойнер.
Чистые виртуальные функции (pure virtual functions) предоставляют механизм для сохранения функции в виде неопределенной и абстрактной. Класс, содержащий такую абстрактную функцию, не может быть порожден напрямую, а неабстрактный класс-потомок должен определить ее. Синтаксис чистой виртуальной функции в Си++ выглядит так: virtual void fn() = 0.


Итак мы выяснили, что чисто виртуальной функцией называется функция не имеющяя тело. Только такие функции называются чисто виртуальными.

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


Мда, резковато. Ну ладно, попробуем объяснить ещё раз.
Про Страуструпа вырезал, потому что там объясняется выбор синтаксиса, а не предмет обсуждения. То, что традиционно 0 носит значение "отсутствует" не означает автоматически, отсутствует и тело функции, просто использовалась некоторая параллель. Сам Страуструп пишет, что мог бы ввести ключевое слово "pure", которое ни на какие "отсутствия" не посягалось бы.
Со Страуструпом, т.о. я не спорю. А вот с Яном Джойнером да. Кстати, хотелось бы более точную ссылку на источник цитаты.

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

Раз уж началось "шапкозакидательство" цитатами, позволю себе процитировать Герба Саттера (Herb Sutter), который в книге "Решение сложных задач на С++" (ISBN 5-8459-0352-1 рус. ISBN 0-201-77581-6 англ.) (использую русскую редакцию) пишет на стр. 213:
Цитата
Если производный класс не замещает обычную виртуальную функцию, то он просто наследует её базовую версию. Если вы хотите обеспечить поведение по умолчанию, но при этом не дать производному классу возможности просто "молча" унаследовать виртуальную функцию, её можно сделать чисто виртуальной, но с телом, позволив тем самым автору производного класса при необходимости вызвать её.
Записан
Alf
Гость
« Ответ #12 : 11-03-2005 11:42 » 

В целом склоняюсь к точке зрения valker'а. Все, что допускается формальным синтаксисом языка и не вызывает ошибок при компиляции, является корректным с точки зрения языка. Семантика и устоявшиеся идиомы вроде наследования интерфейса - предмет другого разговора. Посему чисто виртуальная функция с телом формально имеет право на жизнь для явного запрещения инстанцирования объекта, имеющего методы по умолчанию. Хотя лично я бы в данной ситуации предпочел использовать конструктор protected, данная идиома делает то же самое выразительнее (IMHO, разумеется). Оставим это на совести Страуструпа, который сэкономил одно ключевое слово в ущерб выразительности конструкции.

Не согласен лишь со следующим:
...
По-моему, ЧВФ используется для:
1. Задания интерфейса (тем самым запрещая инстанциирование данного класса, он становится интерфейсом)
...

IMHO здесь подмена понятия интерфейса на абстрактный класс. Интерфейс - более узкое понятие, и не любой класс, инстанцирование которого ограничено, автоматически становится интерфейсом.

Интерфейс традиционно реализуется в виде класса, все члены которого - чисто абстрактные функции, без реализации по умолчанию. Фактически он сводится к VTBL, ничего более. Абстрактный класс вполне может иметь члены-переменные.
« Последнее редактирование: 20-12-2007 19:20 от Алексей1153++ » Записан
valker
Гость
« Ответ #13 : 11-03-2005 12:14 » 


Не согласен лишь со следующим:
...
По-моему, ЧВФ используется для:
1. Задания интерфейса (тем самым запрещая инстанциирование данного класса, он становится интерфейсом)
...

IMHO здесь подмена понятия интерфейса на абстрактный класс. Интерфейс - более узкое понятие, и не любой класс, инстанцирование которого ограничено, автоматически становится интерфейсом.

Интерфейс традиционно реализуется в виде класса, все члены которого - чисто абстрактные функции, без реализации по умолчанию. Фактически он сводится к VTBL, ничего более. Абстрактный класс вполне может иметь члены-переменные.

Абсолютно согласен. Признаю, был неправ в этом. Добавлю, что можно если наследник интерфейса определяет не все функции, то он является абстрактным классом.
И ещё одно дополнение, стандарт языка требует, чтобы тело чисто виртуальной функции абстрактного класса было определено вне класса. Т.о. мой пример не вполне соответствует стандарту.
Записан
Михалыч
Команда клуба

ru
Offline Offline
Пол: Мужской

« Ответ #14 : 11-03-2005 13:13 » 

Да-с, господа! А чудненькая у нас получилась дискуссия Улыбаюсь
Нет, правда, я серьезно.
Жаль я опоздал к "раздаче" Улыбаюсь
Читаю новые посты и жутко хочется ответить, читаю ниже - блин, все аргументы, что собрались в голове уже выложены. Добавить то и нечего Улыбаюсь
А на это ответить успею Улыбаюсь
to valker
Цитата
И ещё одно дополнение, стандарт языка требует, чтобы тело чисто виртуальной функции абстрактного класса было определено вне класса. Т.о. мой пример не вполне соответствует стандарту.
Точно! В такой реализации (НЕ вне класса, а ВНУТРИ) его не всякий компилятор пропустит (а может и вообще - никакой, не по стандарту, однако...). Не поленился - попробовано на 5 борланде и gcc под QNX...

Каюсь - упустил я такую возможность использования ЧВФ. Ну, тут уж - увы. Честно говоря в моем личном опыте подобного варианта я не использовал никогда. Более того - в подавляющем большинстве книг по С++ (я имею ввиду наиболее распространенную литературу, кроме м.б. "очень умных книг", коих мало к сожалению) рассматривается именно "пустотелые" ЧВФ Улыбаюсь
А вообще (тут следует жалкая попытка самооправдания Отлично) материал рассчитан не на "профи" им этого не надо, они и так знают (что мы и видим в дискуссии) а на начинающих, а по сему и не претендует на абсолютную полноту изложения. Кому будет интересно, тот несомненно сам накопает еще кучу полезного о тех же виртуальных функциях.
Я уверен на 300%, что читая остальные статьи, которые будут выкладываться постепенно, вы найдете еще море подобных "плюх", а может и покруче.
У нас еще будет о чем поспорить Отлично
Записан

Поживем - увидим... Доживем - узнаем... Выживу - учту  Улыбаюсь
Гром
Птычк. Тьфу, птычник... Вот!
Готовлюсь к пенсии

il
Offline Offline
Пол: Мужской
Бодрый птах


« Ответ #15 : 11-03-2005 17:24 » 

valker - стоп.
Мы говорим на разных языках.
Вот тут ты опять привел цитату, но я так и не вижу ни одного упоминания слова pure (чистая) в отношении функции виртуальной, которая имеет тело.

Есть два типа - виртуальная и чисто виртальная. Первая тело имеет вторая нет. В твоем примере идет перегрузка тела нечистой виртуальной функции, которая имеет тела.
Однако ЛЮБАЯ виртуальная функция может быть перегружена.
Более того, любая виртуальная функция может задать интерфейс. При этом в случае когда интерфейные функции имеют тело, возможно два варианта, когда есть перегрузка и когда нет.
В случае наличия тела у виртуальной функции, и использования ее в виде интерфейса, мы можем видеть стандартное заданное производителем использование функции в случае если наследник не имеет желания в этом случае ее использовать иначе.
В твоем примере стандартный случай перегрузки функции, которая имеет тело, и не является чисто виртуальной, по определению...
В вторых ссылка на статьи по виртуальным функциям находится в Яндексе в течении 1 минуты.
В третьих - если человек спорит с автором 10-ка книг - он в первую очередь предъявляет право на спор с автором, т.е. свои публикации как минимум не в меньшем колличестве.
Не имея таковых я с авторами книг не спорю, понимая, что они много опытнее меня в теории.
Поэтому прежде чем спорить о таковых терминах предпочел бы увидеть доказательство права автора сообщений на форуме на спор с авторитетом
1. В плане реальных публикаций.
2. В наличии право менять термины.

страуструп пишет о пуре функции не только о синтакисие, это просто примеро согласия страуструпа с автором основной цитаты.

Записан

А птичку нашу прошу не обижать!!!
Михалыч
Команда клуба

ru
Offline Offline
Пол: Мужской

« Ответ #16 : 12-03-2005 06:49 » 

to Гром
Цитата
Есть два типа - виртуальная и чисто виртальная. Первая тело имеет вторая нет. В твоем примере идет перегрузка тела нечистой виртуальной функции, которая имеет тела.
Мне кажется ты не совсем прав. Чистая виртуальная функция все же может иметь тело. И при этом продолжать "числиться" и далее "чистой" ...
У того же Страуструпа
Цитата
При описании класса виртуальная функция описывается как чистая с помощью спецификации-чистой (R.9.2). Чистую виртуальную функцию не нужно определять, если только она явно не вызывается с помощью конструкции уточненное-имя (R.5.1).
Бьярн Страустрап. Справочное руководство по C++  Второе дополненное издание
что собственно и проиллюстрировал valker...
Другое дело, что тут можно потеоретизировать на тему, что это какая-то "странная" получается ЧВФ. Вроде как чистая по наличию спецификатора =0, с другой стороны с телом, которое м.б. действительно использовано как некий интерфейс "по умолчанию". С одной стороны стандарту не противоречит, с другой стороны (исключительно на мой взгляд) выглядит настолько странно, что может запутать кого угодно Улыбаюсь Я, лично (м.б. в силу узости моего мышления) не вижу для себя необходимости применения подобных "оригинальных" конструкций.
Уж если надо создать абстрактный класс, а подходящего кандидата на чистую виртуальную функцию не находится (ну есть уже у всех функций тела), то тут либо надо подумать на предмет более высокого уровня абстракции (что более красиво и наверное правильно теоритически), либо, например сделать деструктор чистым виртуальным (что правда не всегда тоже возможно практически). Хотя, наверное, правильно спроектированный абстрактный класс может и не иметь тела деструктора, а интерфейсы по умолчанию - да.
Записан

Поживем - увидим... Доживем - узнаем... Выживу - учту  Улыбаюсь
Гром
Птычк. Тьфу, птычник... Вот!
Готовлюсь к пенсии

il
Offline Offline
Пол: Мужской
Бодрый птах


« Ответ #17 : 12-03-2005 06:56 » 

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

Записан

А птичку нашу прошу не обижать!!!
npak
Команда клуба

ru
Offline Offline
Пол: Мужской

« Ответ #18 : 12-03-2005 09:13 » 

Простите, что влезаю в середину обсуждения ...

Я хочу вернуться к первому примеру valker'а про "чистые виртуальные функции с телом".  По его утверждению, есть компилятор, который позволяет такой фокус.   К сожалению, это не основание утверждать, что у чистых виртуальных функций в объявлении можно указать тело.  Есть компилятор, который не компилирует приведённый пример: gcc 3.3.1

Чтобы выяснить, какой компилятор ближе следует стандарту, обратимся к разделу 10.4 стандарта
Цитата
[Note: a function declaration cannot  provide  both  a
  pure-specifier and a definition. For example,
          struct C {
                  virtual void f() { }=0; // ill-formed
          };
   --end note]
В разделе 9.2 приведена грамматика объявлений членов классов.
Цитата
          member-specification:
                  member-declaration member-specificationopt
                  access-specifier : member-specificationopt
          member-declaration:
                  decl-specifier-seqopt member-declarator-listopt ;
                  function-definition ;opt
                  qualified-id ;
                  using-declaration
                  template-declaration
          member-declarator-list:
                  member-declarator
                  member-declarator-list , member-declarator
          member-declarator:
                  declarator pure-specifieropt
                  declarator constant-initializeropt
                  identifieropt : constant-expression
          pure-specifier:
                   = 0
          constant-initializer:
                   = constant-expression

Из грамматики видно, что за pure-specifier не может следовать function-definition, поэтому пример valker'а не соответствует стандарту С++.

С другой стороны, утверждение, будто у виртуальной функции не может быть тело, тоже не верно.  Нельзя определять тело виртуальной функции в точке объявления (в теле класса), но можно вне тела класса.  Пример:
Код:
#include <iostream>

class A {
public:
    virtual int x() const = 0;
};

class B : public A {
public:
    virtual int x() const { std::cout << "B::x" << std::endl; return 1;}
};

int A::x() const { std::cout << "A::x" << std::endl; return 0;}


extern "C" int main() {
    B b;
    A* p_a;

    b.x(); // печатает B::x
   
    p_a = new B();
    p_a->x(); // печатает B::x
    p_a->A::x(); // печатает A::x

    return 0;
}

В этом примере B :: x() перегружает чистую виртуальную функцию A :: x(), при обращении p_a -> x() вызывается виртуальная функция B :: x ().  Но A :: x() можно вызвать через квалифицированное имя метода p_a-> A :: x().

Итак, чистая виртуальная функция -- это виртуальная функция, объявленная со спецификатором =0.  Для чистой виртуальной функции нельзя задать тело в точке объявления (в теле класса), но можно снабдить телом вне класса.  Передать управление в тело чистой виртуальной функции можно посредством квалифицированного имени.  Если тело виртуальной функции не задано, но вызывается, то будет ошибка линкера о неопределённой ссылке на `A :: x() const'
« Последнее редактирование: 20-12-2007 19:21 от Алексей1153++ » Записан

UniTesK -- индустриальная технология надежного тестирования.

http://www.unitesk.com/ru/
Михалыч
Команда клуба

ru
Offline Offline
Пол: Мужской

« Ответ #19 : 12-03-2005 10:06 » 

to npack
Вы просто не совсем поняли уже сказанное, а может просто не очень внимательно посмотрели. Бывает...
valker об этом тоже говорил, вот его же слова:
Цитата
И ещё одно дополнение, стандарт языка требует, чтобы тело чисто виртуальной функции абстрактного класса было определено вне класса. Т.о. мой пример не вполне соответствует стандарту.
И то, что чистые виртуальные могут иметь тело, это тоже понятно.
И у меня в топике где-то выше есть о том что и gcc и borland позволяют иметь такое тело функции, но конечно ВНЕ тела класса.
Мне кажется имеет смысл поговорить о том, надо ли вообще пользоваться подобного рода конструкциями, даже если стандарт и позволяет.
Записан

Поживем - увидим... Доживем - узнаем... Выживу - учту  Улыбаюсь
npak
Команда клуба

ru
Offline Offline
Пол: Мужской

« Ответ #20 : 12-03-2005 11:03 » 

Скорее, это я Грому привёл цитаты из самого авторитетного  источника -- стандарта С++, и подкрепил цитаты примером.

Из книги в книгу кочует утверждение, что чистая виртуальная функция  -- это функция без тела.   Так вот, "чистота" виртуальной функции не связана с наличием или отсутствием реализации (тела) функции.

Записан

UniTesK -- индустриальная технология надежного тестирования.

http://www.unitesk.com/ru/
sergvs
Гость
« Ответ #21 : 15-03-2005 08:16 » 

Прошу прощения, что не совсем по теме топика - содержание статьи "Виртуальные функции...". Речь идет об оформлении второй части статьи, где приводится пример программы. Дело в том, что некоторые фрагменты текста программы ("<iostream>", "<title",
"<endl") воспринимаются как теги html, в результате чего при просмотре в IE текст
примера выводится с искажениями, а броузеры "мозилловской" группы вообще
обрывают выдачу на середине примера. Желательно бы исправить этот косяк,
т.к. статья очень интересная, особенно для чайников вроде меня.
Записан
Гром
Птычк. Тьфу, птычник... Вот!
Готовлюсь к пенсии

il
Offline Offline
Пол: Мужской
Бодрый птах


« Ответ #22 : 15-03-2005 12:12 » 

Мой косяк - не отследил - спасибо за информаию...
Постараюсь исправить в ближайшее время.
Записан

А птичку нашу прошу не обижать!!!
xelos
Гость
« Ответ #23 : 25-03-2005 13:09 » 

тоже не совсем по теме, скорее буквоедство, однако - Если класс В наследуется от класса А, обычно (UML, а кроме него я уже и не помню где бы использовались диаграммы), стрелка стоит от В к А...
Записан
xelos
Гость
« Ответ #24 : 25-03-2005 13:11 » 

а так, статья мне понравилась - жаль я сам не применяю полиморфизм Жаль специфика не та, однако, все кратко и понятно - освежил в памяти все что касается виртуальных функций.
Записан
Михалыч
Команда клуба

ru
Offline Offline
Пол: Мужской

« Ответ #25 : 25-03-2005 16:44 » 

Цитата
тоже не совсем по теме, скорее буквоедство, однако - Если класс В наследуется от класса А, обычно (UML, а кроме него я уже и не помню где бы использовались диаграммы), стрелка стоит от В к А...
Отлично А это не диаграмма... Это, типа, просто рисунок Отлично
А вообще - спасибо всем и за конструктивную критику и за доброе слово, которое и кошке приятно... Улыбаюсь
« Последнее редактирование: 25-03-2005 17:05 от Михалыч » Записан

Поживем - увидим... Доживем - узнаем... Выживу - учту  Улыбаюсь
NetRaider
Гость
« Ответ #26 : 21-09-2005 07:06 » new

При чтении статьи возникла пара вопросов.

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

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

Тип возвращаемого значения(T1) виртуальной функции может отличаться от типа возвращаемого значения(T2) функции в базовом классе. Но  только в том случае если эти типы(T1 и T2) являются ковариантными.

Два типа являются ковариантными если:
они представлят собой ссылки или указатели на классы, И
T1 является наследником(непосредственным, либо косвенным) T2,
И T1 имеет более слабую(или ту же) cv-квалификацию, чем T2



Например:
Код:
#include <iostream>

struct A {};
struct B : A {};

struct E
{
virtual A* foo()  { return new A; }
virtual const A* foo2() { return new A;   }
virtual A* foo3() { return new A;   }
};


struct F : E
{
B* foo(){ return new B; }
A* foo2() { return 0;  } // менее слабая cv-квалификация
// const A* foo3() { return 0;   } - Ошибка, cv-квалификация более строгая
};


int main()
{
E* e = new F;

A* p = e->foo(); // F::foo
e->foo2(); // F::foo2

delete e;
delete p;
}
Записан
Страниц: [1]   Вверх
  Печать  
 

Powered by SMF 1.1.21 | SMF © 2015, Simple Machines