Отправляет email-рассылки с помощью сервиса Sendsay

Инвестиции: Вопрос-Ответ

  Все выпуски  

C/C++ Вопрос-Ответ Выпуск No 27


Информационный Канал Subscribe.Ru

C/C++ Вопрос-Ответ

Выпуск      : 27
Подписчиков : 2064
Cайт        : SoftMaker.com.ru
Архив       : C/C++ Вопрос-Ответ (архив)
В этом выпуске
От ведущего

Здравствуйте уважаемые подписчики !

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

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

Многим может быть также интересна рассылка: Visual С++ - расширенное программирование, где можно прочитать описания нестандартных приемов программирования с помощъю библиотеки MFC - 'трюков', 'хаков', недокументированных функций.

С уважением, Вахтуров Виктор.

Подписчикам

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

Для того, чтобы задать свой вопрос, пришлите письмо, кликнув по этой ссылке.
Для того, чтобы ответить на вопрос, надо кликнуть по ссылке "ответить", расположенной под текстом вопроса.

Небольшое примечание. Господа, если Вы хотите, чтобы Ваш e-mail был опубликован в рассылке, специально и явно укажите это в письме. Иначе e-mail адреса, указанные в теле Вашего письма в рассылке опубликованы не будут.

Вопросы

Для того, чтобы задать свой вопрос, кликните этой ссылке (вопрос будет опубликован в следующем номере).
Вы можете задавать любые вопросы, касающиеся программирования на языке C и C++. Это могут быть вопросы, касающиеся как конструкций языка, применения библиотек классов, шаблонов (таких как MFC или STL), использования компиляторов, так и самой философии программирования на C или C++. Здесь нет ограничений - спрашивайте и получайте ответы.

Вопрос № 103 ( dimaz )

Скажите пожалуйста, как увеличить производительность отрисовки GDI, GDI+? При определённом количестве точек в линии она начинает медленно перерисовываться (Например при редактировании координат точки - когда тащишь одну из вершин линии мышью).
Разрабатывается векторный редактор (GDI+). Выяснилось что главный тормоз - алгоритм рисования линий, кривых GDI+.
Или мож кто знает как можно устроить механизм отрисовки оптимально?

Ответить на вопрос

Вопрос № 104 ( Diosso )

Люди, как настроить Вижак .NET, чтоб прога не юзала msvcr71d.dll и
прочую муть. Или для этого как то по-особенному надо создавать проект?

Ответить на вопрос

Вопрос № 105 ( Рассылка )

1. Как в Visual C++ сделать кнопки и надписи на них разного цвета.
2. Как с помощью диалогового окна открыть файл и записать
в него по определенному смещению двоичные данные ?
пришлите ответы на zxzx67@bk.ru

Ответить на вопрос

Вопрос № 106 ( Sergey )

Здравствуйте, уважаемые!

Мне нужен исходник класса регулярных выражений.
Причём не привязанный к реализации какого либо производителя, то есть
без их спец классов/объектов, что бы я мог скомпилировать программу и
в bcb и vc и ещё где захочу. Всем заранее спасибо.

Ответить на вопрос

Ответы

Ниже приведены вопросы предыдущего выпуска и ответы на них.

Вопрос № 100 ( Arsa )

Приветствую всех !

Простой, наверно, вопрос. Но как сделать такое - не знаю.
Пишу на Visual C++ (MFC).
В диалоге есть обычный редактор. Для него с помощью Class Wizard-а я сделал объект класса CEdit. В этот редактор я заношу текст.
Я хочу использовать этот редактор только для просмотра. То есть пользователь не должен иметь возможности редактировать текст. Такое достигается установкой стиля ES_READONLY (есть также соответствующий флажок в редакторе ресурсов).
НО !!! При этом фон редактора становится СЕРЫМ.
А мне хотелось бы, чтобы он был такого же цвета как и при редактировании.

Буду очень признателен, если кто нибудь подскажет решение проблемы (правда уже не уверен, что такое вообще возможно).

Ответ ( Neco )

Это стопудово возможно, т.к. на других языках программирования это
достигается очень легко (там где визуальная настройка параметров).
Попробуй после этого перекрасить назад в белый - вдруг получится?

Ответ ( Цыпленков Евгений )

А все просто: надо задать этому окну (коим является и поле ввода) стиль WS_DISABLED . Или для объекта класса CEdit вызвать функцию EnableWindow, а в качестве параметра передать FALSE.

