Здравствуйте, Cyberax, Вы писали:
C>Падать. C>Как вариант — пытаться освободить кэши/временные данные и снова C>повторить вызов.
Ну если предположить, что все освобождается тогда, когда более не требуется и, как следствие, к моменту Х освобождать нечего, а откатываться тоже некуда, то получаем интересный парадокс — на критичных участках кода проверки на корректность выделения памяти теряют смысл — ибо все равно падаем.
Здравствуйте, ansi, Вы писали:
A>А на мой взгляд, код q_i все-же лучше. Идея та же, но при падении одного вызова, все остальные просто не выполняются. Ты же идешь до последнего
...
A>Согласен, но проверки в любом случае нужны. Надежность — основное требование. Потому как если что-то где-то падает, то оно тянет за собой абсолютно все.
Мой код (в продемонстрированном стиле) надежнее. И знаешь почему? Он проще воспринимается и легче отлаживается/модифицируется.
Он точно так же как и все остальные варианты выполняет освобождение памяти в случае неудачи одной из операций выделения памяти. И он медленее в случае проблем с памятью. Но медленее он на пару тактов, а вот проще сущестнвеннее.
A>Вот серьезно, есть ли проблемы с пониманием варианта с goto?
Еще какие. Это заставляет программиста решать шарады, а не просто проанализировать код и пойти дальше.
ЗЫ
Да... Нормальное решение подобных задачь — это переложить отвественность за управление ресурсами на компьютер. Самый лучший способ автоматическое управление памятью (ЖЦ и т.п.). На худой конце деструкторы С++ и т.п.
... << RSDN@Home 1.2.0 alpha rev. 628>>
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Здравствуйте, ansi, Вы писали:
A>На мой взгляд, вариант с goto более естественен и с точки зрения логики, и с точки зрения производительности. Это касается языка C. В C++ ситуация получше: статусная переменная, хелпер классы (конструктор — захват ресурса, деструктор — освобождение в случае плохого значения статусной переменной) и исключения.
Главное не считать что-то незыблемой догмой Кстати, есть статья Д. Кнута "Структурное программирование с использованием goto". PDF. Сам только просматривал С другой стороны даже в тривиальном использовании goto found вместо break что-то есть (метка содержит подсказку). Ну и следующий пример также не выглядит отталкивающим:
int i;
for(i=0; i<len; i++)
if (A[i] == x) goto found;
not_found:
i = len++;
A[i] = x;
B[i] = 0;
found:
B[i]++;
VD>Да... Нормальное решение подобных задачь — это переложить отвественность за управление ресурсами на компьютер. Самый лучший способ автоматическое управление памятью (ЖЦ и т.п.). На худой конец деструкторы С++ и т.п.
Ы-гхмы-кхы-кхм-гы-уг!!!
public class DisposeExample
{
// A base class that implements IDisposable.
// By implementing IDisposable, you are announcing that
// instances of this type allocate scarce resources.public class MyResource: IDisposable
{
// Pointer to an external unmanaged resource.private IntPtr handle;
// Other managed resource this class uses.private Component component = new Component();
// Track whether Dispose has been called.private bool disposed = false;
// The class constructor.public MyResource(IntPtr handle)
{
this.handle = handle;
}
// Implement IDisposable.
// Do not make this method virtual.
// A derived class should not be able to override this method.public void Dispose()
{
Dispose(true);
// This object will be cleaned up by the Dispose method.
// Therefore, you should call GC.SupressFinalize to
// take this object off the finalization queue
// and prevent finalization code for this object
// from executing a second time.
GC.SuppressFinalize(this);
}
// Dispose(bool disposing) executes in two distinct scenarios.
// If disposing equals true, the method has been called directly
// or indirectly by a user's code. Managed and unmanaged resources
// can be disposed.
// If disposing equals false, the method has been called by the
// runtime from inside the finalizer and you should not reference
// other objects. Only unmanaged resources can be disposed.private void Dispose(bool disposing)
{
// Check to see if Dispose has already been called.if(!this.disposed)
{
// If disposing equals true, dispose all managed
// and unmanaged resources.if(disposing)
{
// Dispose managed resources.
component.Dispose();
}
// Call the appropriate methods to clean up
// unmanaged resources here.
// If disposing is false,
// only the following code is executed.
CloseHandle(handle);
handle = IntPtr.Zero;
}
disposed = true;
}
// Use interop to call the method necessary
// to clean up the unmanaged resource.
[System.Runtime.InteropServices.DllImport("Kernel32")]
private extern static Boolean CloseHandle(IntPtr handle);
// Use C# destructor syntax for finalization code.
// This destructor will run only if the Dispose method
// does not get called.
// It gives your base class the opportunity to finalize.
// Do not provide destructors in types derived from this class.
~MyResource()
{
// Do not re-create Dispose clean-up code here.
// Calling Dispose(false) is optimal in terms of
// readability and maintainability.
Dispose(false);
}
}
public static void Main()
{
// Insert code here to create
// and use the MyResource object.
}
}
(c) MSDN
И Саттер с Рихером говорят, что тут не всё так просто и тривиально.
Правильно работающая программа — просто частный случай Undefined Behavior
Здравствуйте, _Winnie, Вы писали:
VD>>Да... Нормальное решение подобных задачь — это переложить отвественность за управление ресурсами на компьютер. Самый лучший способ автоматическое управление памятью (ЖЦ и т.п.). На худой конец деструкторы С++ и т.п.
_W>Ы-гхмы-кхы-кхм-гы-уг!!!
Ну, и нафига ты тут этот диспоз привел?
Зачем он для управления памятью?
... << RSDN@Home 1.2.0 alpha rev. 628>>
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Здравствуйте, VladD2, Вы писали:
VD>Здравствуйте, _Winnie, Вы писали:
_W>>Ы-гхмы-кхы-кхм-гы-уг!!!
VD>Ну, и нафига ты тут этот диспоз привел? VD>Зачем он для управления памятью?
Ладно...
Вообще-то сначала было сказано — VD>>>Да... Нормальное решение подобных задачь — это переложить отвественность за управление ресурсами на компьютер.
Потом, правда, добавлено — VD>>>Самый лучший способ автоматическое управление памятью (ЖЦ и т.п.). На худой конец деструкторы С++ и т.п.
Кроме памяти есть и другие ресурсы.
Ok.
Память — так память.
А shared memory между процессами — не память?
А аллоцированная видеопамять — не память (их десяток видов — вершинные буфера, вертексные, разные виды текстур, шейдеры и тд)?
А аллоцированные буферы в звуковой карте — не память?
А выделенная внутри сишной библиотеки память по malloc — не память? (Мы handle на какой-то объект, совсем не на память, но память принадлежит ему и мы освобождаем её косвенно)
А память на магнитных дисках (временные файлы) — не память?
А флеш-память (временные файлы там же) — не память?
А память на стороне базы данных на сервере для обработки моего запроса — не память?
И самое главное. А память программиста — разве не память?
Почему я должен держать в голове столько тонкостей реализации dispose-паттерна?
Правильно работающая программа — просто частный случай Undefined Behavior
Здравствуйте, Anton Batenev, Вы писали:
AB>А вот меня всегда интересовал вопрос, что делать, если вызов malloc вернул NULL для объекта, который не создать нельзя?
Такие объекты можно создавать в стеке, распределять под них память статически, либо иметь для них специальный буфер с запасом. Обычно таких объектов не много. В целом наблюдается примерно такая ситуация: рост памяти редко происходит сам по себе, а обычно связан с выполнением некоторой операции. Есть для выполнения операции памяти недостаточно, то операция прерывается и мы возвращаемся в то состояние, которое предшествовало выполнению операции.
Все, о чем ты хотел сказать, Влад, все — мимо тазика.
VD>Мой код (в продемонстрированном стиле) надежнее. И знаешь почему? Он проще воспринимается и легче отлаживается/модифицируется.
...и не работает!
Речь была именно о том, чтобы в случае облома не продолжать тупо стучать головой об стену, а аккуратно разрулить ситуацию. Аккуратно, понимаешь? Что это означает на практике? А то, что в случае, если один malloc вернул нуль, то второй раз вызвать его ты уже не имеешь права, пока что-нибудь не освободишь. Это требование так или иначе присутствовало в изначальной постановке вопроса. Ты же его успешно проигнорировал, даже не утруждая себя тем, чтобы вникнуть в суть вопроса, назвал весь код чушью, ну и вот результат — перефразируя классика, "Поздравляю Вас, гражданин семши в лужу".
Твой "надежный" код является элементарно неработоспособным. Надежно так неработоспособным. Пойми ты наконец, что кроме MS Windows и кроме памяти есть еще множество других типов ресурсов — файлы, сокеты, всяческие хэндлы. И есть язык Си, в котором все эти хэндлы приходится хэндлить вручную. Твое же сообщение эквивалентно утверждению, что "все это чушь и обрабатывать ошибки не надо вообще". В общем, оценка тебе — два с минусом.
Хорошо, представь, что у тебя есть некие функции Push и Pop. Их вызовы должны быть строго парными. При этом Push может и не сработать (вернуть ошибку). И вот в случае ошибки, единственное, что ты имеешь право сделать — это выполнить откат назад в строго обратной последовательности (иначе — BSoD). Ну и специально для тек, кто в танке. Push/Pop — это некая абстракция (так же, как и malloc/free), вместо них могут быть, например, вложенные транзакции. Да, язык — Си и другого у тебя нету.
Так что не надо начинать с оскорблений собеседника, надо начинать с того, чтобы прочитать и понять постановку задачи.
McSeem
Я жертва цепи несчастных случайностей. Как и все мы.
Здравствуйте, Anton Batenev, Вы писали:
AB>А вот меня всегда интересовал вопрос, что делать, если вызов malloc вернул NULL для объекта, который не создать нельзя? Т.е., предположим, что у нас есть два конкурирующих за память приложения, которые в процессе своей работы исчерпали всю доступную память и хотят еще ее откушать. В один из моментов, в одном из приложений, на критичном участке кода, который нельзя не выполнить, malloc вернул NULL. Что делать?
Во времена MS-DOS и написания банковского опер-дня на Borland C++, была ситуация с нехваткой памяти. И именно для объектов, которые "не создать нельзя" (там все объекты было нельзя не создать). Наилучшим решением оказалось просто выполнить exit, предварительно вызвав некий AbortTransaction без проверки ошибок (использовался Novell Netware Btrieve) и сообщив об этом на экране. Все! Никакого шаманства с постоянными проверками на NULL. Это позволило сократить объем кода чуть не в полтора раза и вписаться в доступные 400K — это был такой договор между "светлыми и темными" — админы обязаны гарантировать доступные 400K, я же не имел права превышать этот барьер. Надо ли говорить, что админы нарушали договор гораздо чаще.
Кстати, для корректного завершения приходилось резервировать пару килобайт, которые освобождались сразу же после облома.
Щас придет Влад и скажет, что я сам себе противоречу. И будет прав, потому что задачи бывают разные. В том моем случае вот такое грубоватое решение было вполне допустимо. Более того — оно было оптимальным. В других же случаях, например, где-нибудь глубоко в кишках Oracle, 100% корректная обработка ошибок — это задача первостепенной важности, а уж есть там goto или нет — не столь существенно. Я бы в таких ситуациях сделал именно с goto ибо это — наиболее очевидное, простое и надежное решение. Оператор goto сам по себе не является злом. Злом является использование goto в Фортрановском стиле, когда у нас цикл на 5 страниц и в нем goto туда-сюда, аж в глазах рябит. Это примерно так же, как молодых учат не сравнивать double на равенство, типа этого делать нельзя. На самом деле вполне можно и даже нужно — надо только понимать, когда это можно и нужно.
McSeem
Я жертва цепи несчастных случайностей. Как и все мы.
Здравствуйте, VladD2, Вы писали:
VD>Здравствуйте, c-smile, Вы писали:
CS>>Если уже на то по пошло то на C это пишется так:...
VD>А если в последсвтии нужно будет перезанять пмять под одно из полей?
Для этого в структуру еще вводится одно поле — allocatedSize.
Этого хвататет чтобы потом правильно её удалить и те поля которые
переаллоцированы т.е. указывают за пределы structPtr + allocatedSize;
McSeem2,
MS>... Это примерно так же, как молодых учат не сравнивать double на равенство, типа этого делать нельзя. На самом деле вполне можно и даже нужно — надо только понимать, когда это можно и нужно.
Извини, это ты про какие сравнения? Примерчик можно?
Здравствуйте, Lazy Cjow Rhrr, Вы писали:
MS>>... Это примерно так же, как молодых учат не сравнивать double на равенство, типа этого делать нельзя. На самом деле вполне можно и даже нужно — надо только понимать, когда это можно и нужно. LCR>Извини, это ты про какие сравнения? Примерчик можно?
Вероятно,
double i = 0.0;
if (i != 0.0)
; // always false
Т.е. имется ввиду случай равенства, когда одинаковая по величине погрешность присутствует в обоих частях сравниваемого выражения, что и дает равенство вне зависимости от величины погрешности.
_Winnie,
_W>И самое главное. _W>А память программиста — разве не память? _W>Почему я должен держать в голове столько тонкостей реализации dispose-паттерна?
Каркас этого, с позволения сказать, "паттерна":
Dispose(bool) -> // удаление в зависимости от того, откуда вызов
Dispose() содержит Dispose(true), вызывается явно
~MyReso() содержит Dispose(false), вызывается неявно
Остальные детали достраиваются автоматически (флаг там, доступ к полям и неупр. ресурсам).
Как ты понимаешь, я ещё жив, и переполнения памяти не произошло.
Здравствуйте, McSeem2, Вы писали:
MS> Это примерно так же, как молодых учат не сравнивать double на равенство, типа этого делать нельзя. На самом деле вполне можно и даже нужно — надо только понимать, когда это можно и нужно.
Здравствуйте, shadone, Вы писали:
S>хм, а когда можно и нужно сравнивать double ?
Ну, например, в алгоритме General Polygon Clipper by Alan Murta http://www.cs.man.ac.uk/~toby/alan/software/
Более того, там важна даже разница между меньше и меньше-равно. А случай примерно такой:
double a = 100;
double b = a;
if(a == b) . . .
Но понятно, что два теоретически одинаковые значения, но вычисленные по-разному (один через синус, другой через экспоненту) надо сравнивать с некой epsilon.
McSeem
Я жертва цепи несчастных случайностей. Как и все мы.
Здравствуйте, ansi, Вы писали:
A>Довольно часто приходится захватывать некоторые ресурсы по принципу "все, либо ничего". Например, необходимо выделить память под структурку, а потом еще и для ее членов, далее открыть файл и записать его хэндл в эту структуру и в конце концов вернуть указатель на эту самую структуру. Но если что-то упало, то необходимо освободить все ранее захваченное и вернуть 0.