acc15
Гость
|
|
« : 28-07-2006 22:51 » |
|
class test1 { int x; public: test1() { std::cout << "test1 --> constructor" << std::endl; x = 0; } test1(const test1 &t) { std::cout << "!!! test1 --> copy constructor" << std::endl; x = 1; } ~test1() { if (x == 0) std::cout << "test1 <-- destructor" << std::endl; else std::cout << "!!! test1 <-- destructor" << std::endl; } };
int main(int argc, char* argv[]) { std::vector<test1> vec1;
std::cout << "Step 1, Capacity:" << vec1.capacity() << std::endl; vec1.resize(5); std::cout << "Step 2, Capacity:" << vec1.capacity() << std::endl; vec1.resize(2); std::cout << "Step 3, Capacity:" << vec1.capacity() << std::endl; }
Вывод: Step 1, Capacity:0 test1 --> constructor !!! test1 --> copy constructor // 0 !!! test1 --> copy constructor // 1 !!! test1 --> copy constructor // 2 !!! test1 --> copy constructor // 3 !!! test1 --> copy constructor // 4 !!! test1 --> copy constructor // очевидно временная !!! test1 <-- destructor // тут же удаляется test1 <-- destructor Step 2, Capacity:5 test1 --> constructor !!! test1 <-- destructor // вот как это сделать? !!! test1 <-- destructor // вызвалось только 3 деструктора !!! test1 <-- destructor // ведь если вызывать delete то вызвались бы все 5... test1 <-- destructor Step 3, Capacity:5 !!! test1 <-- destructor !!! test1 <-- destructor Собственно вопрос в комментариях. Насколько мне не изменяет память явно вызывать деструктор нельзя... или я ошибаюсь? Почему operator delete не вызывает деструкторы всех элементов? overloadа я не заметил...
|
|
« Последнее редактирование: 28-07-2006 23:15 от acc15 »
|
Записан
|
|
|
|
Finch
Спокойный
Администратор
Offline
Пол:
Пролетал мимо
|
|
« Ответ #1 : 29-07-2006 09:19 » |
|
acc15, Я чуть доработал твою программу #include <iostream> #include <vector>
class test1 { int x; static int count; public: test1() { count++; std::cout << "test1 --> constructor " << count<< std::endl; x = 0; } test1(const test1 &t) { count++; std::cout << "!!! test1 --> copy constructor " << count << std::endl; x = 1; } ~test1() { count--; if (x == 0) std::cout << "test1 <-- destructor " << count<< std::endl; else std::cout << "!!! test1 <-- destructor " <<count<< std::endl; } };
int test1::count=0;
int main(int argc, char* argv[]) { std::vector<test1> vec1;
std::cout << "Step 1, Capacity:" << vec1.capacity() << std::endl; vec1.resize(5); std::cout << "Step 2, Capacity:" << vec1.capacity() << std::endl; vec1.resize(2); std::cout << "Step 3, Capacity:" << vec1.capacity() << std::endl; }
Вот результаты: Step 1, Capacity:0 test1 --> constructor 1 !!! test1 --> copy constructor 2 !!! test1 --> copy constructor 3 !!! test1 --> copy constructor 4 !!! test1 --> copy constructor 5 !!! test1 --> copy constructor 6 test1 <-- destructor 5 Step 2, Capacity:5 test1 --> constructor 6 !!! test1 <-- destructor 5 !!! test1 <-- destructor 4 !!! test1 <-- destructor 3 test1 <-- destructor 2 Step 3, Capacity:5 !!! test1 <-- destructor 1 !!! test1 <-- destructor 0
Как видиш, все деструкторы были вызваны. А вообше правильно, команды vec1.resize(5); //--------------------------------- vec1.resize(2);
Сначало создается 5 элементов, потом ты сокрашаеш количество элементов до 2. Отсюда 3 дестркутора. Но скорее всего вектор не стал убивать свою внутрению структуру, отсюда и размер остался 5.
|
|
« Последнее редактирование: 29-07-2006 09:24 от Finch »
|
Записан
|
Не будите спашяго дракона. Джаффар (Коша)
|
|
|
acc15
Гость
|
|
« Ответ #2 : 29-07-2006 15:05 » |
|
Не... всё сделано грамотно... Собственно я вот о чем спрашивал... Как сделать так чтобы управлять деструктором/конструктором можно было самому (а не по new, delete) и вот оно решение: test1 *vec;
vec = (test1*) malloc(sizeof(test1)*5); // выделяем память for (int i=0;i<5;i++) ::new (&vec[i]) test1(); // вызываем конструкторы
// что-то делаем...
for (int i=0;i<5;i++) vec[i].~test1(); // вызываем деструкторы free(vec); // освобождаем память
Несмотря на то что явно вызывать деструктор нельзя... STL делает именно так... Да и new как оказалось не всегда выделяет память под объект, а может использовать уже выделенную. Такой подход позволяет резервировать память под объекты (а не только под встроенные типы) чтобы не "перевыделять" заново (что и делает vector)
|
|
|
Записан
|
|
|
|
Finch
Спокойный
Администратор
Offline
Пол:
Пролетал мимо
|
|
« Ответ #3 : 29-07-2006 18:51 » |
|
acc15, Ты что то совсем запутал всех и вся. Это ссылка на один единственный объект, но не как массив объектов. Я лично делаю так в подобной ситуации int main(int argc, char* argv[]) { test1 **vec; vec=new test1 *[5]; for(int i=0; i<5; i++) vec[i]=new test1(); std::cout << "Chto to delaem" << std::endl; for(int i=0; i<5; i++) delete vec[i]; delete [] vec; return 0; }
Вот результат работы test1 --> constructor 1 test1 --> constructor 2 test1 --> constructor 3 test1 --> constructor 4 test1 --> constructor 5 Chto to delaem test1 <-- destructor 4 test1 <-- destructor 3 test1 <-- destructor 2 test1 <-- destructor 1 test1 <-- destructor 0
|
|
|
Записан
|
Не будите спашяго дракона. Джаффар (Коша)
|
|
|
acc15
Гость
|
|
« Ответ #4 : 29-07-2006 19:01 » |
|
1. Это не ссылка, а указатель 2. Указатель можно интепретировать как указатель на 1 единственный элемент или же как указатель на 1ый элемент массива... Меня же интересовало как это делает std::vector... Если использовать твой вариант, то теряется 4 байта на каждый элемент массива... (на указатель на объект)...
|
|
« Последнее редактирование: 29-07-2006 19:11 от acc15 »
|
Записан
|
|
|
|
Finch
Спокойный
Администратор
Offline
Пол:
Пролетал мимо
|
|
« Ответ #5 : 30-07-2006 12:45 » |
|
acc15, будь добр, обьясни в чем разница между 1. Это не ссылка, а указатель
По второму пункту: Делай как тебе удобно. Если ты считаеш свой путь единственно правильным, флаг тебе в руки. Я лично вижу в твоем способе одну проблему. Это возможность расширения. У тебя время на расширение массива будет занимать значительно больше. И оно будет увеличиваться с увеличением сложности классов.
|
|
|
Записан
|
Не будите спашяго дракона. Джаффар (Коша)
|
|
|
Dimka
Деятель
Команда клуба
Offline
Пол:
|
|
« Ответ #6 : 30-07-2006 19:40 » |
|
Если использовать твой вариант, то теряется 4 байта на каждый элемент массива... (на указатель на объект)... Первый враг программы - микроптимизация (если речь идёт не об embedded софте, где каждый байт на счету). Finch, по-моему, он использует изменение размеров вектора именно для того, чтобы блочно выделять память под маленькие объекты. Поэтому твой вариант с массивом указателей не достигает его цели. С другой стороны, я проводил тесты на Athlon 1800 - выделение "поштучно" памяти для элементов char *[]. И вот что из этого получилось (под Linux): // test.cpp
#include <ctime> #include <iostream>
int main() { const int size = 1000000; char *a[size]; std::clock_t ns, nf, ds, df; ns = std::clock(); for(int i = 0; i < size; ++i) a[i] = new char; nf = ds = std::clock(); for(int i = 0; i < size; ++i) delete a[i]; df = std::clock(); float n = (float)(nf - ns) / CLOCKS_PER_SEC; float d = (float)(df - ds) / CLOCKS_PER_SEC; std::cout << size << " items: "; std::cout << "new " << n << " sec. and "; std::cout << "delete " << d << " sec." << std::endl; return 0; } $ g++ -o test ./test.cpp $ ./test 1000000 items: new 0.16 sec. and delete 0.08 sec.
Из чего следует, что отказ от блочных операций с памятью для мелких объектов в стандартном C++ тоже не такая страшная вещь, как её принято малевать. (Обращаю внимание, что компиляция проводилась с выключенной оптимизацей). Если же в векторе хранятся большие объекты, временем жизни которыми нужно управлять вручную, то для большого объекта добавочные 4 байта роли не играют. Из чего следует совет не маяться дурью и не изобретать велосипеды.
|
|
« Последнее редактирование: 30-07-2006 19:42 от dimka »
|
Записан
|
Программировать - значит понимать (К. Нюгард) Невывернутое лучше, чем вправленное (М. Аврелий) Многие готовы скорее умереть, чем подумать (Б. Рассел)
|
|
|
Finch
Спокойный
Администратор
Offline
Пол:
Пролетал мимо
|
|
« Ответ #7 : 30-07-2006 20:07 » |
|
dimka, acc15 имеет виду объекты, а не элементарные типы. Тут сушествует два пути копирования при расширении массива, Просто копирование всего блока. Но тогда происходит привязка к конкретному компилятору. Внутреняя структура объектов, как я знаю не привязана никакими стандартами. И если будут перекрестные ссылки, то этот способ сразу отпадает. Второй путь, копирование через конструкторы копирования. С технической точки зрения более безопасный. Но тогда будет теряться куча времени. И на больших массивах, задержка времени будет заметна.
|
|
« Последнее редактирование: 30-07-2006 20:19 от Finch »
|
Записан
|
Не будите спашяго дракона. Джаффар (Коша)
|
|
|
Dimka
Деятель
Команда клуба
Offline
Пол:
|
|
« Ответ #8 : 30-07-2006 20:52 » |
|
acc15 имеет виду объекты, а не элементарные типы. Пожалуйста, объекты, и с явным копированием: // test.cpp
#include <ctime> #include <iostream>
class X { private: char _x; public: X(const char x) : _x(x) {} X(const X &x) : _x(x._x) {} };
int main() { const int size = 1000000; X *a[size], *b[size]; std::clock_t ns, nf, cs, cf, ds, df; ns = std::clock(); for(int i = 0; i < size; ++i) a[i] = new X((char)i); nf = cs = std::clock(); for(int i = 0; i < size; ++i) b[i] = new X(*a[i]); cf = ds = std::clock(); for(int i = 0; i < size; ++i) { delete a[i]; delete b[i]; } df = std::clock(); float n = (float)(nf - ns) / CLOCKS_PER_SEC; float c = (float)(cf - cs) / CLOCKS_PER_SEC; float d = (float)(df - ds) / CLOCKS_PER_SEC; std::cout << size << " items: "; std::cout << "new " << n << " sec. and "; std::cout << "copy " << c << " sec. and "; std::cout << "delete " << d << " sec." << std::endl; return 0; } $ g++ -o test ./test.cpp $ ./test 1000000 items: new 0.15 sec. and copy 0.16 sec. and delete 0.13 sec.
не вижу никакой существенной разницы.
|
|
|
Записан
|
Программировать - значит понимать (К. Нюгард) Невывернутое лучше, чем вправленное (М. Аврелий) Многие готовы скорее умереть, чем подумать (Б. Рассел)
|
|
|
Антон (LogRus)
|
|
« Ответ #9 : 31-07-2006 04:23 » |
|
acc15, я тебе предлагаю несколько мыслей: 1. Читать раздел книги "Эффективное использование STL" касающийся аллокаторов памяти 2. вектор может быть инстанцирован с аллокатором отличным от аллокатора по поумолчанию 3. нормальный вектор после выводнения resize(n) где n<size() не высвобожадет ранее выделеную память(как ты сам заметил capcaity:5) и следовательно последующие push_back для вектора вызывают конструкторы уже на выделеной памяти, для вектора самая дорогая операция релоцирование данных при привышении capacity 4. для совего класса можешь перегрузить new и делет 5. подумай об использовании распределителей памяти сторонних разработчиков. например у нас используют boost::pool в местах где происходит много созданий и удалений объектов для предовращения фрагментации памяти и укорения выделения памяти. У нас ипользуют boost::pool в перегруженном new и delete 6. как тебе уже говрили это всё ранняя оптимизация, как известно не стоит оптимизировать то что и так работает да тех пор пока не станет очевидно, что тут нужно есть серьёзные проблемы с производительностью
|
|
|
Записан
|
Странно всё это....
|
|
|
Dimka
Деятель
Команда клуба
Offline
Пол:
|
|
« Ответ #10 : 31-07-2006 05:06 » |
|
Кроме того в тему будет прочитать о паттерне проектирования Flyweight
|
|
|
Записан
|
Программировать - значит понимать (К. Нюгард) Невывернутое лучше, чем вправленное (М. Аврелий) Многие готовы скорее умереть, чем подумать (Б. Рассел)
|
|
|
Dimka
Деятель
Команда клуба
Offline
Пол:
|
|
« Ответ #11 : 31-07-2006 08:14 » |
|
Те же тесты на PIV (3 ГГц, HT) под Windows: Для первого варианта: >test.exe 100000 items: new 0.015 sec. and delete 0.016 sec.
и для второго варианта: >test.exe 100000 items: new 0.015 sec. and copy 0.016 sec. and delete 0.047 sec.
|
|
|
Записан
|
Программировать - значит понимать (К. Нюгард) Невывернутое лучше, чем вправленное (М. Аврелий) Многие готовы скорее умереть, чем подумать (Б. Рассел)
|
|
|
acc15
Гость
|
|
« Ответ #12 : 31-07-2006 12:34 » |
|
Тут нет никаких микрооптимизаций... Зачем терять N*4 (ia64 - N*8) байт когда всё тем же объемом операций можно сделать то же самое и без лишних затрат? Если ты считаеш свой путь единственно правильным, флаг тебе в руки. Это не моё решение... может быть ISO? Я лично согласен что их решение едиственно правильное А ты? Почти аналог "твоего" решения - std::list (не по принципу работы а по результатам (больше размер, быстрее работает при расширении)) Но всё же меня интересовало то как реализует такие действия std::vector... acc15, будь добр, обьясни в чем разница между 1. Это не ссылка, а указатель test1 &obj; test1 *obj; Кроме того в тему будет прочитать о паттерне проектирования Flyweight А можно написать сточку на которую можно тыкнуть мышкой чтоб произошел фокус?
|
|
« Последнее редактирование: 31-07-2006 12:45 от acc15 »
|
Записан
|
|
|
|
Dimka
Деятель
Команда клуба
Offline
Пол:
|
|
« Ответ #13 : 31-07-2006 14:33 » |
|
Тут нет никаких микрооптимизаций... Зачем терять N*4 (ia64 - N* байт когда всё тем же объемом операций можно сделать то же самое и без лишних затрат? Если речь об IA64, то у систем с такими процессорами дефицита памяти, требующего экономии M=N*sizeof(void *) байт, явно не существует. Они ставятся на новые производительные рабочие станции и сервера. Здесь главный вопрос при реальной разработке программ, не "Зачем терять M байт памяти?", а "Зачем терять вагон времени, занимаясь такой микрооптимизацией, которая к тому же жёстко привязывает решение к аппаратной платформе и потребностям задачи - т.е. является одноразовым решением?" Вот второй вопрос - серьёзный. Как мне тут коллега говорит, образно выражаясь: "Это время, спущенное в унитаз!" Одно дело изучать вопрос из любопытства - здесь слова никто не скажет. Другое дело пытаться оправдываться "оптимальностью" - здесь требуется экономическое обоснование подобной деятельности. А можно написать сточку на которую можно тыкнуть мышкой чтоб произошел фокус? Не существует URL'ов вглубь PDF, хранящих сканированные книжки, запакованных в архивы и лежащих на неизвестных серверах - для фокуса надо много тыкать мышкой.
|
|
« Последнее редактирование: 06-12-2007 19:18 от Алексей1153++ »
|
Записан
|
Программировать - значит понимать (К. Нюгард) Невывернутое лучше, чем вправленное (М. Аврелий) Многие готовы скорее умереть, чем подумать (Б. Рассел)
|
|
|
LP
Помогающий
Offline
|
|
« Ответ #14 : 04-08-2006 12:06 » |
|
!!! test1 <-- destructor // вот как это сделать?
Воспользоваться кнопкой F11. Вызов сводится к erase(begin() + _Newsize, end()); // _Newsize == 2
который, кроме всего прочего, применяет макрос _DESTRUCTOR к удаляемым элементам. #define _DESTRUCTOR(ty, ptr) (ptr)->~ty()
Насколько мне не изменяет память явно вызывать деструктор нельзя
Можно.
|
|
|
Записан
|
Если эта надпись уменьшается, значит ваш монитор уносят
|
|
|
|