Ответ ( 3V )

Я такое делал очень просто - обрабатывал для EDIT-а сообщение WM_GETDLGCODE (в MFC есть макрос для карты сообщений ON_WM_GETDLGCODE и обработчик можно добавить через Class Wizard) и возвращал в качестве результата (DLGC_WANTARROWS | DLGC_WANTTAB). Еще, кажется, обрабатывал WM_CUT, WM_PASTE, WM_CLEAR, WM_UNDO. В обработчике WM_CUT вызывал CEdit::Copy, а в обработчиках WM_PASTE, WM_CLEAR, WM_UNDO - ничего не делал (просто эти команды не срабатывали). Их конечно, в контекстном меню также погасить нужно.
Вопрос № 101 ( synops )

Здравствуйте. Начал тут разбираться с исключениями в C++. Как то все странно пока выглядит. В связи с чем и возникли вопросы.

Итак.

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

2. Для обработки исключений есть конструкции try и catch.
Но есть также __try, __except и __finally.
Что то не понятно, чем они отличаются, если обе нужны для одного и того же.

3. Собственно, как работает __try, __except и __finally ?
Как я понял, можно использовать только __try и __except или __try и __finally, но можно и сразу __try, __except и __finally, причем вызывается и __finally и __except. Но какой в этом смысл ?

Буду очень благодарен за ответы !

Ответ ( Gena )

1. Проверки делать надо. А поймав исключение, ты его должен обработать в блоке обработчика (при делении на нуль, например, предпринять какие-либо действия), иначе последствия необработанных исключений могут быть самые разные - от безобидных неправильных результатов до нарушения блоков данных в памяти (они проявятся при дальнейшей работе программы - после блоков проверки и обработки исключений).

2. Try и catch являются средствами самого С++, а __try и __except и __finally являются их "потомками" что ли, и используются при программировании с использованием API Win32 и их совместное использование не рекомендуется.

3. __try и __except используются в том случае, когда только при возникновении исключения нужно выполнить некоторые действия (например, выдать соответствующее сообщение), а __except и __finally - когда действие в блоке __finally должно выполниться в любом случае после того как выполнится или не выполнится код в блоке __try. Например, выделяется память для объекта, затем с ним должны быть произведены некоторые действия, после которых память должна быть освобождена. Тогда в блок __try пишут код действий над объектом, а в __finally - освобождение памяти, который будет выполнен и в случае созникновения исключения и в случае нормального выполнения кода в блоке __try.
А смысл может заключаться в слеующем: при обрботке исключения нужно уведомить пользователя о возникшем сбое в работе, а затем освободить пямять. Вот и получается, что уведомление выдается в блоке __except, а освобождение памяти - в __finally.

Ответ ( Шматко А.А. )

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

if(/* ... */) throw /* ... */

а обработка возникшей ошибки отделена от основного алгоритмического
наполнения функции, и возможно все такие "обработки" собраны вместе
рядышком в конце функции или даже в вышележащей (по стеку вызовов)
функции. Сравни:

if(/* ... */)
{
 /* ... */
}
else
{
 /* ... */
}

