简体   繁体   English

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

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

I have an array char* source and a vector std::vector<char> target .我有一个数组char* source和一个向量std::vector<char> target I'd like to make the vector target point to source in O(1), without copying the data.我想让向量target指向 O(1) 中的source ,而不复制数据。

Something along these lines:这些方面的东西:

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

Why is it not possible to manually change where a vector points to?为什么不能手动更改向量指向的位置? Is there some specific, unmanageable problem that would arise if this was possible?如果可能的话,是否会出现一些特定的、无法管理的问题?

C++ vector class supports adding and deleting elements, with guaranteed consecutive order in memory. C++ vector class支持添加和删除元素,保证memory中的连续顺序。 If you could initialize your vector with existing memory buffer, and add enough elements to it, it would either overflow or require reallocation.如果您可以使用现有的 memory 缓冲区初始化您的vector ,并向其中添加足够的元素,它会溢出或需要重新分配。

The interface of vector assumes that it manages its internal buffer, that is, it can allocate, deallocate, resize it whenever it wants (within spec, of course). vector的接口假定它管理其内部缓冲区,也就是说,它可以随时分配、解除分配、调整大小(当然,在规范范围内)。 If you need something that is not allowed to manage its buffer, you cannot use vector - use a different data structure or write one yourself.如果您需要一些不允许管理其缓冲区的东西,则不能使用vector - 使用不同的数据结构或自己编写一个。

You can create a vector object by copying your data (using a constructor with two pointers or assign ), but this is obviously not what you want.您可以通过复制数据(使用带有两个指针或assign的构造函数)来创建vector object,但这显然不是您想要的。

Alternatively, you can use string_view , which looks almost or maybe exactly what you need.或者,您可以使用string_view ,它看起来几乎或可能正是您需要的。

std::vector is considered to be the owner of the underlying buffer. std::vector被认为是底层缓冲区的所有者。 You can change the buffer but this change causes allocation ie making a copy of the source buffer which you don't want (as stated in the question).您可以更改缓冲区,但此更改会导致分配,即制作您不想要的源缓冲区的副本(如问题中所述)。

You could do the following:您可以执行以下操作:


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

but againstd::vector::assign :但又std::vector::assign

Replaces the contents with copies of those in the range [first, last).将内容替换为 [first, last) 范围内的内容的副本

So copy is performed again.因此再次执行复制。 You can't get away from it while using std::vector .使用std::vector时您无法摆脱它。

If you don't want to copy data, then you should use std::span from C++20 (or create your own span) or use std::string_view (which looks suitable for you since you have an array of char s).如果您不想复制数据,那么您应该使用 C++20 中的std::span (或创建您自己的跨度)或使用std::string_view (看起来适合您,因为您有一个char数组)。

1st option: Using std::string_view第一个选项:使用std::string_view

Since you are limited to C++17, std::string_view might be perfect for you.由于您仅限于 C++17,因此std::string_view可能非常适合您。 It constructs a view of the first 3 characters of the character array starting with the element pointed by source .它从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;
}

2nd option: Using std::span from C++20第二个选项:使用 C++20 中的std::span

std::span comes from C++20 so it might not be the most perfect way for you, but you might be interested in what it is and how it works. std::span来自 C++20,因此它可能不是最适合您的方式,但您可能会对它是什么以及它的工作原理感兴趣。 You can think of std::span as a bit generalized version of std::string_view because it is a contiguous sequence of objects of any type, not just characters.您可以将std::span视为std::string_view的一个通用版本,因为它是任何类型的对象的连续序列,而不仅仅是字符。 The usage is similar as with the 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;
}

3rd option: Your own span第三个选项:你自己的跨度

If you are limited to C++17, you can think of creating your own span struct.如果您仅限于 C++17,您可以考虑创建自己的span结构。 It might still be an overkill but let me show you (btw take a look at this more elaborated answer ):这可能仍然是一种矫枉过正,但让我告诉你(顺便看看这个更详细的答案):

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

So the usage is the same as with the C++20's version of std::span .所以用法与 C++20 版本的std::span相同。

