Форум программистов «Весельчак У»
  *
Добро пожаловать, Гость. Пожалуйста, войдите или зарегистрируйтесь.
Вам не пришло письмо с кодом активации?

  • Рекомендуем проверить настройки временной зоны в вашем профиле (страница "Внешний вид форума", пункт "Часовой пояс:").
  • У нас больше нет рассылок. Если вам приходят письма от наших бывших рассылок mail.ru и subscribe.ru, то знайте, что это не мы рассылаем.
   Начало  
Наши сайты
Помощь Поиск Календарь Почта Войти Регистрация  
 
Страниц: [1]   Вниз
  Печать  
Автор Тема: std::vector... изменение размера...  (Прочитано 16574 раз)
0 Пользователей и 6 Гостей смотрят эту тему.
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
Спокойный
Администратор

il
Offline 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
Спокойный
Администратор

il
Offline Offline
Пол: Мужской
Пролетал мимо


« Ответ #3 : 29-07-2006 18:51 » 

acc15, Ты что то совсем запутал всех и вся.
Код:
test1 *vec;
Это ссылка на один единственный объект, но не как массив объектов.
Я лично делаю так в подобной ситуации
Код:
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 » new

1. Это не ссылка, а указатель
2. Указатель можно интепретировать как указатель на 1 единственный элемент или же как указатель на 1ый элемент массива...
Меня же интересовало как это делает std::vector...
Если использовать твой вариант, то теряется 4 байта на каждый элемент массива... (на указатель на объект)...
« Последнее редактирование: 29-07-2006 19:11 от acc15 » Записан
Finch
Спокойный
Администратор

il
Offline Offline
Пол: Мужской
Пролетал мимо


« Ответ #5 : 30-07-2006 12:45 » 

acc15, будь добр, обьясни в чем разница между
Цитата
1. Это не ссылка, а указатель
По второму пункту: Делай как тебе удобно. Если ты считаеш свой путь единственно правильным, флаг тебе в руки. Я лично вижу в твоем способе одну проблему. Это возможность расширения. У тебя время на расширение массива будет занимать значительно больше. И оно будет увеличиваться с увеличением сложности классов.
Записан

Не будите спашяго дракона.
             Джаффар (Коша)
Dimka
Деятель
Команда клуба

ru
Offline Offline
Пол: Мужской

« Ответ #6 : 30-07-2006 19:40 » 

Цитата: acc15
Если использовать твой вариант, то теряется 4 байта на каждый элемент массива... (на указатель на объект)...
Первый враг программы - микроптимизация (если речь идёт не об embedded софте, где каждый байт на счету).

Finch, по-моему, он использует изменение размеров вектора именно для того, чтобы блочно выделять память под маленькие объекты. Поэтому твой вариант с массивом указателей не достигает его цели.

С другой стороны, я проводил тесты на Athlon 1800 - выделение "поштучно" памяти для элементов char *[]. И вот что из этого получилось (под Linux):
Код: (C++)
// 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
Спокойный
Администратор

il
Offline Offline
Пол: Мужской
Пролетал мимо


« Ответ #7 : 30-07-2006 20:07 » 

dimka, acc15 имеет виду объекты, а не элементарные типы. Тут сушествует два пути копирования при расширении массива, Просто копирование всего блока. Но тогда происходит привязка к конкретному компилятору. Внутреняя структура объектов, как я знаю не привязана никакими стандартами. И если будут перекрестные ссылки, то этот способ сразу отпадает.
Второй путь, копирование через конструкторы копирования. С технической точки зрения более безопасный. Но тогда будет теряться куча времени. И на больших массивах, задержка времени будет заметна.
 
« Последнее редактирование: 30-07-2006 20:19 от Finch » Записан

Не будите спашяго дракона.
             Джаффар (Коша)
Dimka
Деятель
Команда клуба

ru
Offline Offline
Пол: Мужской

« Ответ #8 : 30-07-2006 20:52 » 

Цитата: Finch
acc15 имеет виду объекты, а не элементарные типы.
Пожалуйста, объекты, и с явным копированием:
Код: (C++)
// 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)
Глобальный модератор

ru
Offline Offline
Пол: Мужской
Внимание! Люблю сахар в кубиках!


WWW
« Ответ #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
Деятель
Команда клуба

ru
Offline Offline
Пол: Мужской

« Ответ #10 : 31-07-2006 05:06 » 

Кроме того в тему будет прочитать о паттерне проектирования Flyweight
Записан

Программировать - значит понимать (К. Нюгард)
Невывернутое лучше, чем вправленное (М. Аврелий)
Многие готовы скорее умереть, чем подумать (Б. Рассел)
Dimka
Деятель
Команда клуба

ru
Offline 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
Деятель
Команда клуба

ru
Offline Offline
Пол: Мужской

« Ответ #13 : 31-07-2006 14:33 » 

Цитата: acc15
Тут нет никаких микрооптимизаций... Зачем терять N*4 (ia64 - N* байт когда всё тем же объемом операций можно сделать то же самое и без лишних затрат?
Если речь об IA64, то у систем с такими процессорами дефицита памяти, требующего экономии M=N*sizeof(void *) байт, явно не существует. Они ставятся на новые производительные рабочие станции и сервера.

Здесь главный вопрос при реальной разработке программ, не "Зачем терять M байт памяти?", а "Зачем терять вагон времени, занимаясь такой микрооптимизацией, которая к тому же жёстко привязывает решение к аппаратной платформе и потребностям задачи - т.е. является одноразовым решением?" Вот второй вопрос - серьёзный. Как мне тут коллега говорит, образно выражаясь: "Это время, спущенное в унитаз!"

Одно дело изучать вопрос из любопытства - здесь слова никто не скажет. Другое дело пытаться оправдываться "оптимальностью" - здесь требуется экономическое обоснование подобной деятельности.

Цитата: acc15
А можно написать сточку на которую можно тыкнуть мышкой чтоб произошел фокус?
Не существует URL'ов вглубь PDF, хранящих сканированные книжки, запакованных в архивы и лежащих на неизвестных серверах - для фокуса надо много тыкать мышкой.
« Последнее редактирование: 06-12-2007 19:18 от Алексей1153++ » Записан

Программировать - значит понимать (К. Нюгард)
Невывернутое лучше, чем вправленное (М. Аврелий)
Многие готовы скорее умереть, чем подумать (Б. Рассел)
LP
Помогающий

ru
Offline Offline

« Ответ #14 : 04-08-2006 12:06 » 

Цитата
!!! test1 <-- destructor // вот как это сделать?
Воспользоваться кнопкой F11. Ага
Вызов
Код:
vec1.resize(2);
сводится к
Код:
erase(begin() + _Newsize, end()); // _Newsize == 2
который, кроме всего прочего, применяет макрос _DESTRUCTOR к удаляемым элементам.
Код:
#define _DESTRUCTOR(ty, ptr)	(ptr)->~ty()
Цитата
Насколько мне не изменяет память явно вызывать деструктор нельзя
Можно. Улыбаюсь
Записан

Если эта надпись уменьшается, значит ваш монитор уносят
Страниц: [1]   Вверх
  Печать  
 

Powered by SMF 1.1.21 | SMF © 2015, Simple Machines