в которой скорее всего будут ещё и вложенные if для других проверок на
ошибки, и где всё свалено в кучу и перемешано. К тому же исключения могут
обрабатываться не только в самой функции, в которой зарегистрирована
ошибка, но и выше по стеку вызовов функций. Поэтому отпадает необходимость
в предусматривании специального значения, которым функция будет
сигнализировать о своих ошибках. Вместо этого функция, бросая исключение,
просто "не завершается", а снимается с выполнения.
Исключения могут использоваться также в тех случаях, когда методы
обработки возникшей ошибки самой функции неизвестны. Или неоднозначны. В
этом случае функция, бросая исключение, рассчитывает, что вызывающий её
код знает, как с честью выйти из создавшейся ситуации, и обработает эту
ошибку наиподходящим образом. Такое поведение типично для библиотечных
функций. Например, std::vector<>::at() бросает исключение
std::out_of_range, если индекс выходит за допустимый диапазон. Как выйти
из этой ситуации, лучше всего решать программисту, а не вектору.
Однако, стандарт C++ допускает существование платформ, в которых аппаратно
могут генерироваться некоторые сигналы, которые исполнительной средой
отображаются в исключения C++. В таких случаях исключения могут
"появиться" в программе из "ниоткуда", так как соответствующих им throw
просто нет. В твоём случае это как раз может вылиться в отсутствие
необходимости проверять на нуль возвращаемое функцией значение. Однако,
стандарт не обязывает все реализации (даже те, где соответствующая
аппаратная поддержка имеется) поступать таким образом. Поэтому в первую
очередь нужно посмотреть документацию на используемую тобой платформу
(впрочем, я почти уверен, что это будет Win32 environment на аппаратной
платформе Intel x86 IA32) и
компилятор, и если ты не увидишь там упоминания о том, что исполнительная
среда ведёт себя именно таким вот "удобным" образом, проверять на нуль
всё-таки придётся. Впрочем, смотри следующий пункт.
2. try и catch (а также throw) - это элементы языка C++, являющиеся
составной частью механизма обработки исключительных ситуаций C++. __try,
__except и __finally (а ещё __leave и куча функций - RaiseException(),
GetExceptionInformation(), AbnormatTermination(),
AddVectoredExceptionHandler() и т.п., - структур - EXCEPTION_RECORD,
EXCEPTION_POINTERS и т.п. - и констант - EXCEPTION_CONTINUE_SEARCH,
EXCEPTION_NONCONTINUABLE, EXCEPTION_MAXIMUM_PARAMETERS и т.п.) - это
элементы языков C++ и plain-C, причём отсутствующие в стандарте и
являющиеся расширением этих языков. Эти конструкции введены Microsoft
специально для поддержки механизма структурированной обработки исключений,
являющегося частью Win32, то есть фактически - операционной системы.
К языку этот механизм имеет слабое отношение. Структурированная обработка
исключений может использоваться и другими языками. Например, Delphi также
её поддерживает, только с помощью немного других языковых конструкций.
Не следует путать эти два механизма. Хоть они по назначению и похожи, но
реализуются и используются по-разному. В отличие от исключений C++,
структурированные исключения (также называемые SEH - Structured Exception
Handling) сильнее завязаны на исполнительную среду. В частности имеется и
стандартное исключение EXCEPTION_INT_DIVIDE_BY_ZERO, которое бросается при
попытке целочисленного деления на нуль, а также
EXCEPTION_FLT_DIVIDE_BY_ZERO, при делении с плавающей точкой. В своём коде
можно записать, например:

int someFunc();

__try
{
 someVariable=someValue/someFunc();
}
__except(GetExceptionCode()==EXCEPTION_INT_DIVIDE_BY_ZERO ?
        EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH)
{
 /* обработка деления на нуль */
}


и тогда - вуаля! - то что надо. На нуль перед делением можно не проверять.
Но не следует забывать, что исключительные ситуации - это ИСКЛЮЧИТЕЛЬНЫЕ
ситуации. Предполагается, что они возникают в исключительных случаях. То
есть редко. Так как их обработка - хоть SEH, хоть в стиле C++ - вообще
говоря, не торопится. Если ситуации с возможным возвратом нуля ожидается
часто, а обработка каждого случая индивидуальна и локальна по месту
возникнования, лучше обойтись без исключений.
3. Сразу и __except, и __finally за одним и тем же __try следовать не
могут, иначе будет ошибка компиляции. Если за __try следует __except, то
такой блок называется блоком исключений (exception block), если же за
__try следует __finally, то он называется блоком завершения (termination
block). Блоки исключения предназначены для отлова и обработки брошенных
исключений, тогда как блоки завершения для гарантированного исполнения
кода в любом случае, даже если из "вложенно вызванной" функции будет
брошено исключение, которое перехватывается в нижележащей функции или не
перехватывается вообще.
Например:

CRITICAL_SECTION ct;

void someFunc1()
{
 /* ... */
 // Проверка на ошибку
 if(/* ... */) RaiseException(/* ... */);
 /* ... */
}

void someFunc2()
{
 /* ... */
 EnterCriticalSection(&ct);
 __try
 {
  /* ... */
  someFunc1();    // Из этой функции, возможно,
                  // будет брошено исключение
  /* ... */
 }
 __finally
 {
  LeaveCriticalSection(&ct); // Этот код вызовется ВСЕГДА,
                             // хоть было исключение, хоть нет
 }
 /* ... */
}

