Πώς να διαγράψετε και να διαγράψετε δείκτες σε αντικείμενα αποθηκεύονται σε ένα φορέα;

ψήφοι
27

Έχω ένα φορέα που αποθηκεύει δείκτες σε πολλά αντικείμενα instantiated δυναμικά, και προσπαθώ να επαναλάβει μέσω του φορέα και την κατάργηση ορισμένων στοιχείων (αφαίρεση από το φορέα και να καταστρέψει αντικείμενο), αλλά είμαι έχοντας προβλήματα. Εδώ είναι τι μοιάζει:

    vector<Entity*> Entities;
    /* Fill vector here */
    vector<Entity*>::iterator it;
    for(it=Entities.begin(); it!=Entities.end(); it++)
        if((*it)->getXPos() > 1.5f)
            Entities.erase(it);

Όταν κάποιο από τα αντικείμενα οντότητας φτάσετε στο xPos> 1.5, οι συντριβές προγράμματος με ένα λάθος ισχυρισμό ... Όποιος ξέρει τι κάνω λάθος;

Είμαι με τη χρήση VC ++ 2008.

Δημοσιεύθηκε 13/06/2009 στις 18:27
πηγή χρήστη
Σε άλλες γλώσσες...                            


5 απαντήσεις

ψήφοι
0

Μόλις τροποποιήσετε το φορέα, όλα τα εκκρεμή επαναλήπτες γίνει άκυρη. Με άλλα λόγια, δεν μπορείτε να τροποποιήσετε το φορέα, ενώ είστε επανάληψη μέσα από αυτό. Σκεφτείτε τι κάνει στη μνήμη και θα δείτε γιατί. Υποψιάζομαι ότι διεκδικούν σας είναι «άκυρη επαναλήπτη» διεκδικούν.

std :: διάνυσμα :: διαγραφής () επιστρέφει έναν επαναλήπτη που θα πρέπει να χρησιμοποιήσετε για να αντικαταστήσει εκείνο που χρησιμοποιούσατε. Δείτε εδώ .

Απαντήθηκε 13/06/2009 στις 18:34
πηγή χρήστη

ψήφοι
2
if((*it)->getXPos() > 1.5f)
{
   delete *it;
   it = Entities.erase(it);
}
Απαντήθηκε 13/06/2009 στις 18:34
πηγή χρήστη

ψήφοι
37

Θα πρέπει να είστε προσεκτικοί γιατί erase()θα ακυρώσει τις υπάρχουσες επαναλήπτες. Ωστόσο, ir επιστρέφει ένα νέο έγκυρο iterator που μπορείτε να χρησιμοποιήσετε:

for ( it = Entities.begin(); it != Entities.end(); )
   if( (*it)->getXPos() > 1.5f )
      delete * it;  
      it = Entities.erase(it);
   }
   else {
      ++it;
   }
}
Απαντήθηκε 13/06/2009 στις 18:40
πηγή χρήστη

ψήφοι
8

Το «σωστό» τρόπο για να γίνει αυτό είναι με τη χρήση ενός αλγόριθμου:

#include <algorithm>
#include <functional>

