반복하지 않고 배열의 내용을 C ++의 std :: vector에 어떻게 복사합니까?


122

나중에 처리하기 위해 저장해야하는 프로그램의 다른 부분에서 내 함수로 전달되는 값 배열이 있습니다. 데이터를 처리 할 시간이되기 전에 내 함수가 몇 번 호출 될지 모르기 때문에 동적 저장 구조가 필요하므로 std::vector. push_back모든 값에 대해 개별적으로 표준 루프를 수행 할 필요가 없습니다 memcpy..

답변:


117

배열과 배열 크기를 얻은 후에 벡터를 구성 할 수 있다면 다음과 같이 말할 수 있습니다.

std::vector<ValueType> vec(a, a + n);

... a배열이고 n포함 된 요소의 수 라고 가정 합니다 . 그렇지 않으면 std::copy()w / resize()가 트릭을 수행합니다.

memcpy()값이 POD (plain-old data) 유형이라는 것을 확신 할 수없는 한 멀리 떨어져 있습니다.

또한 이들 중 어느 것도 for 루프를 피할 수 없다는 점은 주목할 가치가 있습니다. 코드에서 확인해야하는지 여부에 대한 질문 일뿐입니다. O (n) 런타임 성능은 값 복사를 위해 불가피합니다.

마지막으로 C 스타일 배열은 대부분의 STL 알고리즘에 대해 완벽하게 유효한 컨테이너입니다. 원시 포인터는 begin(), ( ptr + n)는 end().


4
반복 및 push_back 호출이 나쁜 이유는 배열이 충분히 길면 벡터의 크기를 여러 번 강제로 조정할 수 있기 때문입니다.
bradtgmurray

@bradtgmurray : 위에서 제안한 "two iterators"벡터 생성자의 합리적인 구현은 필요한 수의 요소를 얻기 위해 두 개의 반복자에서 std :: distance ()를 먼저 호출 한 다음 한 번만 할당 할 것이라고 생각합니다.
Drew Hall

4
@bradtgmurray : push_back ()조차도 벡터의 기하 급수적 인 증가 (일명 "상각 된 상수 시간") 때문에 그렇게 나쁘지는 않습니다. 최악의 경우 런타임이 2 배 정도 나빠질 것이라고 생각합니다.
Drew Hall

2
벡터가 이미 있으면 vec.clear (); vec.insert (vec.begin (), a, a + n); 잘 작동합니다. 그러면 a가 포인터가 될 필요도없고 반복자 일 뿐이며 벡터 할당은 일반적으로 실패 할 것입니다 (그리고 C ++ / STL 방식).
MP24

6
구성 할 수없는 또 다른 대안은 : assign : vec.assign(a, a+n)이며 복사 및 크기 조정보다 더 간단합니다.
mMontu 2013-10-28

209

여기에 많은 답변이 있었고 거의 모두가 작업을 완료 할 것입니다.

그러나 잘못된 조언이 있습니다!

옵션은 다음과 같습니다.

vector<int> dataVec;

int dataArray[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
unsigned dataArraySize = sizeof(dataArray) / sizeof(int);

// Method 1: Copy the array to the vector using back_inserter.
{
    copy(&dataArray[0], &dataArray[dataArraySize], back_inserter(dataVec));
}

// Method 2: Same as 1 but pre-extend the vector by the size of the array using reserve
{
    dataVec.reserve(dataVec.size() + dataArraySize);
    copy(&dataArray[0], &dataArray[dataArraySize], back_inserter(dataVec));
}

// Method 3: Memcpy
{
    dataVec.resize(dataVec.size() + dataArraySize);
    memcpy(&dataVec[dataVec.size() - dataArraySize], &dataArray[0], dataArraySize * sizeof(int));
}

// Method 4: vector::insert
{
    dataVec.insert(dataVec.end(), &dataArray[0], &dataArray[dataArraySize]);
}

// Method 5: vector + vector
{
    vector<int> dataVec2(&dataArray[0], &dataArray[dataArraySize]);
    dataVec.insert(dataVec.end(), dataVec2.begin(), dataVec2.end());
}

긴 이야기를 짧게 잘라내려면 vector :: insert를 사용하는 방법 4는 bsruth의 시나리오에 가장 적합합니다.

다음은 몇 가지 세부 사항입니다.

방법 1 은 아마도 가장 이해하기 쉬운 것입니다. 배열에서 각 요소를 복사하여 벡터 뒤쪽으로 밀어 넣으면됩니다. 아아, 느립니다. 루프 (복사 기능으로 함축 됨)가 있기 때문에 각 요소는 개별적으로 처리되어야합니다. 배열과 벡터가 연속적인 블록이라는 사실을 기반으로 성능을 향상시킬 수 없습니다.

방법 2방법 1 에 대해 제안 된 성능 향상입니다. 추가하기 전에 배열의 크기를 미리 예약하십시오. 큰 배열의 경우 도움 이 수 있습니다. 그러나 여기서 가장 좋은 조언은 프로파일 링에서 개선을 얻을 수 있다고 제안하지 않는 한 예약을 사용하지 않는 것입니다 (또는 반복기가 무효화되지 않도록해야 함). Bjarne은 동의합니다 . 덧붙여서, 나는이 방법 이 방법 1보다 규칙적으로 현저히 느린 이유를 포괄적으로 설명하기 위해 애 쓰고 있지만 대부분의 시간 이 가장 느리다 는 것을 발견했습니다 ...

방법 3 은 구식 해결책입니다. 문제에 C를 던져보세요! POD 유형에 대해 빠르고 잘 작동합니다. 이 경우 memcpy가 벡터의 경계 밖에서 작동하고 크기가 변경되었음을 벡터에 알릴 방법이 없기 때문에 resize를 호출해야합니다. 추악한 솔루션 (바이트 복사!)과는 별개 로 POD 유형에만 사용할 수 있음을 기억하십시오 . 이 솔루션을 사용하지 않을 것입니다.

방법 4 가 가장 좋은 방법입니다. 의미가 명확하고 (보통) 가장 빠르며 모든 개체에 대해 작동합니다. 이 응용 프로그램에이 방법을 사용하는 데 단점이 없습니다.

방법 5 는 방법 4에 대한 조정입니다. 배열을 벡터에 복사 한 다음 추가합니다. 좋은 옵션-일반적으로 빠르고 명확합니다.

마지막으로, 배열 대신 벡터를 사용할 수 있다는 것을 알고 계십니까? 함수가 c 스타일 배열을 예상하는 경우에도 벡터를 사용할 수 있습니다.

vector<char> v(50); // Ensure there's enough space
strcpy(&v[0], "prefer vectors to c arrays");

누군가가 도움이되기를 바랍니다!


6
"& dataArray [dataArraySize]"를 안전하고 이식 가능하게 참조 할 수는 없습니다. 끝을 지나는 포인터 / 반복자를 역 참조하는 것입니다. 대신 dataArray + dataArraySize라고 말하면 포인터를 먼저 역 참조하지 않고도 포인터를 얻을 수 있습니다.
Drew Hall

2
@Drew : 예, 적어도 C에서는 할 수 있습니다. &expr평가하지 않는 것으로 정의되어 expr있으며 주소 만 계산합니다. 그리고 포인터 하나의 마지막 요소 과거도 완벽하게 유효합니다.
Roland Illig

2
방법 4를 2로 시도해 보셨습니까? 즉, 삽입하기 전에 공간을 확보합니다. 데이터 크기가 크면 다중 삽입에는 다중 재 할당이 필요한 것 같습니다. 크기를 미리 알기 때문에 삽입하기 전에 재 할당 할 수 있습니다.
Jorge Leitao

2
@MattyT 방법 5의 요점은 무엇입니까? 데이터의 중간 복사본을 만드는 이유는 무엇입니까?
Ruslan

2
나는 개인적으로 포인터로 자동 감소하는 배열로부터 이익을 얻고 dataVec.insert(dataVec.end(), dataArray, dataArray + dataArraySize);싶습니다. – 나에게 훨씬 더 명확하게 보입니다. 컴파일러가 벡터를 다시 최적화 할 수없는 한 방법 5에서도 얻을 수있는 것은없고 비효율적으로 보입니다.
Aconcagua

37

수행중인 모든 작업이 기존 데이터를 교체하는 것이라면이를 수행 할 수 있습니다.

std::vector<int> data; // evil global :)

void CopyData(int *newData, size_t count)
{
   data.assign(newData, newData + count);
}

1
이해하기 쉽고 확실히 가장 빠른 솔루션입니다 (단지이면에있는 memcpy 일뿐입니다).
Don Scott

deta.assign이 data.insert보다 빠릅니까?


10

내 답변 만 수정할 수 있으므로 내 질문에 대한 다른 답변에서 복합 답변을 만들 것입니다. 답변 해주신 모든 분들께 감사드립니다.

std :: copy를 사용 하면 여전히 백그라운드에서 반복되지만 코드를 입력 할 필요는 없습니다.

int foo(int* data, int size)
{
   static std::vector<int> my_data; //normally a class variable
   std::copy(data, data + size, std::back_inserter(my_data));
   return 0;
}

일반 memcpy 사용 . 이것은 아마도 기본 데이터 유형 (예 : int)에 가장 잘 사용되지만 더 복잡한 구조체 또는 클래스 배열에는 사용되지 않습니다.

vector<int> x(size);
memcpy(&x[0], source, size*sizeof(int));

이 접근 방식을 권장하려고했습니다.
mmocny

크기를 미리 알고 back_inserter를 사용하지 않는 경우 벡터의 크기를 미리 조정하는 것이 더 효율적일 가능성이 높습니다.
luke

my_data.reserve (size)를 추가 할 수 있습니다.
David Nehme

내부적으로 이것은 당신이 피하고 싶은 일을 정확히 수행하고 있음을 유의하십시오. 그것은 비트를 복사하는 것이 아니라 단지 반복하고 push_back ()을 호출하는 것입니다. 코드 입력 만 피하고 싶었나요?
mmocny

1
벡터 생성자를 사용하여 데이터를 복사하지 않습니까?
Martin York

3

memcpy를 피하십시오. 꼭 필요한 경우가 아니면 포인터 작업을 엉망으로 만들 이유가 없습니다. 또한 int와 같은 POD 유형에서만 작동하지만 구성이 필요한 유형을 처리하는 경우 실패합니다.


8
실제로 해결책을 제안하지 않기 때문에 다른 답변 중 하나에 대한 의견 일 수 있습니다.
finnw

3
int dataArray[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };//source

unsigned dataArraySize = sizeof(dataArray) / sizeof(int);

std::vector<int> myvector (dataArraySize );//target

std::copy ( myints, myints+dataArraySize , myvector.begin() );

//myvector now has 1,2,3,...10 :-)

2
이 코드 스 니펫은 환영하며 약간의 도움을 줄 수 있지만 문제를 해결하는 방법이유에 대한 설명포함하면 크게 개선 될 것 입니다. 지금 질문하는 사람뿐만 아니라 미래에 독자를 위해 질문에 답하고 있다는 것을 기억하십시오! 제발 편집 설명을 추가하고, 제한 및 가정이 적용 무엇의 표시를 제공하는 답변을.
Toby Speight

4
잠깐, 뭐야 myints?
mavavilj

2

또 다른 대답은 그 사람이 "내 함수가 몇 번 호출 될지 모르겠습니다."라고 말 했으므로 벡터 삽입 방법을 사용하여 벡터 끝에 값 배열을 추가 할 수 있습니다.

vector<int> x;

void AddValues(int* values, size_t size)
{
   x.insert(x.end(), values, values+size);
}

나는 벡터의 구현이 반복기 유형과 유형 자체를 기반으로 값을 삽입하는 가장 좋은 방법을 최적화 할 수 있어야하기 때문에 이런 방식을 좋아합니다. stl 구현에 대해 다소 답변하고 있습니다.

가장 빠른 속도를 보장해야하고 유형이 POD 유형이라는 것을 알고 있다면 Thomas의 대답에서 크기 조정 방법을 권장합니다.

vector<int> x;

void AddValues(int* values, size_t size)
{
   size_t old_size(x.size());
   x.resize(old_size + size, 0);
   memcpy(&x[old_size], values, size * sizeof(int));
}

1

위에 제시된 방법 외에도 std :: Vector.reserve (), std :: Vector.resize () 중 하나를 사용하거나 크기에 맞게 벡터를 구성하여 벡터에 충분한 요소가 있는지 확인해야합니다. 데이터를 보관합니다. 그렇지 않으면 메모리가 손상됩니다. 이것은 std :: copy () 또는 memcpy ()에 해당됩니다.

이것이 vector.push_back ()을 사용하는 이유이며 벡터의 끝을 지나서 쓸 수 없습니다.


back_inserter를 사용하는 경우 복사 할 벡터의 크기를 미리 예약 할 필요가 없습니다. back_inserter는 push_back ()을 수행합니다.
John Dibling

0

벡터의 항목이 얼마나 큰지 알고 있다고 가정합니다.

std::vector<int> myArray;
myArray.resize (item_count, 0);
memcpy (&myArray.front(), source, item_count * sizeof(int));

http://www.cppreference.com/wiki/stl/vector/start


그것은 std :: vector의 구현에 의존하지 않습니까?
ReaperUnreal

그건 정말 나쁘다! 배열을 두 번, 하나는 '0'으로 채운 다음 적절한 값으로 채 웁니다. 그냥하세요 : std :: vector <int> myArray (source, source + item_count); 컴파일러를 신뢰하여 memcpy를 생성하십시오!
Chris Jefferson

컴파일러가 __memcpy_int_aligned를 생성하도록 신뢰하십시오. 더 빨라야합니다
MSalters
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.