«Перемещение» последовательных контейнеров на указатели - PullRequest
0 голосов
/ 19 февраля 2019

Я создаю буфер для сетевых подключений, где вы можете явно выделить память или вы можете предоставить ее самостоятельно через какой-то последовательный контейнер (например ::std::vector,std::array), в котором эти чанки памяти хранятся всписок того, что мы используем позже для операций чтения / записи.(куски необходимы для обработки нескольких запросов на чтение / запись) У меня есть вопрос по поводу последней части, я хочу сделать указатель на данные контейнера, а затем сказать контейнеру, что он больше не заботится о своих данных.Так что-то вроде семантики перемещения.

std::vector<int> v = {9,8,7,6,5,4,3,2,1,0};
std::vector<int> _v(std::move(v));

Где _v имеет все значения v и v, оставленные в безопасном состоянии.

Проблема в том, что я просто делаю указатель для v.data()после окончания срока действия контейнера данные, указанные указателем, высвобождаются вместе с контейнером.Например:

// I would use span to make sure It's a sequential container 
// but for simplicity i use a raw pointer
// gsl::span<int> s;
int *p;
{
   std::vector<int> v = {9,8,7,6,5,4,3,2,1,0};
   // s = gsl::make_span(v);
   p = v.data();
}

for(int i = 0; i < 10; ++i) 
    std::cout << p[i] << " ";

std::cout << std::endl;

Теперь p содержит некоторое количество памяти, и мне понадобится память, ранее принадлежавшая вектору.

Я также пытался v.data() = nullptr, но v.data() - это значение.поэтому невозможно назначить его.У вас есть какие-либо предложения, или это возможно?

edit .: Чтобы было более понятно, чего я пытаюсь достичь:

class readbuf_type
{
    struct item_type // representation of a chunk
    {
        uint8_t * const data;
        size_t size;

        inline item_type(size_t psize)
            : size(psize)
            , data(new uint8_t[psize])
        {}

        template <std::ptrdiff_t tExtent = gsl::dynamic_extent>
        inline item_type(gsl::span<uint8_t,tExtent> s)
            : size(s.size())
            , data(s.data())
        {}

        inline ~item_type()
        { delete[] data; }
    };

    std::list<item_type> queue; // contains the memory
public:

    inline size_t read(uint8_t *buffer, size_t size); // read from queue

    inline size_t write(const uint8_t *buffer, size_t size); // write to queue

    inline void *get_chunk(size_t size)
    {   
        queue.emplace_back(size);
        return queue.back().data;
    }

    template <std::ptrdiff_t tExtent = gsl::dynamic_extent>
    inline void put_chunk(gsl::span<uint8_t,tExtent> arr)
    { 
        queue.emplace_back(arr);
    }
} readbuf;

У меня есть функция get_chunk, чтов основном просто выделяет память с размером, и у меня есть put_chunk, с чем я борюсь, причина, по которой мне это нужно, потому что перед тем, как вы сможете писать в эту очередь, вам нужно выделить память, а затем скопировать все элементы из буфера (вектор, массив) вы пытаетесь записать из очереди.Примерно так:

std::vector<int> v = {9,8,7,6,5,4,3,2,1,0};
// instead of this
readbuf.get_chunk(v.size);
readbuf.write(v.data(), v.size());

// we want this
readbuf.put_chunk({v});

Поскольку мы разрабатываем для распределенных систем, память имеет решающее значение, и поэтому мы хотим избежать ненужного выделения, копирования.

ps.Это мой первый пост,извините, если я не был точным с самого начала ..

Ответы [ 4 ]

0 голосов
/ 07 апреля 2019

Не уверен, что вы пытаетесь достичь, но я бы использовал движущуюся семантику вот так:

#include <iostream>
#include <memory>
#include <vector>

int main() {
std::unique_ptr<std::vector<int>> p; 
{
   std::vector<int> v = {9,8,7,6,5,4,3,2,1,0};
   p = std::move(make_unique<std::vector<int>>(v));
}

for(int i = 0; i < 10; ++i) 
   std::cout << (*p)[i] << " ";

std::cout << std::endl;


return 0;
}
0 голосов
/ 19 февраля 2019

Ваша ошибка в том, что вы думаете, что указатель «содержит» память.Он не содержит ничего, мусор или целые или что-то еще.Это указатель.Это указывает на вещи.Вы удалили этот материал и не перенесли его в другое место, поэтому он больше не может работать.

В общем случае вам понадобится контейнер для помещения этой информации, будь то другойвектор, или даже ваш собственный массив ручной работы.Наличие указателя на данные не означает, что у вас есть данные.

Кроме того, поскольку невозможно попросить вектор отказаться от своего буфера для не векторной вещи, вектор - ваш единственный шанс в этом конкретном случае.дело.Не совсем понятно, почему это не достаточно хорошее решение для вас.:)

0 голосов
/ 19 февраля 2019

К сожалению, область памяти вектора не может быть отделена от объекта std :: vector.Область памяти может быть удалена, даже если вы вставите некоторые данные в объект std :: vector.Поэтому использование этой области памяти позже небезопасно, если только вы не уверены, что этот конкретный объект std :: vector существует и не изменен.

Решение этой проблемы состоит в выделении новой области памяти и копированиисодержимое вектора в этой вновь выделенной области памяти.К вновь выделенной области памяти можно безопасно обращаться, не беспокоясь о состоянии объекта std :: vector.

std::vector<int> v = {1, 2, 3, 4};
int* p = new int[v.size()];
memcpy(p, v.data(), sizeof(int) * v.size());

Не забудьте удалить область памяти после окончания использования этой области памяти.

delete [] p;
0 голосов
/ 19 февраля 2019

Нет, невозможно «украсть» буфер стандартного вектора так, как вы предлагаете, или любой другой стандартный контейнер по этому вопросу.

Вы уже показали одно решение: Переместитьбуфер в другой вектор, вместо того, чтобы просто брать адрес (или другую не принадлежащую ссылку) буфера.Переход от вектора переносит владение внутренним буфером.

Можно было бы реализовать такой пользовательский векторный класс, чей буфер можно было бы украсть, но есть причина, по которой вектор не делает это возможным.Это может быть довольно сложно доказать правильность вашей программы, если вы используете ресурсы волей-неволей.Рассматривали ли вы, как предотвратить утечку данных?Приведенное выше решение намного проще и проще для проверки на правильность.

Другой подход заключается в том, чтобы реструктурировать вашу программу таким образом, чтобы никакие ссылки на данные вашего контейнера не переживали сам контейнер (или любую недействительную операцию)..

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...