jak poprawnie poruszać się po kontenerze vector?

0

Witam ponownie :)
Mam 2 kontenery:
a) jeden zawiera wszystkie checkboxy
b) drugi zawiera checkboxy "kliknięte"

Pisze funkcję usuwającą wszystkie elementy, które zaznaczyliśmy.

void CreateVList::ElementDeleteSelected(){
    vector<CreateVCheckBox *>::iterator it = Lista_Zaznaczone.begin();
    while(it != Lista_Zaznaczone.end())
    {
        if(*it != NULL)
         ElementDelete(*it);
        ++it;
    }
} 

oraz

bool CreateVList::ElementDelete(CreateVCheckBox *wsk){
    if(!wsk)
     return false;

    vector<CreateVCheckBox *>::iterator it = Lista.begin();
    while(it != Lista.end())
    {
        if(*it == wsk)
        {
            wsk->Destroy();
            Lista.erase(it);
            it = Lista_Zaznaczone.begin();
            while(it != Lista_Zaznaczone.end())
            {
                if(*it == wsk)
                 Lista_Zaznaczone.erase(it);
                ++it;
            }
            return true;
        }
        ++it;
    }
    return false;
}

Program mi się jednak wysypuje. Próbuje dojść czemu i jest w sumie w pewnej kwestii dla mnie niejasna sytuacja.


a. Mam elementy 1 2 3 4 5.
b. Iterator wskazuje na element 3.
c. Usuwam element 2.
Na który element wskazuje iterator???
2. ---------
a. Mam elementy 1 2 3 4 5.
b. Iterator wskazuje na element 3.
c. Usuwam element 3.
Na który element wskazuje iterator???

5

Dokumentacja to nie jest taka straszna bestia, która gryzie w przypadku użycia.

http://en.cppreference.com/w/cpp/container/vector/erase

Return value

Iterator following the last removed element. If the iterator pos refers to the last element, the end() iterator is returned.

Inaczej mówiąc: jeśli używasz erase i chcesz nadal korzystać z używanego iteratora, lepiej nadpisz go tym zwróconym przez funkcję.

Ponadto: dlaczego nie używasz auto? Wygodniej Ci pisać std::vector<ACoMiTamZaKrotkoNieMozeBycPrzeciez>::iterator?

0
void CreateVList::ElementDeleteSelected()
{
	for (auto v : Lista_Zaznaczone)
		Lista.erase(std::remove(Lista.begin(), Lista.end(), v), Lista.end());
}
1
  1. Rozważyłbym używanie smart-pointers. Ze zwykłymi wskaźnikami może być ten problem, że nie wiadomo kto jest ownerem a kto observerem obiektu.
  2. CreateVCheckBox to typ obiektu? Nieintuicyjne bardzo... A i w ElementDeleteSelected właściwie nie wiadomo o co chodzi. Nazwij to jak człowiek, delete_selected_elements.
  3. Dlaczego mieszasz polskie nazwy z normalnymi?
  4. Sprawdzaj sobie zawsze iterator validity jeśli robisz takie operacje na kontenerach. Nie analizując kodu dogłębnie strzelam, że właśnie przez to Ci się to wysypywało.
0

Co rozumiesz przez punkt 4? Mam sprawdzać, czy iterator "nie wskazuje na nic"? Ale to chyba niemozliwe :O

0
vector<int> bleble;
bleble.push_back(1);
bleble.push_back(2);

auto it = next(bleble.begin()); // weźmy iterator na element 2
bleble.erase(bleble.begin()); // usuńmy element 1

"Iterators, pointers and references pointing to position (or first) and beyond are invalidated, with all iterators, pointers and references to elements before position (or first) are guaranteed to keep referring to the same elements they were referring to before the call."
http://www.cplusplus.com/reference/vector/vector/erase/
W tym momencie it (skoro jest ZA usuniętym elementem) wskazuje na niewiadomoco, nie jest poprawnym iteratorem.

1
  1. skoro i tak chcesz usunąć wszystko z jednej a tylko wybrane z innej to:
void CreateVList::ElementDeleteSelected()
{
    for(vector<CreateVCheckBox *>::iterator it=Lista_Zaznaczone.begin();it!=Lista_Zaznaczone.end();++it)
    {
        //if(*it != NULL) // ile razy zamierzasz sprawdzać durną sytuacje?
        ElementDelete(*it);
    }
    Lista_Zaznaczone.clear();
} 

bool CreateVList::ElementDelete(CreateVCheckBox *wsk)
{
    //if(!wsk) return false;  // nie przyspieszaj obsługi sytuacji durnych kosztem każdej normalnej
    for(vector<CreateVCheckBox *>::iterator it=Lista.begin();it != Lista.end();++it)
    {
        if(*it==wsk)
        {
            wsk->Destroy();
            Lista.erase(it);
            return true;
        }
    }
    return false;
}
  1. ale jeżeli naprawdę wg jakiegoś warunku:
for(vector<CreateVCheckBox *>::iterator it=Lista.begin();it != Lista.end();)
{
    if(jakis_warunek_ktory_spelnia_kilka_elementow()) it=Lista.erase(it);
    else ++it;
}
0

Ok to już wiem, gdzie mam błąd. Postanowiłem usunąć część kody bo narobiłem syfu a syf powoduje potem więcej syfu i mam takie deklaracje:

vector<CreateVCheckBox *> Lista;
vector<CreateVCheckBox *> Lista_Zaznaczone;
vector<CreateVCheckBox *> Lista_Wyswietl;

bool ElementAdd(vector<CreateVCheckBox *> &List, vector<CreateVCheckBox *>::iterator Before, void *Data);
bool ElementDelete(vector<CreateVCheckBox *> &List, vector<CreateVCheckBox *>::iterator it);
void ElementDeleteAll();
bool ElementSelect(vector<CreateVCheckBox *>::iterator newSelect);
bool ElementDeselect(vector<CreateVCheckBox *>::iterator newDeselect);
void ElementDeselectAll();
void ElementDeleteSelected();

vector<CreateVCheckBox *>::iterator SetIterator(vector<CreateVCheckBox *> & List, CreateVCheckBox *wsk); 

Tak skonstruowane funkcje wyglądają chyba dużo lepiej i będą czytelniejsze.
Tylko tu nasuwa sie pytanie. Da się w jakimś warunku sprawdzić czy dany iterator pochodzi od konkretnego kontenera?
Bo na przykład w funkcji ElementAdd() dodając element do Lista muszę jeszcze utworzyć kontrolkę, natomiast w reszcie przekazać tylko wskaźnik.

4

Coś mam wrażenie, że jeśli musisz sprawdzać czy iterator "należy" do konkretnego kontenera to całość jest źle zaprojektowana.

4

Po kiego opakowujesz w durne funkcje normalne elementy listy?

  • Np: ElementDeselectAll(); -> Lista_Zaznaczone.clear(); - wystarczy przemianowac listę w Selected i jest czytelniej.
  • Np: ElementAdd(Lista,before,Data); -> Lista.insert(before,Data);
    To jakiś nonsens.
1
gswidwa napisał(a):

Tylko tu nasuwa sie pytanie. Da się w jakimś warunku sprawdzić czy dany iterator pochodzi od konkretnego kontenera?

Dopowiadając: da się. Tak chociażby.

  vector<int> vec1 = {1, 2, 3};
  vector<int> vec2 = {5, 6, 7};
  auto it1 = vec1.begin();
  for (auto it2 = vec2.begin(); it2 != vec2.end(); ++it2)
    if (it1 == it2)
      nalezy;

Ale znowu - trzeba uważać na iterator validity.</del>
Bzdury.
§ 24.2.5 The domain of == for forward iterators is that of iterators over the same underlying sequence.

Możesz zrobić coś takiego:

  vector<int> vec1 = {1, 2, 3};
  vector<int> vec2 = {5, 6, 7};
  auto it1 = vec1.begin();
  for (auto it2 = vec2.begin(); it2 != vec2.end(); ++it2)
    if (&*it1 == &*it2)
      nalezy;

Ale tutaj z kolei jeśli w vectorze są obiekty, które przeładowują swój operator& (adres w pamięci obiektu), to nie poleci.

0

okejka :) To ja się biorę za poprawianie tego wszystkiego, dziekuję wszystkim uprzejmie :)