void someFunc3()
{
 /* ... */
 __try
 {
  /* ... */
  someFunc2();
  /* ... */
 }
 __except(/* ... */)  // Здесь брошенное исключение
                      // обработается (или нет)
 {
  /* ... */
 }
}

int main()
{
 InitializeCriticalSection(&ct);
 /* ... */
 someFunc3();
 /* ... */
 DeleteCriticalSection(&ct);
}

Без блоков завершения невозможно было бы проектировать надёжный код. В
приведённом примере если LeaveCriticalSection() не заключить в блок
завершения, то освобождение критической секции выполнялось бы только если
исключение в функции someFunc1() не бросалось. В случае же броска
исключения функции someFunc1() и someFunc2() просто снимались бы БЕЗ
выполнения операторов между броском исключения (в функции someFunc1())
или вызовом someFunc1() (в функции someFunc2()) и концами соответствующих
функций, и критическая секция НАВСЕГДА осталась бы занятой.
В C++ аналога __finally нет. Но он там и не нужен, так как в C++ есть
классы и "деструкторы к ним". На C++ можно было бы написать так:

class CritSect
{
 mutable CRITICAL_SECTION ct;

public:
  CritSect()    { InitializeCriticalSection(&ct); }
 ~CritSect()    { DeleteCriticalSection(&ct); }
 void Lock() const    { EnterCriticalSection(&ct); }
 void Unlock() const    { LeaveCriticalSection(&ct); }
};

class Guard
{
 const CritSect& ct;

public:
 Guard(const CritSect& src) : ct(src) { ct.Lock(); }
 ~Guard()    { ct.Unlock(); }
};

CritSect ct;

void someFunc1()
{
 /* ... */
 // Проверка на ошибку
 if(/* ... */) RaiseException(/* ... */);
 /* ... */
}

void someFunc2()
{
 /* ... */
 {
  Guard sentry; // Простого объявления "охранника" достаточно.
      // Его конструктор и деструктор сами всё сделают.
  /* ... */
  someFunc1(); // Из этой функции, возможно, будет
               // брошено исключение
  /* ... */
 }
 /* ... */
}

void someFunc3()
{
 /* ... */
 __try
 {
  /* ... */
  someFunc2();
  /* ... */
 }
 __except(/* ... */) // Здесь брошенное исключение
                     // обработается (или нет)
 {
  /* ... */
 }
}

int main()
{
  /* ... */
 someFunc3();
 /* ... */
}

Этот пример демонстрирует принцип "выделение ресурса есть инициализация".
В классе CritSect конструктор инициализирует экземпляр класса, "выделяя
ресурс" критической секции. Деструктор выполняет очистку. В классе Guard
инициализация заключается в захвате критической секции, а деструктор,
выполняя очистку, освобождает её. Язык C++ гарантирует корректное
разрушение (читай - вызов деструкторов) всех успешно сконструированных
локальных объектов, принадлежащих областям видимости, снимаемым в процессе
размотки стека при обработке брошенного исключения.
Но я отвлёкся. Как видишь, у блоков завершения и блоков исключения разные
назначения. И используются они с разыми целями. Поэтому они вполне могут
использоваться и вместе, если в этом возникла необходимось. Нужно просто
ипользовать вложенные __try. Например:

__try
{
 __try
 {
  /* ... */
 }
 __finally
 {
  /* ... */
 }
}
__except(/* ... */
{
 /* ... */
}

Всего-то...
:)

Ответ ( Владимир )

Кодишь ты в C++Builder. Так вот эти __try, __except, __finally Добавлены
для совместимости с f___ing Delphi. Забей на них. Все равно они слизаны с
С++ и особенно не отличаются от try/catch. Borland вообще напихала в
Великий и Могучий столько несовместимых со стандартом С++ расширений, что
теперь уже можно говорить о различных диалектах С++: ANSI C++,
Borland C++, Microsoft C++, Microsoft managed C++, GNU C++. Если Microsoft
сохраняет приличия, то Borland совсем охренела и делает с С++ что хочет.
Вопрос № 102 ( lera )

Только учусь программировать на C++.
Пытаюсь писать программы, применяя везде классы.
Расскажите пожалуйста, когда функции класса надо делать виртуальными, а когда нет ?
То есть как выбрать, сделать ли функцию виртуальной ?
Я поняла так, что если это внутренняя функция (для внутренних целей), а также ее потом изменять будет не надо, то она не должна быть виртуальной, а если она может быть изменена в классах-наследниках, то лучше ее сделать виртуальной. Но как точно узнать, какие функции будут, а какие не будут изменяться ?

