2

О пользе блогов для блогописателей и о будущем

На прошлой неделе выяснилось, что срочно нужна копия отчета, который я даже не начинал писать. Я никогда не считал себя графоманом, поскольку перспектива излияния объемного текста за заданное время приводит меня в ужас. Вчера начал потихоньку что-то кропать ручкой на листочках :)))) Удивительное дело - до того как я начал вести блог, я из себя выдавливал текст по капле (ну, практически, по букве). Счас стало гораздо легче - похоже, ведение блога стимулирует в человеке какие-то литературные навыки (если можно так выразиться :))) ) И посему, я надеюсь, что напишу-таки этот долбаный отчет к концу недели.
Теперь немного о будущем. Очень надеюсь, что наконец-то напишу о моих мучениях с Java Reflection API, когда я пытался проанализировать внешний по отношению к моей программе класс(т.е. этот класс не являлся частью моей программы). Как оказалось, это непросто, но вполне решаемо. Причем решил я эту проблему очень "криво", совсем не так, как это нужно было делать (как было нужно, я выяснил совершенно "нечаянно" и гораздо позже).
Наверняка те, кто иногда ко мне заходит, заметили что я начал выдавать странные длинные посты, вызывающие законный вопрос - "а на хрена это надо??" (я имею в виду мои последние творения о SmartPointers и исключениях). Вот хочу еще написать и о том, зачем все это мне надо. Глядишь, и еще кто-нибудь заинтересуется.
6

Финансовый аспект фотопечати в домашних условиях (Личный опыт)

Недели на две, а то и на месяц меня лишили фотоаппарат, а это значит, что появилось время обработать и напечатать отснятый материал.
Если с обработкой все ясно, нужно наличие компьютера, графического редактора и желание заниматься обработкой фото, то с печатью дело обстоит иначе. Конечно, есть огромное число фотосалонов, где быстро и относительно недорого напечатают любое количество снимков. Но это только на первый, неискушенный взгляд.
У меня лежит компашка с фотоматериалом с нашей свадьбы, на ней записано порядка 900 кадров, около 700 из них хочу отпечатать. В среднем цена по Казани - 5 рублей за один отпечаток 10х15. Значит, я отдам около 3500 рублей. Пусть даже с 10% скидкой это составит 3150 рублей. отдать такую сумму в раз, тем паче не зная, что я получу на выходе, мне жалко. Сколько бы не уверял меня сотрудник салона, что будет применена ручная коррекция к каждому кадру, не поверю: за час-два 700 кадров ручками не обработаешь КАЧЕСТВЕННО, а именно за это время обещали выполнить мой заказ. Вспоминается по такому поводу анекдот о том, как в Китае снимают цветное кино.
Думаю, каждый, кто периодически отдает в печать цифровые фото, не раз, получив отпечатки, оказывался разочарован в результате.
Вот и я, несколько раз попав в подобную ситуацию, пришла к выводу, что фотопроцесс нужно контролировать самой от начала и до конца. И пару лет назад приобрела бюджетный струйный фотопринтер HP DeskJet 4163.
Вероятно мне возразят, мол, печатать дома на своем принтере дороже, на одних картриджах разоришься. Действительно набор картриджей для фотопечати в моем случае потянет на 2000 рублей, это около 100 отпечатков 10х15 в лучшем случае. Да еще за фото бумагу HP глянцевую 240 г/м 100 карточек 10х15 придется выложить около 800 деревянных. Суммируем расходы и делим их на количество отпечатков на выходе (в идеале 100), получаем: 2000/100+800/100 = 20 рублей (стоимость чернил) + 8 рублей (стоимость бумаги) = 28 рублей за фото. Конечно, это ОЧЕНЬ дорого!!!
Что ж будем думать дальше...

