繁体   English   中英

为什么我们不能更改 std::vector 的数据指针?

[英]Why can't we change data pointer of std::vector?

我有一个数组char* source和一个向量std::vector<char> target 我想让向量target指向 O(1) 中的source ,而不复制数据。

这些方面的东西:

#include <vector>

char* source = new char[3] { 1, 2, 3 };
std::vector<char> target;
target.resize(3);

target.setData(source); // <- Doesn't exist
// OR
std::swap(target.data(), source); // <- swap() does not support char*
delete[] source;

为什么不能手动更改向量指向的位置? 如果可能的话,是否会出现一些特定的、无法管理的问题?

C++ vector class支持添加和删除元素,保证memory中的连续顺序。 如果您可以使用现有的 memory 缓冲区初始化您的vector ,并向其中添加足够的元素,它会溢出或需要重新分配。

vector的接口假定它管理其内部缓冲区,也就是说,它可以随时分配、解除分配、调整大小(当然,在规范范围内)。 如果您需要一些不允许管理其缓冲区的东西,则不能使用vector - 使用不同的数据结构或自己编写一个。

您可以通过复制数据(使用带有两个指针或assign的构造函数)来创建vector object,但这显然不是您想要的。

或者,您可以使用string_view ,它看起来几乎或可能正是您需要的。

std::vector被认为是底层缓冲区的所有者。 您可以更改缓冲区,但此更改会导致分配,即制作您不想要的源缓冲区的副本(如问题中所述)。

您可以执行以下操作:


#include <vector>

int main() {
    char* source = new char[3] { 1, 2, 3 };
    std::vector<char> target;
    target.resize(3);
    target.assign(source, source + 3);
    delete[] source;
    return 0;
}

但又std::vector::assign

将内容替换为 [first, last) 范围内的内容的副本

因此再次执行复制。 使用std::vector时您无法摆脱它。

如果您不想复制数据,那么您应该使用 C++20 中的std::span (或创建您自己的跨度)或使用std::string_view (看起来适合您,因为您有一个char数组)。

第一个选项:使用std::string_view

由于您仅限于 C++17,因此std::string_view可能非常适合您。 它从source指向的元素开始构造字符数组的前 3 个字符的视图。

#include <iostream>
#include <string_view>

int main() {
    char* source = new char[3] { 1, 2, 3 };

    std::string_view strv( source, 3 );

    delete[] source;

    return 0;
}

第二个选项:使用 C++20 中的std::span

std::span来自 C++20,因此它可能不是最适合您的方式,但您可能会对它是什么以及它的工作原理感兴趣。 您可以将std::span视为std::string_view的一个通用版本,因为它是任何类型的对象的连续序列,而不仅仅是字符。 用法与std::string_view

#include <span>
#include <iostream>

int main() {
    char* source = new char[3] { 1, 2, 3 };

    std::span s( source, 3 );

    delete[] source;

    return 0;
}

第三个选项:你自己的跨度

如果您仅限于 C++17,您可以考虑创建自己的span结构。 这可能仍然是一种矫枉过正,但让我告诉你(顺便看看这个更详细的答案):

template<typename T>
class span {
   T* ptr_;
   std::size_t len_;

public:
    span(T* ptr, std::size_t len) noexcept
        : ptr_{ptr}, len_{len}
    {}

    T& operator[](int i) noexcept {
        return *ptr_[i];
    }

    T const& operator[](int i) const noexcept {
        return *ptr_[i];
    }

    std::size_t size() const noexcept {
        return len_;
    }

    T* begin() noexcept {
        return ptr_;
    }

    T* end() noexcept {
        return ptr_ + len_;
    }
};

int main() {
    char* source = new char[3] { 1, 2, 3 };

    span s( source, 3 );

    delete[] source;

    return 0;
}

所以用法与 C++20 版本的std::span相同。

std::string_view 和 std::span 是好东西(如果你有支持它们的编译器版本)。 滚动你自己的类似物也可以。

但是有些人忽略了为什么要对向量执行此操作的全部要点:

  • 因为你有一个API 给Struct[] + size_t并给你所有权
  • 而且您还有一个接受std::vector<Struct>API

所有权可以很容易地转移到向量中,并且没有复制!

你可以说:但是自定义分配器,memory 映射文件指针,ROM memory 然后我可以设置为指针呢?

  • 如果您已经准备好设置矢量内部,您应该知道自己在做什么。
  • 在这些情况下,您实际上可以尝试为您的向量提供一个“正确的”分配器。

也许会在编译此代码时发出警告,但如果可能的话,那就太好了。

我会这样做:

  • std::vector会得到一个构造函数,它要求std::vector::raw_source
  • std::vector::raw_source是一个uint8_t*, size_t, bool结构(目前)
  • bool takeOwnership : 告诉我们是否取得所有权 (false => copy)
  • size_t size : 原始数据的大小
  • uint8_t* ptr :指向原始数据的指针
  • 在取得所有权时,向量调整大小等使用向量分配策略,因为无论如何您都提供了模板参数 - 这里没有什么新鲜事。 如果这不符合你做错事的数据!

是的 API 我说看起来比单个“set_data(..)”或“own_memory(...)”function 更复杂,但试图澄清任何曾经使用过这个 Z8A5DA52ED12644427D359E70CA5 的人都非常了解它的含义。 我希望这个 API 存在,但也许我仍然忽略了其他问题原因?

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM