Форум программистов, компьютерный форум CyberForum.ru

О перевыделении памяти для массива объектов - C++

Восстановить пароль Регистрация
 
 
Рейтинг: Рейтинг темы: голосов - 12, средняя оценка - 4.92
CatsCanFly
0 / 0 / 0
Регистрация: 14.04.2013
Сообщений: 21
28.07.2013, 17:04     О перевыделении памяти для массива объектов #1
Мне необходимо сделать динамический массив объектов класса, который будет расширяться при необходимости.
Понятно, что для такого существует выделение через malloc и перевыделение через realloc, но malloc не годится для объектов, тк не вызывает конструктор. Посоветуйте максимально эффективный с точки зрения производительности вариант пожалуйста (думал над выделением новой памяти через new[], копирования из существующего массива объектов в новый и удаление исходного, но мне вариант это показался некрасивым). Готовые решения типа std::vector использовать нельзя.
После регистрации реклама в сообщениях будет скрыта и будут доступны все возможности форума.
alsav22
5282 / 4801 / 442
Регистрация: 04.06.2011
Сообщений: 13,587
28.07.2013, 17:07     О перевыделении памяти для массива объектов #2
Цитата Сообщение от CatsCanFly Посмотреть сообщение
думал над выделением новой памяти через new[], копирования из существующего массива объектов в новый и удаление исходного, но мне вариант это показался некрасивым
realloc, наверное, то же самое делает.
Bend3r
 Аватар для Bend3r
142 / 129 / 17
Регистрация: 29.07.2012
Сообщений: 681
28.07.2013, 17:11     О перевыделении памяти для массива объектов #3
Мне кажется не получится, т.к при новом выделении сотрутся старые данные. Как вариант заранее обьявлять большое кол-во чтоб хватило
alsav22
28.07.2013, 17:19
  #4

Не по теме:

Цитата Сообщение от Bend3r Посмотреть сообщение
Мне кажется не получится, т.к при новом выделении сотрутся старые данные.
Это о чём?

Bend3r
 Аватар для Bend3r
142 / 129 / 17
Регистрация: 29.07.2012
Сообщений: 681
28.07.2013, 17:32     О перевыделении памяти для массива объектов #5
Цитата Сообщение от alsav22 Посмотреть сообщение

Не по теме:


Это о чём?

Ну вот пример.
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
int *mas = new int[10];
for(int i = 0; i < 10; i++)
{
    mas[i] = i;
}
for(int i = 0; i < 10; i++)
{
    cout << mas[i] << " ";
}
 
mas = new int[20]; //пытаюсь увеличить размерность
 
for(int i = 0; i < 10; i++)
{
    cout << mas[i] << " ";
}
В этом же массиве необходимо увеличить размерность. Я увеличиваю, но старые данные стираются. Я про это имел ввиду. Хотя может возможно как-то можно увеличить размерность массива, без удаления старых данных.
Croessmah
Модератор
Эксперт С++
 Аватар для Croessmah
11811 / 6790 / 767
Регистрация: 27.09.2012
Сообщений: 16,840
Записей в блоге: 2
Завершенные тесты: 1
28.07.2013, 18:02     О перевыделении памяти для массива объектов #6
Цитата Сообщение от Bend3r Посмотреть сообщение
Я увеличиваю, но старые данные стираются.
Есть такая вещь, как копирование. Выделяем новую память, копируем данные из старой, освобождаем старую память.
Bend3r
 Аватар для Bend3r
142 / 129 / 17
Регистрация: 29.07.2012
Сообщений: 681
28.07.2013, 18:05     О перевыделении памяти для массива объектов #7
Цитата Сообщение от Croessmah Посмотреть сообщение
Есть такая вещь, как копирование. Выделяем новую память, копируем данные из старой, освобождаем старую память.
Пример покажите пожалуйста.
alsav22
5282 / 4801 / 442
Регистрация: 04.06.2011
Сообщений: 13,587
28.07.2013, 18:17     О перевыделении памяти для массива объектов #8
Цитата Сообщение от Bend3r Посмотреть сообщение
Пример покажите пожалуйста.
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
int *mas = new int[10];
for(int i = 0; i < 10; i++)
{
    mas[i] = i;
}
 
