Здравствуйте, alexander_st, Вы писали:
_>чтобы испортить указатели ф-я не должна стрелять в стек, она должна всего лишь вызвать для них delete и лечится это передачей константных указателей
Не лечится Удалять указатели на константы можно.
Если уж позволено рефакторить программу, то нужно заменить сигнатуру g на что-нибудь такое
void g(std::vector<char>& a, std::vector<char>& b); // n не нужно, т.к. размеры переданы в составе объектов
// либоvoid g(boost::shared_array<char> a, boost::shared_array<char> b, size_t n)
void f(size_t n)
{
boost::shared_array<char> a(new char[n]), b(new char[n]);
g(a,b,n); // g может передать права собственности куда-то на сторону
// тогда по выходу из f() массивы останутся жить, как и задумано!
}
_>чем в данном контексте использование std::vector лучше, чем непосредственный указатели? К>>А вот метнуть исключение функция g() может запросто. _>но в вашем коде так же нет защиты от метания исключений
Вот как раз этим-то и лучше, что защита есть.
Вылет исключения — это частный случай выхода из блока. При этом разматывается стек, вызываются деструкторы.
А то, что я никак не перехватываю и не обрабатываю исключения (ни std::bad_alloc, ни, тем более, неизвестно что из недр g()) — ну так, от меня это и не требовалось... Тот, кто сможет, этажом выше, то и обработает. А я лишь обеспечу непротиворечивость своего участка системы.
Здравствуйте, Vamp, Вы писали:
К>>А вот метнуть исключение функция g() может запросто. V>А вот это маловероятно. Сигнатура функции предполагает, что она написана не на С++, а на С. А значит, скорее всего, никаких исключений она бросить не может.
Да щас!
Во-первых, сишная функция может вызвать сиплюсплюсную, и исключение полетит оттуда. И прекрасно перелетит через сишный слой.
Во-вторых, мало ли у кого какие олдскульные замашки остались.
В-третьих, сишная функция может метнуть структурное исключение (windows-specific). Аварийный размотчик стека готов и к таким испытаниям.
Вот если бы функция была объявлена void g(char*,char*,size_t)throw() — это другое дело... Но она же не объявлена.
V>И по большому счету, я вижу реальную проблему только в printf.
Проблема в том, что задание провалено. То ли пастор Шлаг замечтался, вдыхая воздух свободы, то ли папаша Мюллер был чересчур внимателен... В пересказе топикстартера не хватает подробностей, чтобы понять, что там на самом деле.
K>void g( const char *, const char *, size_t ); // константности в исходной задаче не было...
K>void f( size_t n )
K>{
K> сhar *a = NULL;
K> char *b = NULL;
K> if (n <= 0) // усложнение номер раз: size_t беззнаковое
K> return; // неоправданность: а может, g() должен выполняться и при нулевой длине?
K> try
K> {
K> a = new char[n]; // для справки: new char[0] возвращает ненулевой указатель на пустой массив.
K> b = new char[n];
K> g( a, b, n );
K> }
K> catch (...)
K> {
K> if (a) delete [] a; // усложнение номер два: delete[] и для нулевого указателя работает корректно
K> if (b) delete [] b; // номер два-бис
// забыл сделать throw; - сожрал исключение (может быть, оно кому-то пригодилось бы выше?)
K> }
K> if (a) delete [] a; // бабах! повторное удаление (если первое было в catch(...))
K> if (b) delete [] b; // бабах-бис
K>}
K>
K>какие авто_птр?? какой буст??? Зачем усложнять жизнь в тривиальном куске кода? KISS!
auto_ptr и boost — это как раз и есть воплощение KISS, в отличие от мосвелосипедстроймонтажа.
K>Не смущает, что нумерация массива начинается с 0, и в argc[0] == 'имя исполняемого файла'? K>Т.е. argc всегда > 0
Не смущает, что условие — if(argc>1), т.е. if(argc>=2), т.е. argv[1] валиден?
R>>> std::auto_ptr<char> a (new char[n]);
R>>> std::auto_ptr<char> b (new char[n]);
I>
S>>Тут UB. I>Почему?
Потому что delete new[]. Непарная операция.
Хотя для POD-типов и обычных менеджеров памяти (не изысканных-навороченных-специфических) это сведётся к free(malloc()).
К>Да щас! К>Во-первых, сишная функция может вызвать сиплюсплюсную, и исключение полетит оттуда. И прекрасно перелетит через сишный слой.
Это правда. Об этом не подумал.
К>Во-вторых, мало ли у кого какие олдскульные замашки остались.
Ну нет. Уж если человек не пользуется векторами, то исключений он, скорее всего, тоже не признает.
К>В-третьих, сишная функция может метнуть структурное исключение (windows-specific). Аварийный размотчик стека готов и к таким испытаниям.
А может, это под Unix все работает? И там не SEH, а SIGBUS?
К>Проблема в том, что задание провалено.
Я не думаю, что это проблема . Работать в организации, предлагающей такие задания как дистанционное — верный путь в никуда. Очевидно, что задание предполагает тщательное обсуждение с кандидатом после ЛЮБОГО его ответа.
Здравствуйте, alexander_st, Вы писали:
V>>Вообще-то, никто не мешает вызвать delete для константных указателей. _>Уп-с ... правда? _>давно последний раз пробовали?
Здравствуйте, Vamp, Вы писали:
К>>Во-вторых, мало ли у кого какие олдскульные замашки остались. V>Ну нет. Уж если человек не пользуется векторами, то исключений он, скорее всего, тоже не признает.
Незнание законов не освобождает от ответственности. Раз есть new, значит, есть и std::bad_alloc.
Вот если на уровне main() забыли написать try-catch, ловящий все долетающие до этого уровня исключения, то внезапный бросок приведёт к terminate(), и тут, конечно, уже пофиг на какую-либо стековую гигиену.
К>>В-третьих, сишная функция может метнуть структурное исключение (windows-specific). Аварийный размотчик стека готов и к таким испытаниям. V>А может, это под Unix все работает? И там не SEH, а SIGBUS?
А кстати, как в юниксе принято раскручивать стек после сигналов?
В виндах всё несложно: каждая функция, собирающаяся раскручивать, просто на входе в блок регистрирует обработчик исключения, на выходе дерегистрирует. И задача этого обработчика — вызвать деструкторы и отпустить исключение дальше, к следующему обработчику в цепочке. Компилятор берёт на себя задачу написать такой пролог-эпилог. В никсах компилятор не столь добр к пользователю, ну так это уже его личная лень
К>>Проблема в том, что задание провалено. V>Я не думаю, что это проблема . Работать в организации, предлагающей такие задания как дистанционное — верный путь в никуда. Очевидно, что задание предполагает тщательное обсуждение с кандидатом после ЛЮБОГО его ответа.
Не буду заниматься инсинуациями, не зная подробностей
К>Незнание законов не освобождает от ответственности. Раз есть new, значит, есть и std::bad_alloc.
Где? В вызывающей функции он есть. Но я вообще-то не считаю, что bad_alloc надо перехватывать, за исключением редких стратегий типа — попросим 10 гиг, не получилось — тогда 5, опять не получилось, тогда 2 Кб. Как правило, программа просит столько памяти, сколько ей надо для работы, и ничего осмысленного после bad_alloc сделать не сможет. Более того, в ситуации тотальной нехватки вполне возможно, что даже диагностические сообщения вывести не удастся, так что пусть уж честно рушится. По крайней мере, в тех же Unix будет вполне читабельная корка.
К>А кстати, как в юниксе принято раскручивать стек после сигналов?
Никак. С другой стороны, сигналы как правило посылаются только в том случае, когда сделать уже реально ничего нельзя. Ну как ты будешь раскручивать стек, если у тебя стек порушился к чертям?
К>Не буду заниматься инсинуациями, не зная подробностей
Это же самое интересное!
Здравствуйте, Smal, Вы писали:
S>Здравствуйте, igna, Вы писали:
I>>Здравствуйте, Kernan, Вы писали:
I>>
K>>> catch (...)
K>>> {
K>>> if (a) delete [] a;
K>>> if (b) delete [] b;
K>>> }
K>>> if (a) delete [] a;
K>>> if (b) delete [] b;
I>>
I>>Здесь дублируются оба delete, такой код трудно сопровождать.
S>И if-ы лишние. delete отлично удаляет нулевой указатель.
не все компиляторы одинаково полезны.
Здравствуйте, Vamp, Вы писали:
K>>какие авто_птр?? какой буст??? Зачем усложнять жизнь в тривиальном куске кода? KISS! V>А ведь ты не понял принцип КИСС.
Просвяти меня.
Здравствуйте, Кодт, Вы писали:
К>auto_ptr и boost — это как раз и есть воплощение KISS, в отличие от мосвелосипедстроймонтажа.
Каки бусты/stl в 3-х строчках кода, а? Надеюсь, ты не из тех кто начинает использовать стратегии, где достаточно православного switch?
Теоретически, в g() могли переставить указатели на что-нибудь другое, и не удалить то, на что они указывали. Лечится с помощью void g( char * const, char * const, size_t ); Хотя в реальных проектах пока ни разу не встречал такой защиты.
Мафиозная диктатура это нестабильность. Если не мафиозная диктатура, то Конституция и демократия.
A_V>Теоретически, в g() могли переставить указатели на что-нибудь другое, и не удалить то, на что они указывали.
Не могли там такого сделать. Внутри этой функции она бы переставила свои локальные копии указателей, а не исходные. Чтоб была возможность переставить исходные указатели, описание функции должно быть таким:
void g( char * &, char * &, size_t );
Здравствуйте, Kernan, Вы писали:
S>>И if-ы лишние. delete отлично удаляет нулевой указатель. K>не все компиляторы одинаково полезны.
Компилятор здесь ни при чем. delete, равно как и free нулевого указателя — это законная операция, так что смысла в проверках нет, если только тебе не платят за килограммы кода
Но разу уж мы пишем в c-стиле, то надо идти до конца
void g( const char *, const char *, size_t );
void f( size_t n )
{
сhar *a = malloc(n),
*b = malloc(n);
if (a && b) g( a, b, n );
free(a);
free(b);
}
Здравствуйте, Kernan, Вы писали:
К>>auto_ptr и boost — это как раз и есть воплощение KISS, в отличие от мосвелосипедстроймонтажа. K>Каки бусты/stl в 3-х строчках кода, а? Надеюсь, ты не из тех кто начинает использовать стратегии, где достаточно православного switch?
Привет участникам синтаксического оверхеда(tm)!
#include <vector>
void f( size_t n )
{
сhar *a = NULL; std::vector<char> a(max(n,1));
char *b = NULL; std::vector<char> b(max(n,1));
if (n <= 0) //return; //try//
{ //
a = new char[n]; //
b = new char[n]; //
g( a, b, n ); g(&a[0], &b[0], n);
} //catch (...) //
{ //if (a) delete [] a; //if (b) delete [] b; //
} //if (a) delete [] a; //if (b) delete [] b; //
}