компании 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() {};

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

Листинг 1

// 
#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("Пошли дальше...");
}
//