По весне приобретала я в одном фотомагазине нейтральный фотофильтр, разговорилась с продавцом и он, поинтересовавшись моим принтером, предложил мне чернила для заправки картриджей. Тогда мне было, прямо скажу, не до этого и отметив для себя, где и что можно купить, довольная покупкой покинула сие заведение. Но по мере накопления материала и осознания того, что есть кадры, которые достойны вывода на печать, мысли о том, как сделать струйную печать в домашних условиях дешевле, вернулись.
Не мудрствуя лукаво, купила предложенные мне ранее чернила фирмы Skyhorse, получила краткие инструкции по заправке и отправилась на работу ожидать окончания рабочего дня, дабы провести хирургическое вмешательство в самое сердце моего принтера. Вопросы сохранности работоспособности принтера меня не заботитили, поскольку очевидным плюсом HP является сменный картридж, а не только чернильница (убью картридж - выкину, куплю новый), вот такое вот сменное сердце. К тому же срок гарантийного обслуживания принтухи вышел.
Итак, я купила 6 100-миллилитровых пузырьков с чернилами для трех цветного картриджа, фотокартриджа и черного картриджа. Отдала за этот набор около 900 рублей. Прикинем. На одну заправку картриджа № 134 чернил каждого цвета вливаем не более 7 миллилитров, на черный картридж № 129 потратим около 10 миллилитров чернил, тоже с фотокартриджем, проведя несложные арифметические расчеты, получим 9-10 заправок. Соответственно стоимость одной заправки 90-100 рублей. Теперь стоимость заправки поделим на количество отпечатков (100 штук 10х15 с одного набора заправленных картриджей), получаем стоимость чернил необходимых для печати фотографии размера 10х15 равной примерно 1 рубль. По разным данным картридж способен пережить от 5 до нескольких десятков заправок.
Таким образом, по чернилам экономия у меня составила 20 рублей при оригинальном картридже к 1 рублю при заправленном, то есть 95%.
С бумагой стороннеого производителя экономия тоже может получиться приличной, но поскольку мне пока не до экспериментов, что-либо рассказать о стоимости и качестве бумаги не HP не могу. К тому же эксперименты тоже не бывают дешевы.
Но так как отдавать 8 рублей за лист 10х15 меня жаба душит, я попыталась сэкономить, не отказываясь от фирменной фотобумаги. Поскольку передо мной стоит задача распечатать свадебные фото, то выбор мой пал на глянцевую бумагу 240-250 г/м2. В ближайшем магазине "Эльдорадо" я купила пачку глянцевой бумаги НР (А4, 50 листов, 250г/м2) за 779 рублей. С листа формата А4 (297х210 мм, а реально 296х209мм) получаем 4 карточки 148х105мм, это практически 10х15 см, значит, с 50 листов А4 получим 200 листов 10х15. А это в свою очередь получается меньше 4 рублей за 1 фото.
По бумаге экономия у меня составила около 50%.
В итоге отпечаток 10х15 в домашних условиях у меня вышел стоимостью: 1 рубль (цена необходимых чернил) + 3 рубля 90 копеек (цена 1 листа глянцевой фотобумаги НР 250 г/м2) = 4 рубля 90 копеек.
Что ж меня этот вариант вполне устраивает. Это примерно на 80% дешевле, чем при печати с оригинальными картриджами, и это не дороже, чем в салоне (в итоге 3450 рублей), зато ручная коррекция каждого снимка, полный контроль процесса печати и уверенность в результате. Ну, не без риска, конечно,но, по мне, так игра того стоит. К тому же остается множество возможностей еще больше удешевить печать фото в домашних условиях: это, во-первых, эксперименты с бумагой, а во-вторых, у меня появились мысли в дальнейшем попробвать СНПЧ, вот только жаль для НР их почти не бывает.
2

Метки для координатных осей

При рисовании графиков нередко встает такая задача: мы хотим, что бы на координатных осях графика у нас в нужном месте отображались метки, причем метки должны иметь "круглые" значения. Например, по оси X у нас минимальное значение 0.2259, а максимальное 0.2372, тогда метки должны стоять в точках 0.2260, 0.2280, 0.2300, 0.2320, 0.2340 и 0.2360 (шаг 0.002). Я уже давно использую одну функцию, решающую эту задачу. На вход она получает интервал (x1, x2) и приблизительное число меток, а возвращает массив, содержащий значения меток. Реализация следующая:

#include <stdio.h>
#include <vector>
using namespace std;

