c++ - 전달 - 포인터 벡터 delete




C++:객체의 벡터 대 새로운 객체의 포인터 벡터? (3)

글쎄, 그것은 당신이 당신의 벡터로 무엇을하려하는지에 달려 있습니다.

포인터를 사용하지 않으면 전달 된 객체의 복사본 이 벡터에 저장됩니다. 단순한 개체이거나 저장 공간을 추적하는 데 신경 쓰고 싶지 않은 경우 정확히 원하는 것일 수 있습니다. 복잡한 작업이거나 매우 시간이 많이 걸리는 작업 인 경우에는 해당 작업을 한 번만 수행하고 포인터를 벡터에 전달하는 것이 좋습니다.

샘플 소프트웨어 렌더러를 작성하여 C ++ 기술을 향상시키고 자합니다. 3D 공간에서 점으로 구성된 객체를 가져 와서 2D 뷰포트에 매핑하고 각 점에 대해 다양한 크기의 원을 그립니다. 어떤게 더 좋아:

class World{
    vector<ObjectBaseClass> object_list;
public:
    void generate(){
        object_list.clear();
        object_list.push_back(DerivedClass1());
        object_list.push_back(DerivedClass2());

또는...

class World{
    vector<ObjectBaseClass*> object_list;
public:
    void generate(){
        object_list.clear();
        object_list.push_back(new DerivedClass1());
        object_list.push_back(new DerivedClass2());

?? 벡터가 자동으로 DerivedClass 소멸자를 첫 번째 예제에서는 호출하지만 두 번째에서는 호출하지 않기 때문에 두 번째 예제에서 포인터를 사용하여 새 객체를 작성하면 벡터 사용 지점이 무너지지는 않습니까? 벡터를 사용할 때 액세스 메소드를 사용하는 한 메모리 관리 자체를 처리하기 때문에 새로운 객체에 대한 포인터가 필요합니까? 이제 내가 세상에 또 다른 방법이 있다고 가정 해 봅시다.

void drawfrom(Viewport& view){
    for (unsigned int i=0;i<object_list.size();++i){
        object_list.at(i).draw(view);
    }
}

이 메서드를 호출하면 세계 목록의 모든 개체에 대해 그리기 메서드가 실행됩니다. 파생 클래스가 자신 만의 draw () 버전을 가질 수 있기를 바랍니다. 메서드 선택기 (->)를 사용하려면 포인터가 포인터가 될 필요가 있습니까?


명시 적으로 C ++을 향상시키고 싶다고 말했기 때문에 Boost 사용하는 것이 좋습니다. 이렇게하면 세 가지 방법으로 문제를 해결할 수 있습니다.

shared_ptr 사용

shared_ptr 사용하면 다음과 같이 벡터를 선언 할 수 있습니다.

std::vector< boost::shared_ptr< ObjectBase > > object_list;

그리고 이것을 다음과 같이 사용하십시오 :

typedef std::vector< boost::shared_ptr< ObjectBase > >::iterator ObjectIterator;

for ( ObjectIterator it = object_list.begin(); it != object_list.end(); it++ )
    (*it)->draw(view);

이것은 당신에게 다형성을 주며 포인터의 일반적인 벡터처럼 사용되지만 shared_ptr 은 메모리 관리를 수행하여 객체를 참조하는 마지막 shared_ptr 이 파괴 될 때 객체를 파괴합니다.

C ++ 11에 대한 참고 사항 : C ++ 11에서 shared_ptrstd::shared_ptrstd::shared_ptr 일부가되었으므로 Boost는 더 이상이 방법에 필요하지 않습니다. 그러나 정말로 공유 소유권이 필요하지 않으면 C ++ 11에서 새로 도입 된 std::unique_ptr 을 사용하는 것이 좋습니다.

ptr_vector 사용

ptr_vector 를 사용하면 다음과 같이 할 수 있습니다.

boost::ptr_vector< ObjectBase > object_list;

그리고 이것을 다음과 같이 사용하십시오 :

typedef boost::ptr_vector< ObjectBase >::iterator ObjectIterator;

for ( ObjectIterator it = object_list.begin(); it != object_list.end(); it++ )
    (*it)->draw(view);

이것은 포인터의 법선 벡터처럼 다시 사용되지만 이번에는 ptr_vector 객체의 수명을 관리합니다. 첫 번째 접근 방식과의 차이점은 벡터가 파손되면 객체가 파괴된다는 것입니다. 반면 위에 참조 된 다른 shared_ptr 있으면 컨테이너보다 더 오래 살 수 있습니다.

reference_wrapper 사용하기

ptr_vector 를 사용하면 다음과 같이 선언 할 수 있습니다.

std::vector< boost::reference_wrapper< ObjectBase > > object_list;

그리고 다음과 같이 사용하십시오 :

typedef std::vector< boost::reference_wrapper< ObjectBase > >::iterator 
    ObjectIterator;

for ( ObjectIterator it = object_list.begin(); it != object_list.end(); it++ )
    it->draw(view);

위의 방법과 마찬가지로 이터레이터를 먼저 참조 할 필요가 없습니다. 그러나 이것은 객체의 수명이 다른 곳에서 관리되고 vector 의 수명보다 길 경우에만 작동합니다.

C ++ 11에 대한 참고 사항 : reference_wrapper 는 C ++ 11에서 표준화되었으며 이제 Boost없이 std::reference_wrapper 로 사용할 수 있습니다.

Maciej H 의 대답에서 지적한 것처럼, 첫 번째 방법은 객체 조각을 만듭니다. 일반적으로 컨테이너를 사용할 때 iterators 를 살펴볼 수 있습니다.


첫 번째 질문에 관해서는, 동적으로 할당 된 객체가 아니라 자동으로 할당 된 객체를 사용하는 것이 좋습니다 (즉, 포인터를 저장 하지 말 것). 문제의 유형에 대해 복사 생성 및 할당이 가능하고 너무 비싸지 않는 한 일반적으로 선호됩니다 .

객체를 복사하거나 할당 할 수 없다면 어쨌든 std::vector 객체를 직접 넣을 수 없으므로 문제는 해결할 수 없습니다. 복사 및 / 또는 할당 작업이 많은 경우 (예 : 개체가 많은 양의 데이터를 저장하는 경우) 효율성을 위해 포인터를 저장하는 것이 좋습니다. 그렇지 않으면, 일반적으로 언급 한 이유와 정확히 일치하는 포인터를 저장하지 않는 것이 좋습니다 (자동 할당 취소)

귀하의 두 번째 질문에 관해서는 그렇습니다. 포인터를 저장하는 또 다른 유효한 이유입니다. 동적 디스패치 (가상 메서드 호출)는 포인터 및 참조에서만 작동하며 std::vector 참조를 저장할 수 없습니다. 동일한 벡터에 여러 다형성 유형의 객체를 저장해야하는 경우 슬라이스를 방지하기 위해 포인터를 저장 해야합니다 .





vector