“移动”顺序容器到指针

时间:2019-02-19 16:21:21

标签: c++ c++17 cpp-core-guidelines

我正在为网络连接建立一个缓冲区,您可以在其中显式分配内存,也可以通过一些顺序的容器(例如: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的所有vv值都处于安全状态。

问题是,如果我只是在容器的生存期结束后为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()是右值,因此无法分配它。您有什么建议吗,或者有可能吗?

编辑。: 为了更清楚地说明我要实现的目标:

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 :(得分:4)

否,不可能以您建议的方式“窃取”标准向量的缓冲区-或任何其他与此有关的标准容器。

您已经展示了一种解决方案:将缓冲区移到另一个向量中,而不仅仅是获取缓冲区的地址(或另一个非所有者引用)。从向量移出将转移内部缓冲区的所有权。

有可能实现这样的自定义矢量类,其缓冲区可能被盗,但是矢量无法实现这一点是有原因的。如果您随意地释放资源,很难证明程序的正确性。您是否考虑过如何防止数据泄漏?上面的解决方案更简单,更容易验证正确性。

另一种方法是重新构建程序,以使对容器数据的引用不会超出容器本身(或任何无效操作)。

答案 1 :(得分:1)

不幸的是,向量的存储区域无法与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;

答案 2 :(得分:0)

您的错误是在认为指针“包含”内存。它不包含任何东西,垃圾,整数或其他。它是一个指针。它指向东西。您已经删除了这些内容,并且没有将其转移到其他任何地方,因此它不再起作用。

通常,您将需要一个容器来放置此信息,它可以是另一个向量,甚至是您自己的手工数组。仅具有指向数据的指针并不意味着您具有数据。

此外,由于不可能要求向量将其缓冲区释放为非向量对象,因此在这种特殊情况下,向量实际上是您唯一的机会。尚不清楚为什么这对您来说还不够好。 :)

答案 3 :(得分:0)

不确定您要达到什么目的,但我会使用这样的移动语义:

#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;
}