Ответ ( Пупкин Василий )

Виртуальная функция - это функция, которую можно
переопределять(правильно).
Все обработчики сообщений являются виртуальными функциями . Потому-что
иногда возникает надобность переопределять действия выполняемые
обработчиком.Например чтобы по открытии дочернего
окошка блокировать родительское. А по его закрытии разблокировать
его.
В среде Borland C++ BUILDER обработчик для события(=сообщение) OnShow
(отображение окна) выглядит так:

void __fastcall TForm2::ShowForm(TObject *Sender)
{
        Form1->Enabled=false;
}

То бишь в готовом объекте TForm2 мы переопределили функцию(попробуй
сделать с обычной, если получиться то пиши мне - сходим пива попит :-))
Вот обработчик сообщения по закрытию окна OnClose для разблокировки
1-го окна:

void __fastcall TForm2::CloseForm(TObject *Sender,
                                  TCloseAction &Action)
{
        Form1->Enabled=true;
}

И всё это виртуальные функции.
                Sierokesha

Ответ ( Шматко А.А. )

Никто лучше тебя не ответит на этот вопрос. Парадигма
объектно-ориентированного программирования по своим принципам проста,
однако грамотно применять её принципы - это не один год практики и опыта.
Твоё понимание верно, но только в общих чертах.
Класс представляет собой мощный инструмент абстракции под названием
"чёрный ящик". Когда мы нажимаем кнопочки на "ленивчике", нас абсолютно не
беспокоит, что там внутри происходит. Нам важно только, что определённые
манипуляции на нём вызовут соответствующее воздействие на телевизор (или к
чему там этот "ленивчик" прилагается). Публичный интерфейс класса (public)
как раз и есть этот самый "ленивчик". Он определяет правила воздействия на
объект, представляемый классом, и способы получения информации о его
состоянии. Внутренее содержание класса - это его собственное дело. Это
приватная его часть (private). Есть ещё интерфейс для разработчика. Это
канал связи между "ленивчиком" и телевизором. Должны же схемотехники
как-то реализовывать "послушание" телевизора. Этот интерфейс не может быть
приватным, но и публичным быть не должен. Нам как пользователям доступ к
нему не нужен, даже наверное, вреден - сломаем ещё что-нибудь, не зная,
куда лезем. Это защищённый интерфейс (protected).
Так что понятие внутренней и "внешней" функции слабо соотносится с
необходимостью объявлять их виртуальными или невиртуальными.
"Внутренность" и "внешность" - это вопрос исключительно принадлежности к
одному из трёх категорий интерфейсов. Тот факт, что чаще всего присходит
именно так, как у тебя сложилось понимание - просто "совпадение". Так
действительно чаще всего и происходит. Но это не означает, что делать
выбор между виртуальностью или невиртуальностью нужно, исходя только из
этого признака.
Виртуальность или невиртуальность - это понятие совсем другой парадигмы.
Базовый класс может определять интерфейс, а производные - расширять,
реализовывать или дополнять его. Если предполагается возможным именно такая
эксплуатация некоторых методов класса, стоит подумать над их потенциальной
"виртуальностью". Например, в стандартной библиотеке класс
basic_streambuf<>
занимается буферизацией потока данных ввода/вывода. Он обслуживает сам
буфер, то есть поддерживает выборку их него и помещение в него данных,
выполняет позиционирование текущего указателя и т.п. Много чего. Но он не
умеет главного - выполнять собственно ввод/вывод. Для обеспечения
необходимой функциональности у него имеются методы overflow() и
underflow() (кстати, защищённые виртуальные), которые должны выполнять всю
работу, но у него они просто пусты. Он этому "не научен", так как работа с
физическим носителем информации не в его компетенции. А вот производные от
него классы basic_filebuf<> и basic_stringbuf<> как раз переопределяют эти
методы и выполняют нужные действия, причём по разному: basic_filebuf<>
выполняет файловый ввод/вывод, а basic_stringbuf<> обслуживает запросы
только по выводу, которые при необходимости расширяют строку в памяти. При
необходимости можно реализовать собственный класс, например, какой-нибудь
basic_netbuf<>, который будет выполнять обмен данными по сети,
переопределив соответствующие методы.
Главное, что классы basic_istream<> и basic_ostream<> принимают указатель
на basic_streamf<> в своих конструкторах. При этом на самом деле им может
быть передан (и так и происходит в подавляющем большинстве случаев)
указатель на производный класс, но благодаря полиморфизму, обеспечиваемому
виртуальными методами, вызваны будут переопределённые в производных
классах методы, а не оригинальные, которые определены в базовом классе.
Причём какие именно, даже сами basic_istream<> и basic_ostream<> не знают.
Впрочем, это их и не интересует.
Я думаю, на этом примере будет проще разобраться, когда нужны виртуальные
методы, а когда достаточно невиртуальных. Дело не столько в том, какие
методы БУДУТ меняться, а какие нет. Дело в том, какие ПРЕДНАЗНАЧЕНЫ для
переопределения, а какие не предназначены. А это должен решать автор
класса, проектируя его публичный и защищённый интерфейсы. Резюмируя, если
некоторая функциональность будет изменяться - и тем более если ДОЛЖНА
изменяться - в производных классах, методы их реализующие должны быть
виртуальными. В противном случае виртуальность не нужна. Однако введение
виртуальности в этом случае ошибкой с точки зрения компилятора всё-таки не
будет. Ему-то какая разница? Просто человек, читающий исходники, в которых
будут лишние виртуальные методы, будет несколько озадачен таким положением
дел и возможно будет долго думать, что бы это значило.

Ответ ( Павел Чистяков )

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

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

Какие функции будут изменяться, а какие нет, решать только тебе, так как ты проектируешь класс.

Ответ ( Владимир )

Вообще виртуальные функции нужны для осуществления динамического
полиморфизма.
На деле это выглядит так: Имеется базовый и производный классы.
CBase, CDerived в котором есть метод foo. так вот если он невиртуальный,
то будет так:

СBase* c = new Cderived();
c->foo(); // метод из базового класса

а если виртуальный, то

СBase* c = new Cderived();
c->foo(); // метод из производного класса класса

То есть если ты в производном классе планируешь переопределять методы из
базового класса, но возможно то что обьекты производных классов будут
передаваться как указатели на базовый класс, и использоваться как во
втором примере, то их методы нужно делать виртуальными. Хороший пример:
обьект TObject *sender. Например обработчик события от TButton, передает в
sender обьект Button. Так как большинство методов в классах VCL
виртуальные, то вызываются методы из TButton, хотя обьект имеет тип
Tobject. Вызываются методы из TButton, так как указатель указывает на
TButton, несмотря на то что тип его TObject.
Книги по C/C++
Программирование игр для Windows. Советы профессионала (+ CD-ROM)
Программирование игр для Windows. Советы профессионала (+ CD-ROM)

Автор: Андре Ламот

Книга предназначена для читателей, интересующихся вопросами разработки игр в операционной системе Windows. В ней освещены разнообразные аспекты программирования игр - от азов программирования до серьезного рассмотрения различных компонентов DirectX, от простейших физических моделей до сложных вопросов искусственного интеллекта. Книга будет полезна как начинающим, так и профессиональным разработчикам игр для Windows, хотя определенные знания в области программирования (в частности, языка программирования C или C++), математики и физики существенно облегчат изучение материала.

Страница книги на Озоне
OpenGL. Профессиональное программирование трехмерной графики на C++ (+ CD-ROM)
OpenGL. Профессиональное программирование трехмерной графики на C++ (+ CD-ROM)

Автор: Сергей Гайдуков

Книга посвящена использованию новых возможностей графической библиотеки OpenGL версии выше 1.2 в приложениях, разрабатываемых на языке C++ в Microsoft Visual Studio .NET 2002. Описано применение средств NVIDIA OpenGL SDK для создания реалистичных трехмерных изображений. На примерах рассмотрены загрузка текстур из файлов форматов TGA и JPG, экспорт моделей из 3ds max, хранение данных в ZIP-архивах, отсечение невидимой геометрии, моделирование глянцевых объектов и др.

Прилагается компакт-диск с инструментальными средствами и демонстрационными версиями рассматриваемых примеров.

Для программистов.

Страница книги на Озоне
Всего доброго. До встречи в следующем номере.

http://subscribe.ru/
http://subscribe.ru/feedback/
Подписан адрес:
Код этой рассылки: comp.soft.prog.cppqa
Отписаться

В избранное