for(int i = 0; i < 10; i++)
{
    cout << mas[i] << " ";
}
cout << endl; 
 
int *mas2 = new int[20]; 
 
for(int i = 0; i < 10; i++)
{
    mas2[i] = mas[i];
}
 
delete [] mas;
mas = mas2;
 
for (int i = 10; i < 20; ++i)
{
    mas[i] = i;
}
 
for(int i = 0; i < 20; i++)
{
    cout << mas[i] << " ";
}
 
delete [] mas;
Croessmah
Модератор
Эксперт С++
 Аватар для Croessmah
11811 / 6790 / 767
Регистрация: 27.09.2012
Сообщений: 16,840
Записей в блоге: 2
Завершенные тесты: 1
28.07.2013, 18:32     О перевыделении памяти для массива объектов #9
Цитата Сообщение от Bend3r Посмотреть сообщение
Пример покажите пожалуйста.
http://ideone.com/oUNnc6
Bend3r
 Аватар для Bend3r
142 / 129 / 17
Регистрация: 29.07.2012
Сообщений: 681
28.07.2013, 18:43     О перевыделении памяти для массива объектов #10
Цитата Сообщение от alsav22 Посмотреть сообщение
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
int *mas = new int[10];
for(int i = 0; i < 10; i++)
{
    mas[i] = i;
}
 
for(int i = 0; i < 10; i++)
{
    cout << mas[i] << " ";
}
cout << endl; 
 
int *mas2 = new int[20]; 
 
for(int i = 0; i < 10; i++)
{
    mas2[i] = mas[i];
}
 
delete [] mas;
mas = mas2;
 
for (int i = 10; i < 20; ++i)
{
    mas[i] = i;
}
 
for(int i = 0; i < 20; i++)
{
    cout << mas[i] << " ";
}
 
delete [] mas;
Вы тут используете 2 массива. А я имел ввиду про 1 массив.
alsav22
5282 / 4801 / 442
Регистрация: 04.06.2011
Сообщений: 13,587
28.07.2013, 18:55     О перевыделении памяти для массива объектов #11
Цитата Сообщение от Bend3r Посмотреть сообщение
Вы тут используете 2 массива. А я имел ввиду про 1 массив.
А я имел ввиду это:
Цитата Сообщение от Croessmah Посмотреть сообщение
Есть такая вещь, как копирование. Выделяем новую память, копируем данные из старой, освобождаем старую память.
Цитата Сообщение от Bend3r Посмотреть сообщение
Пример покажите пожалуйста.
Про один массив ещё что-то непонятно?
Bend3r
 Аватар для Bend3r
142 / 129 / 17
Регистрация: 29.07.2012
Сообщений: 681
28.07.2013, 18:57     О перевыделении памяти для массива объектов #12
Все понятно.
Olivеr
 Аватар для Olivеr
411 / 407 / 13
Регистрация: 06.10.2011
Сообщений: 830
28.07.2013, 19:24     О перевыделении памяти для массива объектов #13
http://ideone.com/WQ63ec
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
#include <iostream>
#include <memory>
#include <vector>
#include <string>
#include <algorithm>
#include <iterator>
 
static int default_constructed = 0;
static int constructed = 0;
static int copied = 0;
static int moved = 0;
static int destroyed = 0;
 
struct Foo
{
    std::string attr1;
    std::vector<int> attr2;
 
    Foo() : attr1(), attr2() { ++default_constructed; }
 
    Foo(const std::string &str,
        const std::vector<int> &v) :
            attr1(str), attr2(v) { ++constructed; }
 
    Foo(Foo &&x) :
            attr1( std::move(x.attr1) ),
            attr2( std::move(x.attr2) ) { ++moved; }
 
    Foo(const Foo &x) : Foo(x.attr1, x.attr2) { ++copied; }
 
    ~Foo() { ++destroyed; }
};
 
int main()
{
    std::allocator<Foo> alloc;
 
    // выделим место под 100 объектов Foo
    Foo *array100 = alloc.allocate(100);
 
    // запишем кол-во
    size_t size = 100;
 
    // и сконструируем столько же
    std::uninitialized_fill( array100,
                             array100 + size,
                             Foo("string", {1000, 42}) ); // constructed = 1 (ref)
                                                          // copy constructed = 100
                                                          // destroyed = 1
 
    // увеличим место - 1000
    Foo *array1000 = alloc.allocate(1000);
    size_t new_size = 1000;
 
    // объекты array100 нужно переместить в новое место
    // если тип обладает перемещающим конструктором, то будет использован именно он
    std::uninitialized_copy( std::make_move_iterator(array100),
                             std::make_move_iterator(array100 + size),
                             array1000 ); // move constructed = 100
 
    // инициализируем остальные 900 элементов
    std::uninitialized_fill( array1000 + size,
                             array1000 + new_size,
                             Foo("string v2", {1000, 87}) ); // constructed = 1 (ref)
                                                             // copy constructed = 900
                                                             // destroyed = 1
 
    // объекты array100 больше не валидны
    std::for_each(array100, array100 + size, [&alloc](Foo &x) { x.~Foo(); } );
                                                            // destroyed = 100
 
    // освободим место под array100
    alloc.deallocate(array100, size);
    array100 = nullptr;
 
    // обновим размер
    size = new_size;
 
    // array1000 больше не нужен
    // уничтожим объекты
    std::for_each(array1000, array1000 + size, [&alloc](Foo &x) { x.~Foo(); } );
                                                            //destroyed = 1000
 
    // освободим память
    alloc.deallocate(array1000, size);
 
    // перестраховка
    array1000 = nullptr;
 
    std::cout << "Objects default constructed = " << default_constructed
              << "\nObjects constructed = " << constructed
              << "\nObjects copy constructed = " << copied
              << "\nObjects move constructed = " << moved
              << "\nObjects destroyed = " << destroyed;
 
    return 0;
}
CatsCanFly
0 / 0 / 0
Регистрация: 14.04.2013
Сообщений: 21
03.08.2013, 11:57  [ТС]     О перевыделении памяти для массива объектов #14
Olivеr, Спасибо, но у меня почему то нет функции make_move_iterator (конечно, файл <iterator> подключен). Использую VC++ 2008, как я понял из гугла, make_move_iterator есть только в новом стандарте, который 2008 студией не поддерживается. А можно показать пример максимально простой? STL я еще не изучал, и использовать ее не желательно к тому же, необходимо изучить максимально подробно работу с памятью для объектов, а не пользоваться готовыми решениями STL (задача то не практическая а в целях изучения).
Суть такова, у меня есть класс, который является максимальноп простой реализацией динамического расширяемого массива объектов, например:
C++
1
2
3
4
5
6
7
8
9
10
11
class CArray
{
private:
    SomeClass* arr;
    size_t size;
    CArray() : size(10)
    {
        arr = new SomeClass[10];
    }
    //...
}
Сам класс SomeClass так же содержит в себе указатель на динамический массив и освобождает от него память в своем деструкторе:
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
class SomeClass
{
    int* smth;
    SomeClass()
    {
        smth = new int[20];
    }
    //...
    ~SomeClass()
    {
        delete[] smth;
    }
}
Допустим, мы создаем метод void resize(size_t new_size); для CArray, где копируем содержимое массива в выделенный массив нового размера и удаляем старый:
C++
1
2
3
4
5
6
void CArray::resize(size_t new_size)
{
    SomeClass* temp = new SomeClass[new_size];
    memcpy(temp, this->arr; sizeof(SomeClass) * new_size); // ??? не уверен что так хорошо делать
    delete[] arr;
}
Так вот, при удалении старого массива в этом методе вызовется и деструктор для объектов класса SomeClass, в котором будет освобождена память в которой хранились его собственные массивы, что в результате делает копирование бессмысленным - указатели в скопированных на новое место объектах остались, а память на которую они указывали мы потеряли. С другой стороны отказываться от освобождения памяти в деструткоре SomeClass нельзя, это необходимо в остальных случаях его использования. Надеюсь я понятно объяснил суть своей проблемы. Как понимаю, самый логичный ответ - не вызывать в методе resize деструкторы для содержащихся в массиве объектов, но вот как это сделать? И правильно ли это? В общем, подскажите как решить эту проблему, желательно максимально подробно и без использования готовых решений из stl и других библиотек, а так, что бы было понятно, как это работает на самом деле. Буду очень сильно благодарен
OhMyGodSoLong
~ Эврика! ~
 Аватар для OhMyGodSoLong
1234 / 983 / 42
Регистрация: 24.07.2012
Сообщений: 2,002
03.08.2013, 12:19     О перевыделении памяти для массива объектов #15
Цитата Сообщение от CatsCanFly Посмотреть сообщение
Как понимаю, самый логичный ответ - не вызывать в методе resize деструкторы для содержащихся в массиве объектов, но вот как это сделать? И правильно ли это? В общем, подскажите как решить эту проблему, желательно максимально подробно и без использования готовых решений из stl и других библиотек, а так, что бы было понятно, как это работает на самом деле. Буду очень сильно благодарен
Решение простое: никогда не хранить в контейнерах объекты, не являющиеся значениями. Объект, который нельзя просто так взять и скопировать — это не значение. Объект, деструкторы которого влияют не только на него самого — это не значение. Самый простой способ превратить объекты в значения: хранить указатели на объекты. Желательно какой-нибудь shared_ptr, а не просто голый указатель, потому что тогда не придётся танцевать с бубном, чтобы стандартые алгоритмы STL работали правильно и не теряли объекты.

Ах да, и никаких memcpy() для не POD-типов (читай: тупо интов, флоатов, или там сишных структурок).
CatsCanFly
0 / 0 / 0
Регистрация: 14.04.2013
Сообщений: 21
03.08.2013, 12:28  [ТС]     О перевыделении памяти для массива объектов #16
Цитата Сообщение от OhMyGodSoLong Посмотреть сообщение
Решение простое: никогда не хранить в контейнерах объекты, не являющиеся значениями. Объект, который нельзя просто так взять и скопировать — это не значение. Объект, деструкторы которого влияют не только на него самого — это не значение. Самый простой способ превратить объекты в значения: хранить указатели на объекты. Желательно какой-нибудь shared_ptr, а не просто голый указатель, потому что тогда не придётся танцевать с бубном, чтобы стандартые алгоритмы STL работали правильно и не теряли объекты.

Ах да, и никаких memcpy() для не POD-типов (читай: тупо интов, флоатов, или там сишных структурок).
Если никогда не хранить так подобные объекты, то как поступать, когда это все же приходится делать? Например класс - динамический расширяемый массив строк разной длины, ну или хотя бы таких же массивов?
Kuzia domovenok
 Аватар для Kuzia domovenok
1882 / 1737 / 116
Регистрация: 25.03.2012
Сообщений: 5,907
Записей в блоге: 1
03.08.2013, 13:12     О перевыделении памяти для массива объектов #17
Цитата Сообщение от OhMyGodSoLong Посмотреть сообщение
Решение простое: никогда не хранить в контейнерах объекты, не являющиеся значениями.
Довольно странное решение. Если у объекта есть корректный конструктор копий, оператор = и деструктор, как же его контейнер сломает? Пример можно? а то я не допёр.

Добавлено через 1 минуту
Цитата Сообщение от CatsCanFly Посмотреть сообщение
Olivеr, Спасибо, но у меня почему то нет функции make_move_iterator (конечно, файл <iterator> подключен). Использую VC++ 2008
некоторые пихают с++11 куда надо и не надо!

