std :: vector가 push_back으로 객체를 복사합니까?


169

valgrind로 많은 조사를 한 후에 std :: vector가 push_back하려는 객체의 사본을 만든다는 결론을 내 렸습니다.

정말 맞습니까? 벡터는 복사본없이 객체의 참조 나 포인터를 유지할 수 없습니다!

감사


20
이것이 C ++의 기본 원칙입니다. 객체는 값입니다. 과제는 사본을 만듭니다. 당신이 유형을 수정하지 않는 한 같은 객체를 참조하는 두 변수는 수 없습니다 *또는 &포인터 또는 참조 할 수 있습니다.
Daniel Earwicker

8
@DanielEarwicker push_back은 실제로 참조를합니다. 서명만으로도 사본을 만들지 확실하지 않습니다.
Brian Gordon

3
@BrianGordon-말이 없습니다! 따라서지도 원칙이 필요합니다. 그럼에도 불구하고, 우리의 서명에서 뭔가를 추론 할 수 있습니다 push_back: 그것은한다 const&. 값을 버리거나 (사용할 수 없음) 검색 방법이 있습니다. 그래서 우리는의 서명을 보며 backplain을 반환 &하므로 원래 값이 복사되었거나 const조용히 쫓겨났습니다 (매우 나쁨 : 잠재적으로 정의되지 않은 행동). 따라서 디자이너 vector가 합리적 vector<bool>이라고 생각하면 ( 내구성이 아님) 우리는 그것이 사본을 만든다는 결론을 내립니다.
Daniel Earwicker

답변:


183

예, std::vector<T>::push_back()인수의 복사본을 만들어 벡터에 저장합니다. 벡터의 객체에 대한 포인터를 저장하려면 std::vector<whatever*>대신을 만드십시오 std::vector<whatever>.

그러나 벡터가 참조를 보유하고있는 동안 포인터가 참조하는 객체는 유효하게 유지해야합니다 (RAII 관용구를 사용하는 스마트 포인터는 문제를 해결합니다).


또한 원시 포인터를 사용하는 경우 이제 포인터를 정리해야합니다. 이것을할만한 이유가 없습니다 (어쨌든 생각할 수있는 사람은 아닙니다), 항상 스마트 포인터를 사용해야합니다.
Ed S.

1
즉, 더 많은 정보를 위해 stl 컨테이너에 std :: auto_ptr을 사용해서는 안됩니다. 이유가 잘못 사용되었습니다 stdauto-ptr-with- 표준 컨테이너
OriginalCliche

24
C ++ 11부터 push_back인수가 rvalue 참조 인 경우 복사 대신 이동을 수행합니다. (를 사용하여 객체를 rvalue 참조로 변환 할 수 있습니다 std::move().)
emlai

2
@tuple_cat 의견은 "인수가 rvalue 인 경우"라고 말합니다. (인수를 rvalue 참조로 선언 된 엔티티의 이름 인 경우, 인수가 실제로 좌변과에서 이동되지 않습니다) - 처음 그 실수를했다 "칼 니콜"의 대답에 내 편집을 확인
MM

C ++ 11은 복사 또는 이동 (컨테이너가 제공하는 객체를 생성)을 피하기 위해 사용 하기 때문에 아래 에 대답이 있습니다 emplace_back.
Wormer

34

예, std::vector사본을 저장합니다. vector물체의 예상 수명이 무엇인지 어떻게 알 수 있습니까?

객체의 소유권을 전송하거나 공유하려면 포인터를 사용하십시오 shared_ptr. 부스트 또는 TR1 과 같은 스마트 포인터는 리소스 관리를 용이하게합니다.


3
shared_ptr 사용법을 배우십시오-그들은 당신이 원하는 것을 정확하게합니다. 내가 가장 좋아하는 관용구는 typedef boost :: shared_ptr <Foo> FooPtr; 그런 다음 FooPtrs의 용기를 만드십시오
pm100

3
@ pm100-알고 boost::ptr_vector계십니까?
Manuel

2
나는 또한 class Foo { typedef boost::shared_ptr<Foo> ptr; };그냥 쓰는 데 사용 하고 싶습니다 Foo::ptr.
Rupert Jones

2
@ pm100- shared_ptr정확히 발사되지는 않습니다. 참조 stackoverflow.com/questions/327573stackoverflow.com/questions/701456
다니엘 Earwicker

2
shared_ptr은 소유권을 공유 한 경우 좋지만 일반적으로 과도하게 사용됩니다. unique_ptr 또는 boost scoped_ptr은 소유권이 명확 할 때 훨씬 더 의미가 있습니다.
Nemanja Trifunovic

28

C ++ 11부터 모든 표준 컨테이너 ( std::vector, std::map등)는 이동 의미론을 지원하므로 이제 rvalue를 표준 컨테이너에 전달하고 사본을 피할 수 있습니다.

// Example object class.
class object
{
private:
    int             m_val1;
    std::string     m_val2;

public:
    // Constructor for object class.
    object(int val1, std::string &&val2) :
        m_val1(val1),
        m_val2(std::move(val2))
    {

    }
};

std::vector<object> myList;

// #1 Copy into the vector.
object foo1(1, "foo");
myList.push_back(foo1);

// #2 Move into the vector (no copy).
object foo2(1024, "bar");
myList.push_back(std::move(foo2));

// #3 Move temporary into vector (no copy).
myList.push_back(object(453, "baz"));

// #4 Create instance of object directly inside the vector (no copy, no move).
myList.emplace_back(453, "qux");