typedef vector<double> TDoubleArray;
//Вычисление значений, на которых должны проставляться метки на координатных осях.
static TDoubleArray & CalcGridLabels(double xmin, //минимальное значение.
double xmax, //максимальное значение.
int N) //количество меток в указанном интервале +/-2
{
#define MANT_NUM 5
double mant[MANT_NUM] = {0.1, 0.2, 0.25, 0.5, 1.0},
dx = xmax-xmin;
int i;
//Подбираем значение мантиссы, близким к величине dx:
while(dx < mant[0])
for(i=0; i < MANT_NUM; i++)
mant[i] /= 10.0;
while(dx > mant[MANT_NUM-1])
for(i=0; i < MANT_NUM; i++)
mant[i] *= 10.0;
//Если количество меток > 10:
while(1)
{//Первый проход:
for(i=MANT_NUM-1; i >=0 ; i--)
if(dx/mant[i] >= N )
break;
if(i >= 0)
break;//все хорошо, N меток будет.
//Умножаем метки на 2:
for(i=0; i < MANT_NUM; i++)
mant[i] /= 2;
//Второй проход:
for(i=MANT_NUM-1; i >=0 ; i--)
if(dx/mant[i] >= N )
break;
if(i >= 0)
break;//Все хорошо, N меток будет.
//Умножаем метки на 5:
for(i=0; i < MANT_NUM; i++)
mant[i] /= 5;
}
//2)Ищем наименьшую разницу между требуемым количеством меток и
//тем количеством меток, которое можно получить:
int min_diff = (int)(dx/mant[0]), md_ind = 0;
for(i = 0; i < MANT_NUM; i++)
{ int diff = abs((int)(dx*1.00000001/mant[i])-N);
if(diff < min_diff)
{ min_diff = diff;
md_ind = i;
}
}
//Рассчитываем значения меток:
double dm = mant[md_ind];
double start = dm*(int)(xmin*1.00000001/dm);
if(start < xmin)
start += dm;
static TDoubleArray values;
values.resize(1);
values[0] = start;
values.reserve(N+1);
while(start+dm < xmax)
{ start += dm;
values.push_back(start);
}
return values;
#undef MANT_NUM
}

4

SmartPointers для C++ в стиле Java

Не так давно я перешел с плюсов на Java. Согласен с автором поста в том, что чисто по-человечески писать на Java приятней, чем на C++. Страхов меньше, язык проще, в стандартной библиотеке есть почти все, что нужно. Одно мне не нравится - все же для любой проги на Java необходимо окружение, а 17 МБ (для JRE 5) не каждый готов скачать ради того, чтобы запустить простенькую програмку. Хочется мне иметь в C++ безопасность и удобство Java.
В процессе мозголомства и раздумий на тему - как же в принципе реализуется сборщик мусора( он же garbage collector - GC) и лазанью по инету пришел к одному результату... А именно: в C++ можно создать SmartPointer, пользоваться которым можно абсолютно так же, как в Java пользуются ссылками на объекты. И поведение его будет таким же, как поведение ссылок в Java. При этом GC как таковой даже и не нужен - его функции будет выполнять сам SmartPointer.

Как ведут себя ссылки в Java? Через них можно обращаться к полям/методам класса, присваивать ссылкам другие ссылки совместимых типов, выполнять проверку на равенство, делать ссылку "инвалидной" (т.е. присвоить ей значение null). По выходе ссылки из области видимости память, связанная с этой сылкой, может быть освобождена сборщиком мусора, при условии что эта ссылка не была передана куда-то еще. Вот, пожалуй и все.
Итак, если в нашей программе будут только такие SmartPointer-ы и не будет других видов указателей, про ручное освобождение памяти и утечку памяти можно будет забыть. Конечно, можно умудрится сделать циклические ссылки (и на Java это сделать проще, чем на C++) В таких случаях, конечно, придется освобождать память вручную, но не через delete, а просто присваивая ссылке значение NULL, что гораздо безопаснее (можно присваивать одной и той же ссылке NULL сколько влезет, и не будет ничего страшного).
Что бы быть подобием java-ссылки, наш SmartPointer должен уметь:
-выполнять операцию присваивания таких же SmartPointer-ов или SmartPointer-ов совместимых типов,
-выполнять операцию присваивания значения NULL,
-выполнять операцию сравнения.
-знать, сколько его копий существует на данный момент.
При всем этом не должно происходить утечек памяти. То есть если один указатель перезаписывается другим, то память на которую указывал "затертый" указатель, должна быть освобождена.
Объявление этого SmartPointer-a я изобразил так:
template<class T>
class AkSmartPointer{
private:
int *ptrCount;
T* ptrObject;
//Удаление 1 экземпляра ССЫЛКИ:
void removeRef();
public:
//Конструкторы:
//"По умолчанию":
AkSmartPointer();
//При создании экземпляра объекта:
AkSmartPointer(T *object);
//Копирование:
AkSmartPointer(const AkSmartPointer<T> &sp);
//Оператор присваивания:
AkSmartPointer<T> & operator=(const AkSmartPointer<T> &s;sp);
//Оператор проверки на равенство:
bool operator == (const AkSmartPointer<T> &sp) const;
//Удаление 1 экземпляра ССЫЛКИ:
~AkSmartPointer();
//Поучение ссылки на объект:
T * operator ->();
};
Вроде ничего не забыл :) Получение указателя необходимо для доступа к интерфейсу указуемого объекта. Реализацию такого SmartPointer-а я сделал вот такой:

class AkSmartPointerException{
public:
AkSmartPointerException( const char *msg, const char *fname, int line){
printf("Error: %s at %s:%i\n", msg, fname, line);
}
};
template<class T>
class AkSmartPointer{
private:
int *ptrCount;
T* ptrObject;
public:
//Конструкторы:
//"По умолчанию" просто обнуляем все указатели:
AkSmartPointer(){
ptrCount = NULL;
ptrObject = NULL;
}
//При создании экземпляра объекта:
AkSmartPointer(T *object){
ptrObject = object;
if(ptrObject != NULL){
ptrCount = new int(1);
printf("New reference 0x%X created.\n", (unsigned)ptrObject);
}else
ptrCount = NULL;
}
//Копирование:
AkSmartPointer(const AkSmartPointer<T> &sp){
ptrCount = sp.ptrCount;
ptrObject = sp.ptrObject;
if(ptrCount != NULL){
(*ptrCount)++;
printf("New copy of reference 0x%X created, %i references total\n",
(unsigned)ptrObject, *ptrCount);
}
}
//Оператор присваивания:
AkSmartPointer<T> & operator=(const AkSmartPointer<T> &sp){
//Если данный объект уже равен присваемому:
if(sp.ptrCount == ptrCount && sp.ptrObject == ptrObject)
return *this;// ничего не делаем, просто возвращаем текущее значение.
//если выполняется только одно условие из двух - то происходит нечто странное,
//и мы выкидываем исключение:
else if(sp.ptrCount == ptrCount || sp.ptrObject == ptrObject)
throw AkSmartPointerException("ptrCount or ptrObject is equal in different smart pointers", __FILE__, __LINE__);
removeRef();//удаляем у текущего объекта ссылку
ptrCount = sp.ptrCount;
if(ptrCount != NULL)
(*ptrCount)++;
ptrObject = sp.ptrObject;
if(ptrObject != NULL && ptrCount != NULL)
printf("New copy of reference 0x%X created, %i references total\n",
(unsigned)ptrObject, *ptrCount);
return *this;
}
//Оператор проверки на равенство:
bool operator == (const AkSmartPointer<T> &sp) const{ return ptrObject == sp.ptrObject; }
//Удаление 1 экземпляра ССЫЛКИ:
~AkSmartPointer(){removeRef(); }
//Получение ссылки на объект:
T * operator ->(){
if(ptrCount != NULL)
return ptrObject;
throw AkSmartPointerException("Null pointer dereferencing", __FILE__, __LINE__);
}
//Удаление 1 экземпляра ССЫЛКИ:
private:
void removeRef(){
if(ptrCount != NULL){
(*ptrCount)--; //Если ссылок не осталось - зануляем все остальное:
if(*ptrCount <= 0){
unsigned x = (unsigned)ptrObject;
delete ptrCount;
if(ptrObject != NULL)
delete ptrObject;
printf("All references 0x%X deleted.\n", x);
}else
printf("One reference 0x%X removed, %i remaining\n",
(unsigned)ptrObject, *ptrCount);
ptrCount = NULL;
ptrObject = NULL;
}
else if(ptrObject != NULL)//если ptrCount == NULL, но ptrObject != NULL
throw AkSmartPointerException("ptrCount == NULL, but ptrObject != NULL", __FILE__, __LINE__);
else
printf("Do nothing - ptrCount and ptrObject is null.\n");
}
};
Наверное, все понятно и так, но немножко комментариев напишу. Каждое действие по созданию или удалению ссылок на объект я сопровождаю выводом на экран, дабы было видно, какие функции-члены SmartPointer-а вызываются при манипуляциях с ним, отсюда куча printf().