0
pingwindyktator napisał(a):

Ale znowu - trzeba uważać na iterator validity.

Przekombinowałeś: http://ideone.com/AdBDrq

#include <vector>
#include <iostream>
using namespace std;

int main()
  {
   vector<int> vec1={1, 2, 3};
   vector<int> vec2={5, 6, 7};
   auto i1a=vec1.begin();
   auto i2a=vec2.begin();
   auto i1b=vec1.begin()+1;
   auto i2b=vec2.begin()+1;
   auto i1c=vec1.begin()+2;
   auto i2c=vec2.begin()+2;
   for(auto i=vec2.begin();i!=vec2.end();++i)
     {
      if(i==i1a) cout<<"1a"<<endl;
      else if(i==i2a) cout<<"2a"<<endl;
      else if(i==i1b) cout<<"1b"<<endl;
      else if(i==i2b) cout<<"2b"<<endl;
      else if(i==i1c) cout<<"1c"<<endl;
      else if(i==i2c) cout<<"2c"<<endl;
     }
   return 0;
  }

To:

pingwindyktator napisał(a):

§ 24.2.5 The domain of == for forward iterators is that of iterators over the same underlying sequence.
wcale nie przeszkadza.

Ale jeżeli ktoś potrzebuje sprawdzać do którego wektora należy iterator to wg mnie coś jest w projekcie popieprzone czarnym pieprzem.

2

@_13th_Dragon nie rozumiesz. Nie możesz porównać za pomocą operator == dwóch iteratorów z różnych kontenerów. Prawdą jest, że jeśli te iteratory są z tego samego kontenera, to to zadziała. Jeśli nie są, to nie wiadomo co się stanie.
EDIT: zdaje się, że jedyne poprawne rozwiązanie to

  vector<int> vec1 = {1, 2, 3};
  vector<int> vec2 = {5, 6, 7};
  auto it1 = vec1.begin();
  for (auto it2 = vec2.begin(); it2 != vec2.end(); ++it2)
    if (std::addressof(*it1) == std::addressof(*it2))
      nalezy;
0

Już sobie poradziłem z tym. Okazało się że wcale tego nie potrzebuje .

2

Nikt tego jeszcze nie powiedział, ale: iteruj od konca do początku jak usuwasz. W innym wypadku twoj iterator bedzie wskazywal na cos co bylo znisczone.

0

Funkcje mam już pokończone, jedyna rzecz, która mi działa po łebkach to o zgrozo - Tworzenie kontrolek. Są 2 warianty:
a) Tworzę jedną kontrolkę
b) Tworzę wiele kontrolek

Elementy tworzę za pomocą funkcji:

ElementAdd(Lista.end(), NULL);

definicja ElementAdd: http://pastebin.com/YrPQQZuP
definicja GetIterator: http://pastebin.com/qQJYb23z

Opis problemu:
a) Gdy tworzę jedną kontrolkę kontrolka jest tworzona prawidłowo, kontrolkę widać, kontrolkę można klikać i po kliknięciu wysyła swoją nazwę do EditBoxa. Czyli jest okej.
b) Gdy tworzę więcej niż 1 kontrolkę pierwsza kontrolka nieoczekiwanie znika (mogę z niej dalej sukcesywnie odczytywać wartości więc klasa kontrolki jest na swoim miejscu (przynajmniej w konstruktorze listy... (t.j. tam gdzie tworzę kontrolki)). Jak najadę na miejsce, gdzie powinna być pierwsza kontrolka program się wysypuje. Jeżeli chodzi o resztę kontrolek, niezależnie ile ich jest wszystkie działają prócz tej jednej.

Wynik programu dla jednego elementu:
http://imgur.com/IEA3ag4
Wynik programu dla wielu elementów
http://imgur.com/uf8paVy
I jak najedziecie myszką na ten obszar, gdzie powinna być kontrolka o ID 101 program si wysypuje, ale dalej mogę czytać tekst z kontrolki o id 101:
http://imgur.com/cjxF8qx

Juz 6h spędzam na znalezieniem problemu z przerwą na sen :( Pomocy :(

1 użytkowników online, w tym zalogowanych: 0, gości: 1