Страницы

Поиск по вопросам

вторник, 31 декабря 2019 г.

Redim Preserve. Вопрос сложней, чем вы думаете.

#алгоритм #cpp


Соответственно на C++ имеем класс:
  Class MyClass{ 
  public: 
        double *ptr; 
       void Set(); 
  }; 
  MyClass::Set(){ 
        ptr=new double[10000000]; 
  }

Далее в основном коде:
  MyClass *A; 
  A=new MyClass[10000];

Затем требуется изменить размерность массива A с сохранением его элементов, вопрос
в том как это сделать максимально производительно !?    


Ответы

Ответ 1



Способ в лоб: создать новый массив, скопировать туда элементы старого массива, уничтожить старый массив. Правильный способ: использовать STL. P.S. В таком классе, где происходит динамическое выделение памяти, нужно позаботиться об уничтожении ее в деструкторе и о копировании ее в копирующем конструкторе и в операторе присвоения. Иначе дело может очень плохо закончиться ((( PPS. Вот тут на досуге накропал нечто. Приятного аппетита. #include #include using namespace std; enum {SIZE = 10000, SIZE_CL = 5}; class MyClass { private: double* ptr; void array_cpy (MyClass& mc) { if (!flag) { cout << "Copying array" << endl; ptr = new double [SIZE]; memcpy (ptr, mc.ptr, SIZE*sizeof(double)); } else { cout << "Copying ptr" << endl; ptr = mc.ptr; mc.ptr = 0; } } public: static bool flag ; static int count; void set() { cout << "Creating array" << endl; ptr = new double [SIZE]; for (int i = 0; i< SIZE; i++) { ptr[i] = count; } count++; } double get(int index) { return ptr [index]; } MyClass (): ptr(0) {} MyClass (MyClass& mc) { array_cpy(mc); } MyClass& operator= (MyClass& mc) { array_cpy(mc); return *this; } ~MyClass () { if (!flag && ptr) { cout << "Deleting array" << endl; delete[] ptr; } } }; bool MyClass:: flag = false; int MyClass:: count = 0; int main() { MyClass* aa = new MyClass [SIZE_CL]; for (int i = 0; i < SIZE_CL; i++) aa[i].set(); MyClass::flag = true; MyClass* bb = new MyClass [SIZE_CL*2] ; for (int i = 0; i< SIZE_CL; i++) { bb[i] = aa[i]; } MyClass::flag = false; cout << bb[3].get(400) << endl; delete[] bb; } А теперь с vector'ом: #include int main() { vector vec; for (int i = 0; i < SIZE_CL; i++) { vec.push_back (new MyClass); vec[i]->set(); } cout << vec[3]->get(400) << endl; for (int i = SIZE_CL; i < SIZE_CL*2; i++) { vec.push_back (new MyClass); vec[i]->set(); } cout << vec[3]->get(400) << endl; } Сравни выводы программы и проанализируй их. Я думаю, с вектором быстрее на порядок, потому что он работает с массивом указателей на объекты и это очень сильно убыстряет работу.

Ответ 2



Есть подход при котором большой массив храниться в виде дерева под-массивов. Есть базовый массив (назовем его а) в нем хранятся указатели на массивы (bi) и длинна каждого из них. При запросе элемента х из данного массива сначала происходит обращение к массиву "а", в нем определяется в каком из массивов "b" находится данный элемент и забирает его от туда. Данный подход очень удобен при изменении размеров массива т.к. нам нужно просто выделить место под новый массив и добавить ссылку на него в массив. Есть еще куча преимуществ из недостатков несколько более медленное обращение к элементу массива. Если нужно подробнее и с картинками напиши в коментах.

Ответ 3



Я как-то соревновался с STL, пытаясь создать контейнер, который быстрее стандартного. Я написал класс, который пишет, когда вызывается его конструктор, конструктор копирования и деструктор. При реаллокации и удалении элементов из середины vector генерировал кучу сообщений. В своём массиве я сделал так, что конструктор вызывается только при добавлении элемента, а деструктор при удалении. Даже при перераспределении памяти элементы не копируются и не удаляются. Вот как я этого добился. Я выделял память при помощи malloc и освобождал через free, которые работают с пустой памятью, а не с объектами. Конструкторы и деструкторы я вызывал вручную. При перераспределении памяти я использовал realloc. Не вызывается никаких конструкторов и деструкторов. Получается, что просто поменялся адрес объекта (а если realloc смог просто увеличить буфер, то и не поменялся!). По сравнению с вектором это вводит небольшое ограничение: нельзя хранить в контейнере объекты, которые полагаются на то, что их адрес всегда будет неизменным. В любом случае, такие объекты встречаются очень редко, а в векторе и так очень много более важных ограничений. Этот подход можно применить и здесь: struct MyClass { double* ptr; //Ты забыл конструктор и деструктор MyClass() {Set();} ~MyClass() {Clear();} void Set() {ptr=new double[10000000];} void Clear() {delete[] ptr; ptr=nullptr;} }; В основном коде: MyClass* A=malloc(10000*sizeof(MyClass)); //Вызываем конструкторы for(int i=0; i<10000; i++) new(A+i) MyClass; //... //Работаем с ним до того, как перестанет хватать места //... A=realloc(A, 20000*sizeof(MyClass)); //Вызываем конструкторы для новых 10000 объектов for(int i=10000; i<20000; i++) new(A+i) MyClass; //... //Удаляем for(int i=0; i<20000; i++) a[i].~MyClass(); free(A); Здесь используется размещающий new. Если будет ошибка компиляции, возможно понадобится подключить файл new.h. MyClass просто играет роль какой-то нагрузки, и неважно, что он делает и как выделяет память.

Комментариев нет:

Отправить комментарий