В операторе присваивания сначала проверяем, не присваиваем ли мы нашей ссылке ее же значение. Если да - число ссылок не изменилось и ничего делать не надо. Если нет (не выполняются оба условия sp.ptrCount == ptrCount и sp.ptrObject == ptrObject, либо лишь одно из них), то проверяем: оба условия не выполняются или только одно из них. Если только одно условие не выполняется-например, sp.ptrCount == ptrCount, но sp.ptrObject != ptrObject, - это значит, что ссылки на разные объекты имеют общий счетчик, а это недопустимо. Таким образом, в случае выполнения только одного условия из двух, сообщаем об ошибке. Если оба условия не выполняются, то все ОК - одна ссылка перезаписывается другой. В результате присваивания одна из ссылок должна быть удалена (та, что стояла в левой части выражения присваивания) и появится еще одна новая ссылка (та, что стояла в правой части этого выражения). Для удаления ссылки вызываем функцию removeRef у объекта-владельца этой ссылки.

Все остальное по-моему достаточно очевидно (если честно, упарился описывать :) ). Эту реализацию SmartPointer я разместил в файле refCount.hpp.

Уфф, добрались до самого интересного (говорят, русским программистам всегда не терпится запустить программу и увидеть ее в работе. Это правда :) ). Написал я тест и запустил, скрестив пальцы под столом. Сначала хотел его целиком тут привести, но потом передумал. Тест просто показал, что такой SmartPointer вроде бы работает именно так, как я ожидал. Приведу здесь только ту часть теста, которая показала, что происходит при создании объектов и как работает vector.
#include <vector>
#include "refCount.hpp"
struct TestObject{
double x, y;
TestObject(double _x, double _y): x(_x), y(_y){}
};
using namespace std;
typedef AkSmartPointer<TestObject> TestObjectSmartPtr;
int main(int n, char **args){
printf("Declare vector<TestObjectSmartPtr>:\n");
vector<TestObjectSmartPtr> refvect;
{ //Начало блока.
printf("Declare objects a, b, c:\n");
AkSmartPointer<TestObject> sp_a, sp_b, sp_c;
printf("Create object a:\n");
sp_a = new TestObject(1.0, 1.0);//всего 1 ссылка на a
.....
} //Конец блока.
Этот участок кода дал мне такую вот распечатку:
Declare vector:
Declare objects a, b, c:
Create object a:
New reference 0x3F3C90 created.
Do nothing - ptrCount and ptrObject is null.
New copy of reference 0x3F3C90 created, 2 references total
One reference 0x3F3C90 removed, 1 remaining
Хорошо видно, какую последовательность вызовов порождает выражение

sp_a = new TestObject(1.0, 1.0);//всего 1 ссылка на a
Расписывать как все происходит, не буду, дабы не утомлять читателей. :)

Теперь - о том,что касается работы vector. Свой тест я откомпилил в MinGW версии 3.4.2, в Borland C++ builder 5 и в MSVC 6.0 c подключенным компилятором IA-32 v5.0. Что касается создания объектов, и в том и в другом случае распечатки были почти идентичны (за исключением значений адресов объектов). Но что касается vector - разница заметна. В моем тесте есть такой кусок:

printf("Addition object a:\n");
refvect.push_back(sp_a);//2 ссылки
printf("Addition object b:\n");
refvect.push_back(sp_b);//2 ссылки
printf("Addition object c:\n");
refvect.push_back(sp_c);//2 ссылки
Откомпилированный под MinGW, этот кусок вывел на экран следующее:
Addition object a:
New copy of reference 0x3F3C90 created, 2 references total
Addition object b:
New copy of reference 0x3F3C90 created, 3 references total
New copy of reference 0x3F23B8 created, 2 references total
One reference 0x3F3C90 removed, 2 remaining
Addition object c:
New copy of reference 0x3F3C90 created, 3 references total
New copy of reference 0x3F23B8 created, 3 references total
New copy of reference 0x3F23E8 created, 2 references total
One reference 0x3F3C90 removed, 2 remaining
One reference 0x3F23B8 removed, 2 remaining

То есть каждый раз при добавлении 1 элемента в конец вектора, под весь вектор выделялся новый участок памяти, туда копировались элементы из "старого" вектора, а старый участок памяти удалялся(!). То же самое в версии STL MSVC 6.0 (но это ладно, она очень древняя). Похоже, в Borland-е все сделано куда разумнее. Откомпилированный в Borland-е тот же кусок программы вывел на экран гораздо меньше сообщений:
Addition object a:
New copy of reference 0x65415C created, 2 references total
Addition object b:
New copy of reference 0x654180 created, 2 references total
Addition object c:
New copy of reference 0x6541A4 created, 2 references total

