TRegistry
void __fastcall TForm1::Button1Click(TObject *Sender)
{
TRegistry* regKey;
regKey->RootKey=HKEY_LOCAL_MACHINE;
regKey->OpenKey("\\Software\\A Projects",true);
}
выбивает сообщение
Access violation at adress 40048416 in module rtl60.bpl.Read of address 0000002D
Почему....?????
Скажите честно, вы про операторы new и delete хоть что-нибудь слышали?
Давайте, угадаю с трёх раз: нет :D
void __fastcall TForm1::Button1Click(TObject *Sender)
{
TRegistry* regKey = new Registry();
regKey->RootKey=HKEY_LOCAL_MACHINE;
regKey->OpenKey("Software\\A Projects",true);
}
да, не забудь закрыть ключ
Reg->CloseKey();
и освободить память
delete Reg;
delete
void __fastcall TForm1::Button1Click(TObject *Sender)
{
TRegistry* regKey = new Registry();
regKey->RootKey=HKEY_LOCAL_MACHINE;
regKey->OpenKey("Software\\A Projects",true);
}
да, не забудь закрыть ключ
Reg->CloseKey();
и освободить память
delete Reg;
delete[/QUOTE]
И обработать исключения. Ведь гарантии выполнения new Registry() нет, хотя на практике эксепшна на этом месте я ещё не встречал.
Обрабатывать в начале метода исключение не нужно.
В этом случае его обработка произойдёт внутри объекта Application
Про new, delete как раз и слышал но давно ими не пользовался(разве при создании собственных классов когда учил чистый С++), а Builder учу недавно вот потому и протупил......
помоему(недавно вспомнил) более елегантно будет так
TRegistry& regKey = *new Registry();
Код:
TRegistry& regKey = *new Registry();
Без обид, но это стилистический бред, вызванный неприятием оператора "->".
Для объектов, которые создаются и удаляются вручную, используются указатели (*).
В этом случае его обработка произойдёт внутри объекта Application[/QUOTE]
Ага, и мы вываливаемся из нашей функции, не закрыв файлов, не освободив память, не сохранив результаты вычислений, не записав сообщение об ошибке в лог.
1. Открытие/закрытие файлов лучше сделать а) глобально, записывая хендл как поле формы; б) локализованно, внутри гарантированно рабочего кода
2. Для автоматического освобождения памяти можно использовать стандартный билдеровский контейнер sPointer <T>, и забить на delete :D
3. Уж если такие ошибки начинаются, по про результаты можно забыть
4. А для лога проще один раз написать обработчик, и прицепить его на событие Application->OnException
И вообще, я говорил про частный случай, когда нет нужды лишний раз писать try|catch
try{} __finally{} чем не гарантированно работающий код?
[QUOTE=el scorpio]3. Уж если такие ошибки начинаются, по про результаты можно забыть[/QUOTE]
Далеко не всегда. Ох как не всегда...
Честно говоря, просто лень дискутировать.
согласен...
И с чем же, интересно, вы соглашаетесь :confused:
1. Если создаётся один объект, то при ошибке его создания, освобождать ничего не нужно. Если больше - то см. sPointer и не майтесь дурью :D
2. Если произошла ошибка в начале функции, значит никаких "результатов" быть не может.
3. Лог ошибок проще сделать один, и присобачить к Application->OnException.
4. Я говорил о конкретном случае.
И вообще. Ошибка "выделения памяти" - это вообще бред. За всю историю работы с Windows, я встречал только одну программу, которая переполняла мне ОЗУ. Речь идёт о "Heroes of M&M 4", которая, я более чем уверен, допускала банальную утечку памяти.
3. Лог ошибок проще сделать один, и присобачить к Application->OnException.
4. Я говорил о конкретном случае.
И вообще. Ошибка "выделения памяти" - это вообще бред. За всю историю работы с Windows, я встречал только одну программу, которая переполняла мне ОЗУ. Речь идёт о "Heroes of M&M 4", которая, я более чем уверен, допускала банальную утечку памяти.[/QUOTE]
2. Пример: Проводим сложную обработку данных и перед записью результата в файл хотим сохранить состояние некоторых контролов и позицию окна в файл (для чего нажимаем соотв. кнопку), и вдруг облом... А ты говоришь...
3. Очень часто мне не надо, чтобы все эксепшены обрабатывались в одном месте. Пример из жизни. У меня есть БД, в которую я в разные таблички заливаю всякие хитрые данные. Так вот, экспшны при заливке разных таблиц имеют разные последствия, при некоторых приходится останавливать заливку и подчищать хвосты, а некоторые можно просто проигнорировать и продолжать заливку. Это всё гораздо проще отследить , если иметь несколько блоков try{} catch{}.
4. Сталкивался неоднократно раз, например, при работе с графикой (хотя бы с 180-мегапикселовой картинкой.
[/QUOTE]
В этом случае, данные всегда останутся доступными, так как они будут хранится в полях формы или же в "фоновом" потоке вычисления. Но локальными для метода-обработчика они быть в описанной ситуации просто не могут.
И вообще, есть такое правило: "не усложнять". :D
Писать try / catch на каждый фрагмент кода - напрасная трата времени и ресурсов.
Подобный код будет оптимальным для данной ситуации, без какого-либо обработчика исключений
Код:
void TForm1::Button1Click ()
{
sPointer <TIniFile> file = new TIniFile (Edit1->Text);
// здесь мы можем читать/писать файл, а также выполнять команды, способные вызвать ошибку
// использовать file можно как простой указатель TIniFile*, но удалять его не требуется
}
{
sPointer <TIniFile> file = new TIniFile (Edit1->Text);
// здесь мы можем читать/писать файл, а также выполнять команды, способные вызвать ошибку
// использовать file можно как простой указатель TIniFile*, но удалять его не требуется
}
Именно про это я и говорил
Саласен полностью
Код:
void TForm1::Button1Click ()
{
sPointer <TIniFile> file = [COLOR=RED]new TIniFile (Edit1->Text)[/COLOR];
// использовать file можно как простой указатель TIniFile*, но удалять его не требуется
}
{
sPointer <TIniFile> file = [COLOR=RED]new TIniFile (Edit1->Text)[/COLOR];
// использовать file можно как простой указатель TIniFile*, но удалять его не требуется
}
[/QUOTE]
Удалять-то его не требуется, но речь шла не о том.
Если обломается конструкция, которую я отметил красным (сгенерится исключение при выполнении оператора new), то... см. мой пост про несохранённые данные. Про некоторые неудобства с OnException я тоже писал. Так что от try{} catch{} или try{} __finally {} иногда не уйдёшь. И вообще, по моему скромному мнению, использовать эти конструкции отнюдь не зазорно, главное -- не увлекаться.
Если обломается конструкция, которую я отметил красным (сгенерится исключение при выполнении оператора new), то... см. мой пост про несохранённые данные. Про некоторые неудобства с OnException я тоже писал. Так что от try{} catch{} или try{} __finally {} иногда не уйдёшь. И вообще, по моему скромному мнению, использовать эти конструкции отнюдь не зазорно, главное -- не увлекаться.[/QUOTE]
Скорее всего, обломается не new, а конструктор объекта, из-за того, что ему сунут недопустимое имя файла в виде параметров :rolleyes: .
Что же касается "несохранённых данных", то можно сказать только одно: здесь их нет.
Ибо при исключении, теряются значения только тех объектов, что лежат в стеке внутри обрабатывемой ситуации. В данном случае, try расположен внутри объекта Application, посему "потерять" можно только то, что объявлено в обработчике вызываемого события - а таковых данных в этом коде ещё нет - они появятся позже, когда объект уже будет создан, а файл - открыт для доступа.
Конечно, возможна ситуация, когда данный метод будет вызван не действием пользователя, а из какого-либо другого фрагмета кода (например, this->Button1->Click(). Тогда, согласен, данные другого фрагмента будут потеряны, но эту проблему нужно будет решать в том самом "другом" коде.
Более того, в этом случае, "глушение" исключения внутри обработчика OnButton будет просто неправильным, ибо как-то требуется передать сообщение об ошибке вызываемой функции вызвавшей. А иного способа придумать сложно.
Легко.
Код:
{
try
{
/* здесь какой-то код */
this->Button1->Click(); /* <-- что, согласен, крайне не рекомендуется */
}
catch(Exception &E)
{
/* подчищаем хвосты */
}
catch(int &ExternalException)
{
if(ExternalException == 1)
{
/* сообщаем об обломе конструкции this->Button1->Click(); */
}
}
}
TForm1::Button1Click(TObject *Sender)
{
try
{
/* код */
}
catch(Exception &E)
{
/* обрабатываем */
throw 1; /* передаём эстафету */
}
}
try
{
/* здесь какой-то код */
this->Button1->Click(); /* <-- что, согласен, крайне не рекомендуется */
}
catch(Exception &E)
{
/* подчищаем хвосты */
}
catch(int &ExternalException)
{
if(ExternalException == 1)
{
/* сообщаем об обломе конструкции this->Button1->Click(); */
}
}
}
TForm1::Button1Click(TObject *Sender)
{
try
{
/* код */
}
catch(Exception &E)
{
/* обрабатываем */
throw 1; /* передаём эстафету */
}
}
Напоследок пример из жизни. Попробуй-ка применить Application->OnException, если тебе в обработчике исключения надо в лог записать значение локальной переменной. Учитывая, что экземпляров TMyMDIChild может быть несколько, соответственно, в каждом экземпляре значение переменной своё, поэтому глобальная переменная не катит.
[/QUOTE]
Например выделил я память под какие-то объекты, структуры (чтобы считать допустим настройки из реестра), а new TRegistry взял и не отработал. Легче в этом случае все почистить в catch().
Структуры лучше создавать в стеке, а слишком большие объекты - через всё тот же sPointer. И не нужно будет ничего "подчищать" - само удалиться :D
[quote=Plisteron]
Напоследок пример из жизни. Попробуй-ка применить Application->OnException, если тебе в обработчике исключения надо в лог записать значение локальной переменной. Учитывая, что экземпляров TMyMDIChild может быть несколько, соответственно, в каждом экземпляре значение переменной своё, поэтому глобальная переменная не катит.
[/quote]
Прошу уточнить пример.
Если генерируется исключение по команде разработчика, потому что эта самая "локальная переменная" имеет недопустимое с точки зрения алгоритма значение, то всё решается очень просто.
Каждое исключение типа Exception имеет свойство Message. Это свойство формируется из "строки исключения" и "параметров исключения". Так что, всего-навсего, нужно сделать строку сообщения форматируемой, и подставлять в неё параметры
Код:
const AnsiString cseMyErrorMessage = "Переменная x в окне %p имеет неправильное значение '%d'";
throw Exception (cseMyErrorMessage, ARRAYOFCONST ((this->Caption, x)));
throw Exception (cseMyErrorMessage, ARRAYOFCONST ((this->Caption, x)));
И полученная строка, в которой символы подстановки будут заменены на указанные параметры, запишется в лог.
Ну что ты заладил всё про указатели да про указатели...
Не подчистятся, например:
Printer()->BeginDoc();
CreateFile();
DetDC();
fopen();
FindFirst();
[QUOTE=el scorpio]Прошу уточнить пример.[/QUOTE]
Уточняю. Идёт загрузка данных в БД Oracle в нескольких окнах (некоторые данные берутся из файла, другие являются результатами работы программы). В БД Oracle, естественно, отрабатываются различные ограничения целостности данных и триггеры, в результате работы которых часть данных может быть не принята. Структуры данных разные. Исключение, конечно же, генерится самим Ораклом и передаются в компоненты ODAC, которые, в свою очередь, генерят исключение в моей проге с соответствующей диагностикой от Оракла. Таким образом, я throw в проге в данном случае не использую, исключение генерится не мной. И, по-твоему, я должен в OnException смотреть, в каком окне у меня сгенерировано исключение, посмотреть, какая у меня там структура загружаемых данных, выяснить, какие поля там ключевые, какая ещё пояснительная информация нужна пользователю и продампить всё это в лог? Не проще ли для каждой процедуры загрузки сделать свой обработчик?
Ещё: что будет понятнее пользователю: "Переменная x в окне 0x7fa2 имеет неправильное значение '177374', переменная id равна 456789"
или:"При загрузке лицевого счёта плательщика дата платежа более ранняя, чем дата выставленного счёта. Код плательщика 456789"?
Да, чуть не забыл: в проге есть конструкция, которая гвоворит, принимать ли данные "обо всех" одной транзакцией или же "о каждом" в рамках отдельных транзакций.
Код:
if(!checkTransaction->Checked)
os->StartTransaction();
os->StartTransaction();
Соответственно, при "зачистке" я должен посмотреть, была ли начата транзакция в этом соединении, и если да, общая ли она или персональная и принять решение, откатить её или нет. И что мне теперь, в OnException ещё и указатель на соединение с БД передавать и все данные, от которых зависит решение об откате?
Цитата:
Не проще ли для каждой процедуры загрузки сделать свой обработчик?
Ещё: что будет понятнее пользователю: "Переменная x в окне 0x7fa2 имеет неправильное значение '177374', переменная id равна 456789"
или:"При загрузке лицевого счёта плательщика дата платежа более ранняя, чем дата выставленного счёта. Код плательщика 456789"?
Ещё: что будет понятнее пользователю: "Переменная x в окне 0x7fa2 имеет неправильное значение '177374', переменная id равна 456789"
или:"При загрузке лицевого счёта плательщика дата платежа более ранняя, чем дата выставленного счёта. Код плательщика 456789"?
А вот на выдачу ТАКИХ сообщений проще настроить саму БД.
И не надо мне рассказывать будто нельзя - в Access такое же делается - а чем Оракл хуже. И не только в VBA - даже в свойствах поля таблицы можно объявить "условие на значение" и "сообщение об ошибке"
P.S.
И вообще, я говорил о ситуации в общем. А не о таком "мегапримере"
Цитата: el scorpio
А вот на выдачу ТАКИХ сообщений проще настроить саму БД.
И не надо мне рассказывать будто нельзя - в Access такое же делается - а чем Оракл хуже. И не только в VBA - даже в свойствах поля таблицы можно объявить "условие на значение" и "сообщение об ошибке"
И не надо мне рассказывать будто нельзя - в Access такое же делается - а чем Оракл хуже. И не только в VBA - даже в свойствах поля таблицы можно объявить "условие на значение" и "сообщение об ошибке"
Можно, конечно. А вот как насчёт алгоритма принятия решения об откате транзакции? Я ответа не получил.
Цитата: el scorpio
P.S.
И вообще, я говорил о ситуации в общем. А не о таком "мегапримере"
И вообще, я говорил о ситуации в общем. А не о таком "мегапримере"
А "в общем" тем более нельзя что-либо однозначно советовать. Так что не надо говорить: "пользуйтесь тем-то и этим-то".