또는 다양한 스마트 포인터를 사용하여 대부분 동일한 효과를 얻을 수 있습니다.

std::unique_ptr

std::vector<std::unique_ptr<object>> myPtrList;

// #5a unique_ptr can only ever be moved.
auto pFoo = std::make_unique<object>(1, "foo");
myPtrList.push_back(std::move(pFoo));

// #5b unique_ptr can only ever be moved.
myPtrList.push_back(std::make_unique<object>(1, "foo"));

std::shared_ptr

std::vector<std::shared_ptr<object>> objectPtrList2;

// #6 shared_ptr can be used to retain a copy of the pointer and update both the vector
// value and the local copy simultaneously.
auto pFooShared = std::make_shared<object>(1, "foo");
objectPtrList2.push_back(pFooShared);
// Pointer to object stored in the vector, but pFooShared is still valid.

2
std::make_unique(성가 시게)에서만 사용 가능 C ++에서 14 위입니다. 이 예제를 컴파일하려면 컴파일러에 표준 적합성을 설정하도록 지시하십시오.
Laryx Decidua

5a에서는 auto pFoo =반복을 피하기 위해 사용할 수 있습니다 . 그리고 모두 std::string제거 할 수 있습니다 캐스트 (에 문자열 리터럴에서 암시 적 변환이 있습니다 std::string)
MM

2
@ user465139는 make_unique누군가가 11 컴파일러는 C ++와 함께 붙어 만 약간의 짜증 그래서 쉽게, C ++ 11에서 구현 될 수있다
MM

1
@MM : 참으로. 여기 교과서 구현이 있습니다 :template<typename T, typename... Args> unique_ptr<T> make_unique(Args&&... args) { return unique_ptr<T>{new T{args...}}; }
Laryx Decidua

1
@Anakin-예, 복사해야 할 경우에만 수행해야합니다. std::move()와 함께 사용 하는 경우 std::shared_ptr소유권이 벡터에 전달 된 후 원래 공유 포인터의 포인터가 변경되었을 수 있습니다. 여기를 참조하십시오 : coliru.stacked-crooked.com/a/99d4f04f05e5c7f3
Karl Nicoll

15

std :: vector는 항상 벡터에 저장되는 모든 내용을 복사합니다.

포인터 벡터를 유지하는 경우 포인터의 사본을 만들지 만 포인터가 가리키는 인스턴스는 만들지 않습니다. 큰 객체를 다루는 경우 항상 포인터 벡터를 사용할 수 있습니다. 적절한 유형의 스마트 포인터 벡터를 사용하면 객체 수명 및 메모리 관리를 처리하는 것이 까다로울 수 있기 때문에 안전 목적에 적합합니다.


3
유형에 의존하지 않습니다. 항상 사본을 만듭니다. 포인터가 포인터의 복사 본인 경우
pm100

둘 다 맞아 기술적으로는 항상 사본을 만듭니다. 실제로 객체에 포인터를 전달하면 객체가 아닌 포인터가 복사됩니다. 안전하게 스마트 포인터를 사용해야합니다.
Steven Sudit

1
예, 항상 복사 중입니다. 그러나 OP가 참조하는 "개체"는 클래스 또는 구조체 일 가능성이 높으므로 "개체"를 복사하는 것이 정의에 따라 달라지는지를 언급하고있었습니다. 그래도 나쁜 말을 했어요.
리드 콥시

3

std :: vector는 뒤로 밀고있는 것의 복사본을 만들뿐만 아니라 컬렉션의 정의에 따라 그렇게 할 것이며 벡터 내에서 올바른 복사 의미가없는 객체는 사용할 수 없습니다. 예를 들어 벡터에는 auto_ptr을 사용하지 않습니다.


2

C ++ 11과 관련된 emplace멤버 함수 패밀리는 오브젝트를 컨테이너로 이동하여 오브젝트의 소유권을 이전 할 수있게합니다.

사용 관용구는 다음과 같습니다

std::vector<Object> objs;

Object l_value_obj { /* initialize */ };
// use object here...

objs.emplace_back(std::move(l_value_obj));

lvalue 객체의 이동은 그렇지 않으면 참조 또는 const 참조로 전달되고 이동 생성자는 호출되지 않으므로 중요합니다.


0

사본을 원하지 않으면; 그런 다음 가장 좋은 방법은 포인터 벡터 (또는 동일한 목표를 제공하는 다른 구조)를 사용하는 것입니다. 사본을 원한다면; 직접 push_back ()을 사용하십시오. 다른 선택의 여지가 없습니다.


1
포인터 벡터에 대한 참고 사항 : vector <shared_ptr <obj>>는 vector <obj *>보다 안전하며 shared_ptr은 작년 기준으로 표준의 일부입니다.
rich.e

-1

이것을 알아 내기 위해 많은 valgrind 조사가 필요한 이유는 무엇입니까? 간단한 코드로 직접 증명하십시오.

std::vector<std::string> vec;

{
      std::string obj("hello world");
      vec.push_pack(obj);
}

std::cout << vec[0] << std::endl;  

"hello world"가 인쇄되면 개체가 복사 된 것이어야합니다


4
이것은 증거가 아닙니다. 오브젝트가 복사되지 않은 경우 마지막 명령문은 정의되지 않은 동작이며 hello를 인쇄 할 수 있습니다.
Mat

4
올바른 테스트는 삽입 후 둘 중 하나를 수정하는 것입니다. 그것들이 같은 객체라면 (벡터가 참조를 저장했다면) 둘 다 수정 될 것입니다.
Francesco Dondi
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.