2008-12-10 40 views
7

Próbuję pozbyć się std :: vector za pomocą boost :: ptr_vector. Teraz próbuję usunąć element z jednego i usunąć usunięty element. Najbardziej oczywiste dla mnie było:Jak usunąć elementy z boost :: ptr_vector

class A 
{ int m; }; 

boost::ptr_vector<A> vec; 
A* a = new A; 
vec.push_back(a); 
vec.erase(a); 

Ale to nawet nie będzie się kompilować (zobacz poniżej pełny komunikat o błędzie). Próbowałem wymazać/usunąć idiom jak na std :: vector, ale wszystkie algorytmy boost :: ptr_vector okazały się nieco inne od tych w std :: vector.

więc moje pytania:

  • Jak mogę usunąć wskaźnik z punktu A ptr_vector?
  • Czy nadal muszę ręcznie usunąć() element, który usunąłem?

błąd kompilatora:

1>------ Build started: Project: ptr_vector_test, Configuration: Debug Win32 ------ 
1>Compiling... 
1>ptr_vector_test.cpp 
1>c:\users\rvanhout\svn\trunk\thirdparty\boost\range\const_iterator.hpp(37) : error C2825: 'C': must be a class or namespace when followed by '::' 
1>  c:\users\rvanhout\svn\trunk\thirdparty\boost\mpl\eval_if.hpp(63) : see reference to class template instantiation 'boost::range_const_iterator<C>' being compiled 
1>  with 
1>  [ 
1>   C=A * 
1>  ] 
1>  c:\users\rvanhout\svn\trunk\thirdparty\boost\range\iterator.hpp(63) : see reference to class template instantiation 'boost::mpl::eval_if_c<C,F1,F2>' being compiled 
1>  with 
1>  [ 
1>   C=true, 
1>   F1=boost::range_const_iterator<A *>, 
1>   F2=boost::range_mutable_iterator<A *const > 
1>  ] 
1>  c:\users\rvanhout\svn\trunk\thirdparty\boost\ptr_container\detail\reversible_ptr_container.hpp(506) : see reference to class template instantiation 'boost::range_iterator<C>' being compiled 
1>  with 
1>  [ 
1>   C=A *const 
1>  ] 
1>  c:\tmp\ptr_vector_test\ptr_vector_test.cpp(21) : see reference to function template instantiation 'boost::void_ptr_iterator<VoidIter,T> boost::ptr_container_detail::reversible_ptr_container<Config,CloneAllocator>::erase<A*>(const Range &)' being compiled 
1>  with 
1>  [ 
1>   VoidIter=std::_Vector_iterator<void *,std::allocator<void *>>, 
1>   T=A, 
1>   Config=boost::ptr_container_detail::sequence_config<A,std::vector<void *,std::allocator<void *>>>, 
1>   CloneAllocator=boost::heap_clone_allocator, 
1>   Range=A * 
1>  ] 
1>c:\users\rvanhout\svn\trunk\thirdparty\boost\range\const_iterator.hpp(37) : error C2039: 'const_iterator' : is not a member of '`global namespace'' 
1>c:\users\rvanhout\svn\trunk\thirdparty\boost\range\const_iterator.hpp(37) : error C2146: syntax error : missing ';' before identifier 'type' 
1>c:\users\rvanhout\svn\trunk\thirdparty\boost\range\const_iterator.hpp(37) : error C2208: 'boost::type' : no members defined using this type 
1>c:\users\rvanhout\svn\trunk\thirdparty\boost\range\const_iterator.hpp(37) : fatal error C1903: unable to recover from previous error(s); stopping compilation 
1>Build log was saved at "file://c:\tmp\ptr_vector_test\Debug\BuildLog.htm" 
1>ptr_vector_test - 5 error(s), 0 warning(s) 
========== Build: 0 succeeded, 1 failed, 0 up-to-date, 0 skipped ========== 

Odpowiedz

9

Cóż, możesz to zrobić również z użyciem std :: vector.

W obu przypadkach kasowanie przyjmuje jako parametr iterator.
Zanim więc będziesz mógł usunąć coś z wektora (lub ptr_vector), musisz go zlokalizować.

Należy również zauważyć, że ptr_vector traktuje zawartość tak, jakby zapisany obiekt nie był wskaźnikiem. Tak więc wyszukiwanie odbywa się za pośrednictwem obiektu.

Więc w zasadzie

std::vector<A>  x; 
std::ptr_vector<A> y; 

// These two object should behave in exactly the same way. 
// The ONLY difference is inserting values which for y are pointers. 
// Y take ownership of the pointer and all subsequent acesses to the 
// members of y look like they are objects 

Przykład:

#include <boost/ptr_container/ptr_vector.hpp> 
#include <vector> 

class A 
{ int m; 
    public: 
    A(int x):m(x) {} 
    bool operator==(A const& rhs) {return m = rhs.m;} 
}; 

int main() 
{ 
    boost::ptr_vector<A> x; 
    x.push_back(new A(1)); 
    x.erase(std::find(x.begin(),x.end(),A(1))); 


    std::vector<A>   y; 
    y.push_back(A(2)); 
    y.erase(std::find(y.begin(),y.end(),A(2))); 

    // To find an exact pointer don't modify the equality. 
    // Use find_if and pass a predicate that tests for a pointer 
    A* a = new A(3); 
    boost:ptr_Vector<A>  z; 
    z.push_back(a); 
    z.erase(std::find_if(y.begin(),y.end(),CheckPointerValue(a)); 
} 

struct CheckPointerValue 
{ 
    CheckPointerValue(A* a):anA(a) {} 
    bool operator()(A const& x) { return &X == anA;} 
    private: 
     A* anA; 
}; 
+0

Myślę, że masz rację co do mojej odpowiedzi poniżej. jeśli rozumiem to poprawnie, domyślny alokator (heap_clone_allocator) klonuje same obiekty podczas ponownego przydzielania). działałoby to przy użyciu view_clone_allocator. Usunąłem moją odpowiedź i przegłosowałem ciebie. dobrze się bawić :) –

+0

Dziękuję, mój problem polegał na tym, że nie zdefiniowałem operatora == dla A * const i który wydaje się konieczny. Aby uzyskać przykład do kompilacji (VC9), musiałem zmienić "A const & rhs" na "A * const & rhs" i odpowiednio zmienić zawartość ciała. To sprawia, że ​​jest trudny w użyciu z klasami bibliotek. – Roel

+0

Brakuje punktu! Element ptr_vector <> przechowuje obiekt tak, jakby był obiektem. Zamiast perwersji operator równości po prostu odrzuć wskaźnik. Jeśli chcesz znaleźć dokładny wskaźnik, musisz użyć find_if i przekazać predykat. –

1

Można użyć erase_if metody szablonu.

vec.erase_if(predicate()); 
+0

Ale wtedy musiałbym napisać predykat specjalnego przeznaczenia, który w zasadzie jest prostym obiektem funkcji porównawczej? Co za bałagan, jeśli w taki sposób powinienem pracować z ptr_vector, po prostu trzymam się mojego wektora shared_ptr:/ – Roel

+0

Użyj boost :: lambda, aby zapisać predykat jako funkcję lambda (w miejscu). – paxos1977

3

Myślę, że chcesz wywołać .release() na wektorze zamiast wymazywać. To usuwa wpis i kasuje pamięć.

Zobacz sekcję "Nowe funkcje", aby uzyskać szczegółowe informacje w the tutorial, lub sprawdź the reference.

Alternatywnie, musisz pobrać iterator do elementu, aby wywołać funkcję kasowania(), jestem pewien, że A * liczy się pod względem ptr_vector.

+0

Nie - "release" usuwa wskaźnik z pojemnika, a kontener kurczy się, ale obiekt nie jest usuwany. Ma tę samą semantykę, co 'auto_ptr'' release' - po prostu używasz jej do transferu zarządzania. – EML

0

Musisz użyć metody member erase_if z odpowiednim predykatem. Nie ma potrzeby usuwania wskaźnika, kontener ma własność.

struct delete_a { 
    bool operator()(boost::ptr_vector<A>::value_type inA) { 
     return inA == a; 
    } 
} 

vec.erase_if(delete_a()); 

(pamiętać, że jest to tylko przykład wybrany dla uproszczenia, do takiej sytuacji w rzeczywistym kodzie Przypuszczam napisać odpowiedni combo bind/equal_to lub użyj lambda)

lub, alternatywnie, wywołanie zwiastuna na poprawnym iteratorze, jeśli nadal chcesz użyć obiektu.

1

Semantyka ptr_vector jest bardzo podobna do zwykłej vector. Musisz znaleźć element, zanim będziesz mógł go wymazać.

+0

Tak, kasowanie zajmuje iterator, ptr_vector nie jest inny. –

+0

Myślę, że potrzebuję ptr_iterator, ale ten wydaje się być chroniony (chociaż mogłem znaleźć posty na listach mailingowych, w których został użyty, więc domyślam się, że został zabezpieczony później ...). – Roel

1

Ciekawa rzecz: STL::vector<> jest Random Access Container, co oznacza, że ​​wykorzystuje Random Access Iterators.

Więc vec.erase (vec.begin() + N) usunie element o indeksie N.

Zauważ, że ten sposób łamie cały iterator mema i nie będzie można przełączać się między trywialnie wektory i listy ...

Powiązane problemy