The std::string_view and std::span are good things to have (if you have compiler version supporting them). std::string_view 和 std::span 是好东西(如果你有支持它们的编译器版本)。 Rolling your own similars is ok too.滚动你自己的类似物也可以。

But some people miss the whole point why one would want to do this exactly to a vector:但是有些人忽略了为什么要对向量执行此操作的全部要点:

  • Because you have an API that gives Struct[] + size_t and give you ownership因为你有一个API 给Struct[] + size_t并给你所有权
  • and you also have an API that accepts std::vector<Struct>而且您还有一个接受std::vector<Struct>API

ownership could be easily transferred into the vector and no copies made!所有权可以很容易地转移到向量中,并且没有复制!

You can say: But what about custom allocators, memory mapped file pointers, rom memory that I could then set as the pointer?你可以说:但是自定义分配器,memory 映射文件指针,ROM memory 然后我可以设置为指针呢?

  • If you are already about to set vector internals you should know what you are doing.如果您已经准备好设置矢量内部,您应该知道自己在做什么。
  • You can try to supply a "correct" allocator in those cases to your vector actually.在这些情况下,您实际上可以尝试为您的向量提供一个“正确的”分配器。

Maybe give a warning on compiling this code yes, but it would be nice if it would be possible.也许会在编译此代码时发出警告,但如果可能的话,那就太好了。

I would do it this way:我会这样做:

  • std::vector would get a constructor that asks for a std::vector::raw_source std::vector会得到一个构造函数,它要求std::vector::raw_source
  • std::vector::raw_source is an uint8_t*, size_t, bool struct (for now) std::vector::raw_source是一个uint8_t*, size_t, bool结构(目前)
  • bool takeOwnership : tells if we are taking ownership (false => copy) bool takeOwnership : 告诉我们是否取得所有权 (false => copy)
  • size_t size : the size of the raw data size_t size : 原始数据的大小
  • uint8_t* ptr : the pointer to raw data uint8_t* ptr :指向原始数据的指针
  • When taking ownership, vector resize and such uses the vectors allocation strategy as you otherwise provided with your template params anyways - nothing new here.在取得所有权时,向量调整大小等使用向量分配策略,因为无论如何您都提供了模板参数 - 这里没有什么新鲜事。 If that does not fit the data you are doing wrong stuff!如果这不符合你做错事的数据!

Yes API I say look more complicated than a single "set_data(..)" or "own_memory(...)" function, but tried to make it clear that anyone who ever uses this api pretty much understands implications of it automagically.是的 API 我说看起来比单个“set_data(..)”或“own_memory(...)”function 更复杂,但试图澄清任何曾经使用过这个 Z8A5DA52ED12644427D359E70CA5 的人都非常了解它的含义。 I would like this API to exists, but maybe I still overlook other causes of issues?我希望这个 API 存在,但也许我仍然忽略了其他问题原因?

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

相关问题 为什么我们不能声明一个 std::vector<abstractclass> ?</abstractclass> - Why can't we declare a std::vector<AbstractClass>? 为什么std :: vector :: data()中没有使用指针typedef? - Why is the pointer typedef not used in std::vector::data()? 为什么我们需要使用std :: vector :: pointer来访问元素? - Why do we need to use std::vector::pointer to access elements? 带数据交换的std :: vector指针 - std::vector pointer with data swap 为什么 GCC 不能假设 std::vector::size 在这个循环中不会改变? - Why can't GCC assume that std::vector::size won't change in this loop? 我可以传递一个 std::vector 吗<std::unique_ptr<t> > 作为一个没有额外分配的原始指针向量? </std::unique_ptr<t> - Can I pass a std::vector<std::unique_ptr<T>> as a vector of raw pointer without extra allocations? 为什么我们不能推广指针声明? - Why can't we generalize the pointer declaration? 为什么我们不能删除初始化的指针? - Why can't we delete an initialized pointer? 为什么我们不能给指针赋值 - Why we can't assign value to pointer 为什么 std::vector 的字符串文字初始化列表不能创建 std::vector<std::string> 在 C++ 中?</std::string> - Why can't a string literal initializer list for a std::vector create a std::vector<std::string> in C++?
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM