Controleren of een iterator geldig is

Is er een manier om te controleren of een iterator (of het nu van een vector, een lijst, een deque…) is (nog steeds) dereferenceable is, d.w.z. niet ongeldig is gemaakt?

Ik heb trycatchgebruikt, maar is er een directere manier om dit te doen?

Voorbeeld: (wat niet werkt)

list<int> l;
for (i = 1; i<10; i++) {
    l.push_back(i * 10);
}
itd = l.begin();
itd++;
if (something) {
    l.erase(itd);
}
/* now, in other place.. check if it points to somewhere meaningful */
if (itd != l.end())
{
    //  blablabla
}

Antwoord 1, autoriteit 100%

Ik neem aan dat je bedoelt “is een iterator geldig”, dat deze niet ongeldig is gemaakt vanwege wijzigingen in de container (bijvoorbeeld invoegen/wissen van/naar een vector). In dat geval, nee, je kunt niet bepalen of een iterator (veilig) dereferenceerbaar is.


Antwoord 2, autoriteit 40%

Zoals jdehaan al zei, als de iterator niet ongeldig is gemaakt en naar een container verwijst, kun je dit controleren door het te vergelijken met container.end().

Houd er echter rekening mee dat als de iterator enkelvoudis — omdat deze niet is geïnitialiseerd of ongeldig is geworden na een muterende bewerking op de container (de iterators van de vector worden ongeldig wanneer u de capaciteit van de vector vergroot , bijvoorbeeld) — de enige bewerking die u erop mag uitvoeren, is toewijzing. Met andere woorden, je kunt niet controleren of een iterator enkelvoud is of niet.

std::vector<int>::iterator iter = vec.begin();
vec.resize(vec.capacity() + 1);
// iter is now singular, you may only perform assignment on it,
// there is no way in general to determine whether it is singular or not

Antwoord 3, autoriteit 20%

Niet-draagbaar antwoord: Ja – in Visual Studio

De STL-iterators van Visual Studio hebben een “debugging”-modus die precies dit doet. Je zou dit niet willen inschakelen in scheepsconstructies (er is overhead), maar handig in gecontroleerde builds.

Lees erover op VC10 hier( dit systeem kan elke release wijzigen en zal dit ook doen, dus zoek de documenten die specifiek zijn voor uw versie).

BewerkenIk moet ook toevoegen: debug-iterators in Visual Studio zijn ontworpen om onmiddellijk te exploderen wanneer je ze gebruikt (in plaats van ongedefinieerd gedrag); om “vragen” van hun staat niet toe te staan.


Antwoord 4, autoriteit 15%

Meestal test je het door te controleren of het anders is dan de end(), zoals

if (it != container.end())
{
   // then dereference
}

Bovendien is het gebruik van uitzonderingsbehandeling voor het vervangen van logica slecht in termen van ontwerp en prestaties. Je vraag is erg goed en het is zeker een vervanging waard in je code. Afhandeling van uitzonderingen, zoals de namen al zeggen, wordt alleen gebruikt voor zeldzame onverwachte problemen.


Antwoord 5, autoriteit 11%

Is er een manier om te controleren of een iterator (of het nu van een vector, een lijst, een deque…) is (nog steeds) dereferenceerbaar is, d.w.z. niet ongeldig is gemaakt?

Nee, dat is er niet. In plaats daarvan moet u de toegang tot de container beheren terwijl uw iterator bestaat, bijvoorbeeld:

  • Uw thread mag de container niet wijzigen (de iterator ongeldig maken) terwijl deze nog steeds een geïnstantieerde iterator voor die container gebruikt

  • Als het risico bestaat dat andere threads de container wijzigen terwijl uw thread itereert, moet uw thread, om dit scenario thread-safe te maken, een soort vergrendeling op de container krijgen (zodat het andere threads van het wijzigen van de container terwijl deze een iterator gebruikt)

Omzeilingen zoals het opvangen van een uitzondering werken niet.

Dit is een specifiek voorbeeld van het meer algemene probleem, “kan ik testen/detecteren of een aanwijzer geldig is?”, waarop het antwoord typisch is “nee, je kunt er niet op testen: in plaats daarvan moet je beheren alle geheugentoewijzingen en verwijderingen om te wetenof een bepaalde aanwijzer nog steeds geldig is”.


Antwoord 6, autoriteit 5%

Proberen en vangen is niet veilig, je zal niet, of in ieder geval zelden gooien als je iterator “out of bounds” is.

wat alemjerus zegt, een iterator kan altijd worden verwijderd. Het maakt niet uit welke lelijkheid eronder ligt. Het is heel goed mogelijk om naar andere geheugengebieden te gaan en naar andere gebieden te schrijven die andere objecten kunnen bevatten. Ik heb naar code gekeken en variabelen zonder specifieke reden zien veranderen. Dat is een bug die heel moeilijk te detecteren is.

Het is ook verstandig om te onthouden dat het invoegen en verwijderen van elementen mogelijk alleverwijzingen, verwijzingen en iterators ongeldig kan maken.

Mijn beste advies zou zijn om je iterators onder controle te houden, en altijd een “eind” iterator bij de hand te houden om te kunnen testen of je aan het “einde van de lijn” bent om zo te zeggen.


Antwoord 7, autoriteit 3%

In sommige STL-containers wordt de huidige iterator ongeldig wanneer u de huidige waarde van de iterator wist. Dit gebeurt omdat de wisbewerking de interne geheugenstructuur van de container- en incrementoperator op bestaande iteratorpunten verandert in een niet-gedefinieerde locatie.

Als je het volgende doet, wordt de iterator geactiveerd voordat deze wordt doorgegeven aan de wisfunctie.

if (something) l.erase(itd++);


Antwoord 8, autoriteit 3%

Is er een manier om te controleren of een iterator dereferenceerbaar is

Ja, met gcc foutopsporingscontainersbeschikbaar als GNU-extensies. Voor std::listkun je in plaats daarvan __gnu_debug::listgebruiken. De volgende code wordt afgebroken zodra geprobeerd wordt een ongeldige iterator te gebruiken. Omdat het debuggen van containers extra overhead met zich meebrengt, zijn ze alleen bedoeld bij het debuggen.

#include <debug/list>
int main() {
  __gnu_debug::list<int> l;
  for (int i = 1; i < 10; i++) {
    l.push_back(i * 10);
  }
  auto itd = l.begin();
  itd++;
  l.erase(itd);
  /* now, in other place.. check if itd points to somewhere meaningful */
  if (itd != l.end()) {
    //  blablabla
  }
}
$ ./a.out 
/usr/include/c++/7/debug/safe_iterator.h:552:
Error: attempt to compare a singular iterator to a past-the-end iterator.
Objects involved in the operation:
    iterator "lhs" @ 0x0x7ffda4c57fc0 {
      type = __gnu_debug::_Safe_iterator<std::_List_iterator<int>, std::__debug::list<int, std::allocator<int> > > (mutable iterator);
      state = singular;
      references sequence with type 'std::__debug::list<int, std::allocator<int> >' @ 0x0x7ffda4c57ff0
    }
    iterator "rhs" @ 0x0x7ffda4c580c0 {
      type = __gnu_debug::_Safe_iterator<std::_List_iterator<int>, std::__debug::list<int, std::allocator<int> > > (mutable iterator);
      state = past-the-end;
      references sequence with type 'std::__debug::list<int, std::allocator<int> >' @ 0x0x7ffda4c57ff0
    }
Aborted (core dumped)

9

Gebruik wis met verhoging:

indien (iets) l.erase (ITD ++);

U kunt de geldigheid van de iterator testen.

Other episodes