Как видно, реализация vector от Borland все же лучше аналогичной в MinGW. Похоже, у Borlan-да при создании вектора сразу резервируется блок под несколько элементов.
Ну и под конец - один из способов, как приведенную реализацию SmartPointer-а заставить работать неправильно:
AkSmartPointer a = new Object(...);
Object *b = a.operator->();
delete b;//пипец!

Но имхо, это будет уже маразм ради маразма :) Если знаете, как с таким SmartPointer-ом организовать утечку памяти - плиз, кидайте в комменты примерчик, но только без описанного маразма или ему подобных (желательно программу целиком). Циклические ссылки приветствуются, но желательно, что бы Ваш пример был компилируемым и запускаемым :). К моему стыду, я до сих пор не знаю, как циклические ссылки можно организовать в C++ (мне один раз это было надо, но я их реализовал так криво, что тошно вспоминать).
P.S. Мне сложно было реализовать ситуацию, когда 2 объекта разных типов должны были ссылаться друг на друга.
5

Воспоминания о лете...

Минувшим летом мы с мужем отдыхали на море. 10 дней в Лазаревском: море, горы, солнце и отсутвие компьютера... Вот это отдых для жены программиста, ну, и разумеется, для самого программиста:)

Сейчас, когда на улице всего +5° С, а батареи греют лишь в одной комнате из трех, воспоминания о летней поездке согревают, если не ноги, то хотя бы душу:))

1

Мой словарный запас

Воть, тест прошел. На проверку словарного запаса :)
Итак, ваш запас: Ваш словарный запас просто великолепен, вероятно вы профессионально владеете языком и решаете кроссворды с легкостью!
Пройти тест
1

Можно ли сделать C++ безопаснее?

Конечно, я наверное, таким вопросом слишком на многое замахнулся. Пусть не "безопасным", но хотя бы чуть менее "опасным" в том, что касается работы с динамической памятью. Я считаю C++ "опасным" языком в этом отношении и думаю, что многие со мной согласятся. С какими основными проблемами встречается человек, пишущий на C++? Думаю, не сильно ошибусь, если скажу, что это утечки памяти и выход за границы диапазонов динамических массивов. С первой проблемой дело обстоит очень сложно, зато незначительные утечки памяти редко вызывают крах программы, если она не работает круглосуточно (например, некая игра для PC, текстовый процессор или какой-нибудь Computer-Aided Design). А вот выход за границы порой валит прогу в самом интересном месте. Даже если вы используете только safe arrays со всяческими проверками и генерацией исключений - все равно бывает, что не один час уйдет на поиск места, где произошла ошибка.
Признатся, я перешел с C++ на Java во многом именно из-за проблем с памятью. Но, кроме сборщика мусора, мне в Java импонирует еще одна вещь: в случае возникновения исключения во время исполнения программы, Java распечатывает стек вызовов! Устраняя глюки (те, что вызывают исключения) в программах на Java, я даже отладчик не использовал, и долгое время (первый год) не пользовался им вообще. После C++ это было что-то!!! Сообщения об ошибках в C++ которые выплевывались при генерации исключений и распечатка содержимого стека (одни цифири) не давали непосвященному вроде меня почти никакой информации о том, где произошла ошибка. Особенно, если использовались шаблоны, а ошибка происходила где-нибудь в debug/vector. В этой статейке я расскажу о своих попытках распечатки стека вызовов в C++ (в случае генерации исключения), в таком же виде, в каком я привык это видеть в Java. Специально для этой этого я написал маленькую программку (надеюсь, моя статья не такая паршивая, как эта программа :)))).

#include <stdio.h>
#include <vector>
void fillDoubleVector(vector<double> &vect){
vect.resize(20);
unsigned size = vect.size();
for(int i=size-1; i >= 0; i++)//Ошибка
vect[i] = i*(double)size - size/(double)(i+1);
}
//-----------------------------------------------------
void fillLongVector(vector<long> &vect){
vector<double> dvect;
fillDoubleVector(dvect);
unsigned size = dvect.size();
vect.resize(size);
for(int i=size-1; i >= 0; i++)//Ошибка
vect[i] = i*size - size/(i+1);
}
//-----------------------------------------------------
int main(int n, const char **args){
vector<long> lvect;
fillLongVector(lvect);
for(int i=0; i < lvect.size(); i++)
printf("lvect[%i] = %i\n", i, lvect[i]);
return 0;
}

Программу сохранил в файле test.cpp откомпилил в MinGW:

g++ -o test.exe -D _GLIBCXX_DEBUG -g3 test.cpp

Определил _GLIBCXX_DEBUG , что бы в программе использовалась отладочная версия STL (которая хотя бы сообщает о таких ошибках, как выход за пределы диапазона). Получил вот такое сообщение об ошибке:

C:/DEV-CPP/BIN/../lib/gcc/mingw32/3.4.2/../../../../include/c++/3.4.2/
debug/vector:192:
error: attempt to subscript container with out-of-bounds index 20,
but container only holds 20 elements.

Objects involved in the operation:
sequence "this" @ 0x0074FDC0 {
type = N15__gnu_debug_def6vectorIdSaIdEEE;
}
abnormal program termination
Замечательное и очень информативное сообщение об ошибке! Только местоположение ошибки указано весьма туманно. Было бы еще змечательно, что бы после этого еще и стек вызовов распечатывался. Переделал я эту программулину, вставив в начало и конец каждой функции блоки try-catch:

#include <stdio.h>
#include <exception>
using namespace std;
#include <vector>
//Класс-исключение:
class MyException : public exception
{
public:
MyException(const char *msg, int line)
{
printf("Error in file %s : %i\n", msg, line);
}
};
//--------------------------------------------------
void fillDoubleVector(vector<double> &vect){
try{
vect.resize(20);
unsigned size = vect.size();
for(int i=size-1; i >= 0; i++)//Ошибка
vect[i] = i*(double)size - size/(double)(i+1);
}catch(exception ex){
throw MyException(__FILE__, __LINE__);
}
}
//-----------------------------------------------------
void fillLongVector(vector<long> &vect){
try{
vector<double> dvect;
fillDoubleVector(dvect);
unsigned size = dvect.size();
vect.resize(size);
for(int i=size-1; i >= 0; i++)//Ошибка
vect[i] = i*size - size/(i+1);
}catch(exception ex){
throw MyException( __FILE__, __LINE__);
}
}
int main(int n, const char **args){
try{
vector<long> lvect;
fillLongVector(lvect);
for(int i=0; i < lvect.size(); i++)
printf("lvect[%i] = %i\n", i, lvect.at(i));
}catch(exception ex){
printf("Error in file %s : %i\n", __FILE__, __LINE__);
}
return 0;
}

Откомпилил, запустил - млин, результат нисколько не изменился!!! Чертовщина, блин! Похоже, что в vector при выходе за пределы массива не выбрасывается исключение, во всяком случае в реализации MinGW. Полез в заголовочные файлы (исходников всей библиотеки, к сожалению, не было) - так и есть, не выбрасывается даже в отладочной версии.
Потом уже прочел в описании STL, что оператор индексирования [] исключение не выбрасывает и проверок никаких не делает, но зато есть функция-член at(), которая работает точно так же как оператор [], но проверки делает и исключение выбрасывает.
Отладочную версию файла vector я несколько раскурочил (перекрестившись и сохранив его копию), вставив в тело функции operator [] вызов функции at() и добился-таки желаемого:

Error in file test2.cpp : 23
Error in file test2.cpp : 36
Error in file test2.cpp : 47

Жаль, что старое информативное сообщение об ошибке уже не выводится. Но все можно исправить, для этого надо немного поковырять исходники библиотек(насколько я понимаю - вставить вместо вызова функции abort() генерацию исключения). Найти бы тока время, что бы этим заняться, и трафик, что бы их (stdlibc++ sources) скачать :)
Еще в дополнение замечу, что кроме выходов за границы есть и другие ошибки, которые можно было бы так отлавливать - главное, что бы при их возникновении выкидывалось исключение. Конечно, ручками вставлять кучу блоков try/catch вряд ли кто захочет, да и вряд ли это нужно делать ручками. Но вот если написать такую приблуду, которая будет сама это в исх. код вставлять, потом все это компилить, а потом из исходного кода удалять... Напишите для меня такую, кто-нибудь, заплатить не обещаю, но обещаю 100 раз скачать :)