簡體   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