компании Borland, и можно надеяться, что в следующей версии Borland C++ Builder они будут исправлены, а пока... пока читайте "Мир ПК". Публикуем фрагменты оригиналов программ с ошибками и предлагаем способы предотвращения ошибочных ситуаций.
Первый листинг (см. листинг 1) демонстрирует ошибку, возникающую при использовании свойств в inline-методах. Если отключить функцию inline, то программа работает превосходно.
Ошибка скрывается в строчке:
v.dblValue=v.dblValue+3;
заставляющей происходить странные вещи. К примеру, вызывается конструктор формы, хотя в нем нет необходимости, - это, собственно говоря, и приводит к сбою. Однако не только это. В приведенном фрагменте нарушено одно правило: все операции со свойствами должны проводиться отдельно. В этом случае лучше всего будет воспользоваться промежуточной переменной:
double tempVar = v.dblValue;
v.dblValue = tempVar+3;
Теперь все формальности соблюдены, и C++ Builder сгенерирует превосходно работающий код.
Очередная ошибка также скрывается в inline-методах (см. листинг 2). Взгляните на следующую строку:
int Get(int aI) const { return (int) List->Items[aI]; }
Казалось бы, все в порядке. Переданный через параметр индекс подставляется в список, и полученный из него объект приводится к типу int. Однако этот параметр теряется, а вместо индекса подставляется самое первое число из списка List->Items. В данном примере единственный сохраненный в списке объект - число 45. Именно оно и подставляется вместо индекса. Это значит, что если список имеет менее 45 элементов, возникнет исключительная ситуация выхода за предел диапазона. Однако если список достаточно велик, ошибка не возникнет, а будет возвращено случайное значение. Ошибка возникает из-за того, что C++ Builder в inline-методах вычисляет значение в неверном порядке. По правилам языка Cи++ сначала должна быть произведена операция [], затем -> и уже потом производится приведение к типу. Однако, как показано в листинге 2, сначала производится приведение к int указателя List, а уже потом все остальное. Исправить ситуацию можно двумя способами. Первый - применить новый способ приведения:
int Get(int aI) const { return int( List->Items[aI] ); }
Тогда компилятор, вызывая конструктор объекта класса int, вынужден сначала вычислить выражение в скобках. И второй: можно просто заставить компилятор сначала вычислить приложение, а уже затем сделать приведение. Для этого достаточно добавить скобки:
int Get(int aI) const { return ( int ) ( List->Items[aI] ); }
Третья ошибка C++ Builder проявляется в момент генерации исключительной ситуации. Пример, приведенный в листинге 3, показывает ошибочную работу компилятора по обработке перехваченного исключения, генерируемого пользователем:
throw 1;
Метод, в котором происходит исключение, описан так, что возвращает экземпляр объекта класса KKK:
KKK TEv::eval()
На деле еще до создания экземпляра этого класса компилятором все равно вызывается для него деструктор. Это серьезная ошибка. К сожалению, проблема эта легко не решается. Зато здесь три решения "в лоб". Самое простое - принять во внимание документацию по Visual Component Library и создавать все объекты этой библиотеки только динамически. Это значит, что вместо строки, описанной в классе TEv:
String c;
нужно написать:
String c = new AnsiString("");
Другое решение предлагает создать пустой конструктор в классе TEv:
TEv() {};
И наконец, самое прямолинейное решение - дайте же компилятору то, что он требует, т. е. деструктор.
Только пусть это будет пустой деструктор класса KKK:
~KKK() {};
Трудно сказать, чем вызвана эта ошибка, скорее всего, неправильной инициализацией стека в момент установки обработчиков исключительной ситуации.
// #pragma resource "*.dfm" TForm1 *Form1; class DV { double value; public: double GetDblValue(){return value;} double SetDblValue(double aVal){return value=aVal;} __property double dblValue= {read=GetDblValue,write=SetDblValue}; }; // __fastcall TForm1::TForm1(TComponent* Owner) : TForm(Owner) { } // void __fastcall TForm1::Button1Click(TObject *Sender) { DV v; v.dblValue=2; // При расширении inline-функций в следующей строке происходит // завершение программы с сообщением об ошибке // 'Abnormal program termination' или 'access violation...' и указывается адрес. // Если расширение inline-функций отключено, все в порядке. v.dblValue=v.dblValue+3; ShowMessage(v.dblValue); }Листинг 2
struct TStruct { TList * const List; TStruct() : List(new TList) {} ~TStruct(){ delete List; } int Get(int aI) const { return (int) List->Items[aI]; } }; void __fastcall TForm1::Button1Click(TObject *Sender) { TStruct s; s.List->Add( (void*) 45); //Несмотря на то что мы здесь передаем индекс 0, в List->Items //попадает совершенно произвольное значение const int val=s.Get(0); ShowMessage(val); } //Листинг 3
// ... class KKK { //Необходима хотя бы одна переменная типа AnsiString String sc; public: KKK(const KKK &aV){} KKK(){} }; class TEv { String c; public: KKK eval(); }; KKK TEv::eval() { throw 1; // До этого места выполнение не дойдет //return KKK(); } void __fastcall TForm1::Button1Click(TObject *Sender) { TEv e; try{ //В этом вызове должен произойти throw на catch(int), //но программа завершается с сообщением об ошибке 'Abnormal program termination' e.eval(); } catch(int){ ShowMessage("Поймали целое"); } catch(...){ ShowMessage("Поймали что-то еще");} ShowMessage("Пошли дальше..."); } //