Последовательные контейнеры и непрерывная память
Насколько мне известно, последовательные stl-контейнеры (вектор, лист) обязаны поддерживать у пользователя иллюзию того, что они есть данные, размещенные в непрерывной памяти. Однако внутренняя их реализация якобы может этому условию не удовлетворять...
В связи с такой неоднозначностью вопрос - можно ли использовать указатели на элементы контейнеров в функциях для работы с памятью, например CopyMemory, memcmp и т.д. ?
Для вектора в принципе можно было бы использовать, но не желательно. Тем более что интерфейсы STL предоставляют такую же функциональность.
Все это похоже на какое-то хакерство, что не есть хорошо. Лучше использовать алгоритмы STL, специальные конструкторы контейнеров.
Просто библиотека, с которой в данный момент я работаю, предоставляет кучу функций, которым надо передавать указатели на блоки памяти. А от использования stl отказываться не хотелось бы...но видимо придется.
Я бы наверно сделал наследник контейнера с функциями, которые динамически генерируют временный массив, с которыми бы могли работать функции той библиотеки. Есть же у строки функция c_str(). Наверно что-то подобное можно и для других контейнеров изобрести.
А насчет динамической генерации массивов...мне в программе время дорого(кучу данных надо обрабатывать), так что боюсь не пройдет така штука.
Но все равно спасибо за совет.
А насчет динамической генерации массивов...мне в программе время дорого(кучу данных надо обрабатывать), так что боюсь не пройдет така штука.
Наверно, тут больше жалко память. Возможно, времени не очень много потратится. Попробую на скорую руки изобрести (наверное, велосипед )))) )
struct myVector: public vector<int>
{
int *array;
int* GetArray()
{
array = new int [this->size()];
copy(this->begin(), this->end(), array);
return array;
}
void ApplyArray()
{
if(array)
{
copy(array, array + this->size(), this->begin());
delete [] array;
}
}
myVector():array(0){}
myVector(size_t sz, int val):array(0), vector<int>(sz, val){}
~myVector(){if(array) delete [] array;}
};
int main()
{
myVector v(5, 1);
for(size_t i = 0; i < v.size(); ++i)
cout << v << ' ';
cout << endl;
// Плучаем ссылку на массив
int *p = v.GetArray();
for(int i = 0; i < v.size(); ++i)
{
*(p + i) = i*2;
}
// удаляем массив
v.ApplyArray();
for(size_t i = 0; i < v.size(); ++i)
{
cout << v << ' ';
}
return 0;
}
23.2.4 Class template vector
...
The elements of a vector are stored contiguously, meaning that if v is a vector<T, Allocator> where T is some type other than bool, then it obeys the identity &v[n] == &v[0] + n for all 0 <= n < v.size().
Но использовать такие возможности без надобности не стоит - отсутствует контроль выхода за пределы вектора. Но ничего не поделаешь, если приходится использовать сторонние API с подобным вызовом.
Признаю - заблуждался.
Однако, на list это правило не распостраняется (не нашел такого в стандарте и эксперименты подтвердили). Поэтому в данном случае лучше работать с вектором. Однако, не удержусь - доработаю свой код для листа.
template<class T> struct myList: public list<T>
{
T *array;
T* GetArray()
{
if(array) return array;
array = new T [this->size()];
copy(this->begin(), this->end(), array);
return array;
}
void ApplyArray()
{
if(array)
{
copy(array, array + this->size(), this->begin());
delete [] array;
array = 0;
}
}
myList():array(0){}
myList(size_t sz, T val):array(0), list<T>(sz, val){}
~myList(){if(array) delete [] array;}
};
int main()
{
myList<int> v(5, 1);
for(myList<int>::iterator it = v.begin(); it != v.end(); ++it)
cout << *it << ' ';
cout << endl;
// Получаем указатель на массив и работаем с ним
for(int *p = v.GetArray() + v.size() - 1; p >= v.array; --p)
{
*p += 2;
}
// применяем и удаляем массив
v.ApplyArray();
for(myList<int>::iterator it = v.begin(); it != v.end(); ++it)
cout << *it << ' ';
return 0;
}
Однако тоже не удержусь и упрощу:
{
list<int> v(5, 1);
for(list<int>::iterator it = v.begin(); it != v.end(); ++it)
cout << *it << ' ';
cout << endl;
// Получаем указатель на массив и работаем с ним
vector<int> array(v.begin(), v.end());
for(int* p = &array.back(); p >= &array.front(); --p)
{
*p += 2;
}
// применяем массив
v.clear();
copy(array.begin(), array.end(), back_inserter(v));
for(list<int>::iterator it = v.begin(); it != v.end(); ++it)
cout << *it << ' ';
return 0;
}
Ну, так каждый может. У меня ж там понты с наследованием контейнера :)
Если серьезно, то непонятно, почему
copy(array.begin(), array.end(), back_inserter(v));
А не
В данном случае вроде размер массива всегда равен размеру листа.
А вот наследоваться от контейнеров считается плохой практикой.
Если серьезно, то непонятно, почему
copy(array.begin(), array.end(), back_inserter(v));
А не
В данном случае вроде размер массива всегда равен размеру листа.
Ну так "во время пути, собачка могла слегка подрасти", другими словами: в процессе работы с array его размер может быть изменен в любую сторону. Конечно, можно запретить изменять размер, но я решил просто переписывать список новыми значениями.