// this is a function object to delete a pointer matching our criteria.
struct entity_deleter
{
    void operator()(Entity*& e) // important to take pointer by reference!
    { 
        if (e->GetXPos() > 1.5f)
        {
            delete e;
            e = NULL;
        }
}

// now, apply entity_deleter to each element, remove the elements that were deleted,
// and erase them from the vector
for_each(Entities.begin(), Entities.end(), entity_deleter());
vector<Entity*>::iterator new_end = remove(Entities.begin(), Entities.end(), static_cast<Entity*>(NULL));
Entities.erase(new_end, Entities.end());

Τώρα ξέρω τι σκέφτεστε. Σκέφτεστε ότι ορισμένες από τις άλλες απαντήσεις είναι μικρότερη. Αλλά, (1) η μέθοδος αυτή συνήθως συγκεντρώνει την ταχύτερη κώδικα - προσπαθήστε να συγκρίνεται, (2) είναι η «σωστή» δρόμο STL, (3) υπάρχουν λιγότερες πιθανότητες για ανόητα λάθη, και (4) είναι πιο εύκολο να διαβάσει αφού μπορείτε να διαβάσετε τον κωδικό STL. Αξίζει εκμάθηση προγραμματισμού STL, και σας προτείνω να ελέγξετε μεγάλο βιβλίο «Αποτελεσματική STL» Scott Meyer, η οποία έχει τα φορτία των συμβουλές STL για αυτού του είδους τα πράγματα.

Ένα άλλο σημαντικό σημείο είναι ότι με το να μην διαγράφοντας τα στοιχεία μέχρι το τέλος της λειτουργίας, τα στοιχεία δεν πρέπει να ανακατεύονται γύρω. GMAN ήταν προτείνοντας να χρησιμοποιηθεί μια λίστα για να αποφευχθεί αυτό, αλλά χρησιμοποιώντας αυτή τη μέθοδο, ολόκληρη η λειτουργία είναι O (n). κωδικός του Neil ανωτέρω, σε αντίθεση, είναι O (n ^ 2), δεδομένου ότι η αναζήτηση είναι O (n) και απομάκρυνση είναι O (n).

Απαντήθηκε 13/06/2009 στις 19:03
πηγή χρήστη

ψήφοι
0

Το κύριο πρόβλημα είναι ότι οι περισσότεροι επαναλήπτες δοχείο STL δεν υποστηρίζουν την προσθήκη ή αφαίρεση στοιχείων στο δοχείο. Μερικοί θα ακυρώσει όλες τις επαναλήπτες, κάποιοι θα ακυρωθεί μόνο ένα iterator που δείχνει σε ένα στοιχείο που αφαιρείται. Μέχρι να πάρετε μια καλύτερη αίσθηση για το πώς λειτουργούν κάθε ένα από τα δοχεία, θα πρέπει να είστε προσεκτικοί για να διαβάσετε την τεκμηρίωση σχετικά με το τι μπορεί και τι δεν μπορεί να κάνει σε ένα δοχείο.

δοχεία STL δεν επιβάλλουν μια συγκεκριμένη εφαρμογή, αλλά ένας φορέας συνήθως υλοποιείται από μία συστοιχία κάτω από την κουκούλα. Αν αφαιρέσετε ένα στοιχείο από την αρχή, όλα τα άλλα στοιχεία μετακινήθηκαν. Αν είχατε ένα iterator που δείχνουν προς ένα από τα άλλα στοιχεία θα μπορούσε τώρα να δείχνει στο στοιχείο μετά το παλιό στοιχείο. Εάν προσθέσετε ένα στοιχείο, η σειρά μπορεί να χρειαστεί να αλλάξει το μέγεθός τους, έτσι ώστε μια νέα σειρά γίνεται, την παλιά πράγματα αντιγραφεί, και τώρα iterator σας είναι στραμμένη προς την παλιά έκδοση του φορέα, η οποία είναι κακό.

Για το πρόβλημά σας θα πρέπει να είναι ασφαλές να επαναλάβει μέσω του φορέα προς τα πίσω και να αφαιρέσετε στοιχεία καθώς πηγαίνετε. Θα είναι επίσης ελαφρώς πιο γρήγορα, από τη στιγμή που συνηθίζει να κινείται γύρω από τα στοιχεία που πρόκειται να διαγράψετε αργότερα.

vector<Entity*> Entities;
/* Fill vector here */
vector<Entity*>::iterator it;
for(it=Entities.end(); it!=Entities.begin(); ){
  --it;
  if(*(*it) > 1.5f){
   delete *it;
   it=Entities.erase(it);
  }
}
Απαντήθηκε 13/06/2009 στις 19:04
πηγή χρήστη

Cookies help us deliver our services. By using our services, you agree to our use of cookies. Learn more