Добавлено через 3 минуты
Цитата Сообщение от CatsCanFly Посмотреть сообщение
Так вот, при удалении старого массива в этом методе вызовется и деструктор для объектов класса SomeClass, в котором будет освобождена память в которой хранились его собственные массивы, что в результате делает копирование бессмысленным - указатели в скопированных на новое место объектах остались, а память на которую они указывали мы потеряли.
А кто тебя просил мешать С и С++? Как бы я не уважал Си, но очевидно же! memcpy не вызывает перегруженного оператора =
А в корректном копировании через цикл (или через std::copy) он бы вызвался и сохранил бы нам данные от удаления деструктором.
Правило трёх же! http://ru.wikipedia.org/wiki/%D0%9F%...BD%D0%B8%D0%B5)

Добавлено через 8 минут
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
void CArray::resize(size_t new_size)
{
    SomeClass* temp = new SomeClass[new_size];
    size=std::min(size, new_size);
//memcpy(temp, this->arr; sizeof(SomeClass) * new_size); // !!! уверен, так нельзя делать
    std::copy(arr, arr+size, temp); 
/*альтернативный вариант тоже корректно работает, если оператор = корректно перегружен
    for (size_t i=0; i<size; i++)
        temp[i]=arr[i];
*/
    delete[] arr;
    arr=temp;
}
OhMyGodSoLong
~ Эврика! ~
 Аватар для OhMyGodSoLong
1234 / 983 / 42
Регистрация: 24.07.2012
Сообщений: 2,002
03.08.2013, 13:15     О перевыделении памяти для массива объектов #18
Цитата Сообщение от Kuzia domovenok Посмотреть сообщение
Довольно странное решение. Если у объекта есть корректный конструктор копий, оператор = и деструктор, как же его контейнер сломает? Пример можно? а то я не допёр.
Никак. То, что вы описали, — это и есть определение объекта-значения.

Цитата Сообщение от Kuzia domovenok Посмотреть сообщение
некоторые пихают с++11 куда надо и не надо!
А некоторые разводят тут луддизм.
Kuzia domovenok
 Аватар для Kuzia domovenok
1882 / 1737 / 116
Регистрация: 25.03.2012
Сообщений: 5,907
Записей в блоге: 1
03.08.2013, 13:18     О перевыделении памяти для массива объектов #19
Цитата Сообщение от OhMyGodSoLong Посмотреть сообщение
А некоторые разводят тут луддизм.
И как же это, если человек сам сказал, что у него MSVS2008. Что теперь IDE каждый год переустанавливать?
MoreAnswers
Эксперт
37091 / 29110 / 5898
Регистрация: 17.06.2006
Сообщений: 43,301
03.08.2013, 13:23     О перевыделении памяти для массива объектов
Еще ссылки по теме:

Функция для перевыделения памяти для массива C++
Выделение памяти для массива C++
Высвобождение памяти для 3 измерения массива C++

Искать еще темы с ответами

Или воспользуйтесь поиском по форуму:
ForEveR
Модератор
Эксперт C++
 Аватар для ForEveR
7927 / 4709 / 318
Регистрация: 24.06.2010
Сообщений: 10,524
Завершенные тесты: 3
03.08.2013, 13:23     О перевыделении памяти для массива объектов #20
Kuzia domovenok, Учитывая, что сейчас текущая версия - MSVC2012, то ДА.
Ну или не пользоваться фичами, но это не значит, что люди на форуме не могут использовать С++11 там где удобно, любой код с использованием С++11 можно переписать на С++03 так-то (ну ок, не берем в расчет variadic templates).
Yandex
Объявления
03.08.2013, 13:23     О перевыделении памяти для массива объектов
Ответ Создать тему
Опции темы

Текущее время: 01:04. Часовой пояс GMT +3.
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin® Version 3.8.9
Copyright ©2000 - 2016, vBulletin Solutions, Inc.
Рейтинг@Mail.ru