왜 std :: vector의 데이터 포인터를 변경할 수 없습니까?

Dec 30 2020

배열 char* source과 벡터가 std::vector<char> target있습니다. 데이터를 복사하지 않고 O (1)에서 벡터를 target가리키고 싶습니다 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;

벡터가 가리키는 위치를 수동으로 변경할 수없는 이유는 무엇입니까? 이것이 가능하다면 발생할 수있는 구체적이고 관리 할 수없는 문제가 있습니까?

답변

7 anatolyg Dec 30 2020 at 17:39

C ++ vector클래스는 메모리에서 보장 된 연속 순서로 요소 추가 및 삭제를 지원합니다. vector기존 메모리 버퍼로 초기화 하고 충분한 요소를 추가 할 수 있다면 오버플로되거나 재 할당이 필요합니다.

의 인터페이스는 내부 버퍼를 관리vector 한다고 가정합니다. 즉 , 원할 때마다 (물론 사양 내에서) 할당, 할당 해제, 크기 조정이 가능합니다. 버퍼 를 관리 할 수없는 것이 필요한 경우 다른 데이터 구조를 사용하거나 직접 작성할 수 없습니다 .vector

vector두 개의 포인터가있는 생성자 또는를 사용하여 데이터를 복사하여 객체를 만들 수 assign있지만 이것은 분명히 원하는 것이 아닙니다.

또는을 사용할 수 있습니다 string_view. 이는 필요한 것과 거의 또는 정확히 일치합니다.

3 NutCracker Dec 30 2020 at 19:23

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.

데이터를 복사하지 않으려면 std::spanC ++ 20에서 사용 하거나 (또는 ​​고유 한 범위를 만들거나) 사용 std::string_view(의 배열이 있으므로 적합 해 보입니다 char).

첫 번째 옵션 : 사용 std::string_view

C ++ 17로 제한되어 std::string_view있으므로 완벽 할 수 있습니다. 로 가리키는 요소로 시작하는 문자 배열의 처음 3 자 뷰를 구성합니다 source.

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

두 번째 옵션 : std::spanC ++ 20에서 사용

std::spanC ++ 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구조체 를 만드는 것을 생각할 수 있습니다 . 여전히 과잉 일 수 있지만 보여 드리겠습니다 (btw는 이보다 정교한 답변을 살펴보십시오 ).

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.