Я даю ответ на оригинальный вопрос ниже.Однако имейте в виду, что в большинстве случаев использование vector::data()
и реорганизация кода таким образом, чтобы упомянутый вектор не выходил за рамки, использование другого вектора, инициализированного vector(std::move(otherVector))
, или замена содержимого другим вектором (vector::swap(otherVector)
)лучший вариант, как было указано в других ответах.
Тем не менее, существует способ отсоединения данных вектора с помощью специального распределителя.Следующее реализует распределитель Noalloc
, который абсолютно ничего не делает.Он не распределяет / конструирует вещи, не освобождает / не разрушает вещи.Также он не содержит нестатических или виртуальных переменных-членов.Поэтому он не должен изменять содержимое экземпляра vector
.Внутри detachData
мы приводим обычный вектор к вектору, используя вышеупомянутый тип распределителя, переосмысливая его тип и перемещая его содержимое в новый вектор с тем же распределителем.Когда новый вектор выходит из области видимости, он будет пытаться уничтожить и освободить содержимое своего массива.Однако, поскольку мы переопределили эту функциональность в нашем пользовательском распределителе, это абсолютно ничего не делает.Функция возвращает указатель на недавно отсоединенные данные исходного вектора (который теперь содержит nullptr в качестве данных и нулевые элементы).Не забудьте потом уничтожить и освободить элементы массива вручную!
#include <iostream>
#include <vector>
#include <new>
// Our custom allocator
template <class T>
struct Noalloc {
typedef T value_type;
Noalloc() = default;
template <class U> constexpr Noalloc(const Noalloc<U>&) noexcept {}
T* allocate(std::size_t n)
{
return nullptr;
}
void deallocate(T* p, std::size_t) noexcept {}
template<class... Args> void construct(T* p, Args&&... args) {}
void destroy(T* p) {}
};
template <class T, class U>
bool operator==(const Noalloc<T>&, const Noalloc<U>&) { return true; }
template <class T, class U>
bool operator!=(const Noalloc<T>&, const Noalloc<U>&) { return false; }
// detach the data from a vector and return its pointer
template<typename T>
T* detachData(std::vector<T>& vec)
{
T* dataPtr = vec.data();
// swap contents with a new vector
// that uses "Noalloc" as allocator but is otherwise identical
std::vector<T, Noalloc<T>> detachHelper
(std::move(*reinterpret_cast<std::vector<T, Noalloc<T>>*>(&vec)));
return dataPtr;
// "detachHelper" runs out of scope here
// But it will neither destruct nor deallocate
// given data due to our custom allocator
}
// destroy and deallocate the data manually
template<typename T>
void destroyData(T* data, size_t n, size_t capacity)
{
// should be pretty self explanatory...
std::allocator<T> alloc;
for(size_t i = 0; i < n; i++)
{
std::allocator_traits<std::allocator<T>>::destroy(alloc, data+i);
}
std::allocator_traits<std::allocator<T>>::deallocate(alloc, data, capacity);
}
int main()
{
int* testData = nullptr;
size_t size = 0;
// For vectors data size and actually allocated size
// may differ. So we need to keep track of both.
// If you want to avoid this you may call "vector::shrink_to_fit()"
// This should resize the vector to fit its data.
// However the implementation is not required to fulfill this request.
// So beware!
size_t capacity = 0;
{
std::vector<int> test{1,2,3,4};
// copy size and data pointer
size = test.size();
capacity = test.capacity();
testData = detachData(test);
// "test" runs out of scope here.
// However after calling "detachData(test)"
// it doesn't contain any data
}
// Print the contents of the array
for(size_t i = 0; i < size; i++)
{
std::cout << testData[i] << std::endl;
}
// Don't forget to destroy the data yourself!
// The original vector doesn't exist anymore
destroyData(testData, size, capacity);
std::cout << "data destroyed successfully" << std::endl;
}