Как стереть & amp; удалить указатели на объекты, хранящиеся в векторе?

Я знаю, что эта ветка довольно старая, но у меня была эта проблема, и я придумал классное решение, которое может быть очень полезным для многих, потому что оно исправляет / расширяет библиотеку Volley по многим аспектам.

Я заметил некоторые неработающие функции Volley:

  • Этот JSONObjectRequest не идеален: в конце вы должны ожидать JSON (см. Response.Listener<JSONObject> ).
  • Как насчет пустых ответов (только с статусом 200)?
  • Что мне делать, если я хочу непосредственно мой POJO из ResponseListener?

Я более или менее скомпилировал множество решений в большом родовом классе, чтобы иметь решение для всей проблемы, которую я цитировал.

  /**
  * Created by laurentmeyer on 25/07/15.
  */
 public class GenericRequest<T> extends JsonRequest<T> {

     private final Gson gson = new Gson();
     private final Class<T> clazz;
     private final Map<String, String> headers;
     // Used for request which do not return anything from the server
     private boolean muteRequest = false;

     /**
      * Basically, this is the constructor which is called by the others.
      * It allows you to send an object of type A to the server and expect a JSON representing a object of type B.
      * The problem with the #JsonObjectRequest is that you expect a JSON at the end.
      * We can do better than that, we can directly receive our POJO.
      * That's what this class does.
      *
      * @param method:        HTTP Method
      * @param classtype:     Classtype to parse the JSON coming from the server
      * @param url:           url to be called
      * @param requestBody:   The body being sent
      * @param listener:      Listener of the request
      * @param errorListener: Error handler of the request
      * @param headers:       Added headers
      */
     private GenericRequest(int method, Class<T> classtype, String url, String requestBody,
                           Response.Listener<T> listener, Response.ErrorListener errorListener, Map<String, String> headers) {
         super(method, url, requestBody, listener,
                 errorListener);
         clazz = classtype;
         this.headers = headers;
         configureRequest();
     }

     /**
      * Method to be called if you want to send some objects to your server via body in JSON of the request (with headers and not muted)
      *
      * @param method:        HTTP Method
      * @param url:           URL to be called
      * @param classtype:     Classtype to parse the JSON returned from the server
      * @param toBeSent:      Object which will be transformed in JSON via Gson and sent to the server
      * @param listener:      Listener of the request
      * @param errorListener: Error handler of the request
      * @param headers:       Added headers
      */
     public GenericRequest(int method, String url, Class<T> classtype, Object toBeSent,
                           Response.Listener<T> listener, Response.ErrorListener errorListener, Map<String, String> headers) {
         this(method, classtype, url, new Gson().toJson(toBeSent), listener,
                 errorListener, headers);
     }

     /**
      * Method to be called if you want to send some objects to your server via body in JSON of the request (without header and not muted)
      *
      * @param method:        HTTP Method
      * @param url:           URL to be called
      * @param classtype:     Classtype to parse the JSON returned from the server
      * @param toBeSent:      Object which will be transformed in JSON via Gson and sent to the server
      * @param listener:      Listener of the request
      * @param errorListener: Error handler of the request
      */
     public GenericRequest(int method, String url, Class<T> classtype, Object toBeSent,
                           Response.Listener<T> listener, Response.ErrorListener errorListener) {
         this(method, classtype, url, new Gson().toJson(toBeSent), listener,
                 errorListener, new HashMap<String, String>());
     }

     /**
      * Method to be called if you want to send something to the server but not with a JSON, just with a defined String (without header and not muted)
      *
      * @param method:        HTTP Method
      * @param url:           URL to be called
      * @param classtype:     Classtype to parse the JSON returned from the server
      * @param requestBody:   String to be sent to the server
      * @param listener:      Listener of the request
      * @param errorListener: Error handler of the request
      */
     public GenericRequest(int method, String url, Class<T> classtype, String requestBody,
                           Response.Listener<T> listener, Response.ErrorListener errorListener) {
         this(method, classtype, url, requestBody, listener,
                 errorListener, new HashMap<String, String>());
     }

     /**
      * Method to be called if you want to GET something from the server and receive the POJO directly after the call (no JSON). (Without header)
      *
      * @param url:           URL to be called
      * @param classtype:     Classtype to parse the JSON returned from the server
      * @param listener:      Listener of the request
      * @param errorListener: Error handler of the request
      */
     public GenericRequest(String url, Class<T> classtype, Response.Listener<T> listener, Response.ErrorListener errorListener) {
         this(Request.Method.GET, url, classtype, "", listener, errorListener);
     }

     /**
      * Method to be called if you want to GET something from the server and receive the POJO directly after the call (no JSON). (With headers)
      *
      * @param url:           URL to be called
      * @param classtype:     Classtype to parse the JSON returned from the server
      * @param listener:      Listener of the request
      * @param errorListener: Error handler of the request
      * @param headers:       Added headers
      */
     public GenericRequest(String url, Class<T> classtype, Response.Listener<T> listener, Response.ErrorListener errorListener, Map<String, String> headers) {
         this(Request.Method.GET, classtype, url, "", listener, errorListener, headers);
     }

     /**
      * Method to be called if you want to send some objects to your server via body in JSON of the request (with headers and muted)
      *
      * @param method:        HTTP Method
      * @param url:           URL to be called
      * @param classtype:     Classtype to parse the JSON returned from the server
      * @param toBeSent:      Object which will be transformed in JSON via Gson and sent to the server
      * @param listener:      Listener of the request
      * @param errorListener: Error handler of the request
      * @param headers:       Added headers
      * @param mute:          Muted (put it to true, to make sense)
      */
     public GenericRequest(int method, String url, Class<T> classtype, Object toBeSent,
                           Response.Listener<T> listener, Response.ErrorListener errorListener, Map<String, String> headers, boolean mute) {
         this(method, classtype, url, new Gson().toJson(toBeSent), listener,
                 errorListener, headers);
         this.muteRequest = mute;
     }

     /**
      * Method to be called if you want to send some objects to your server via body in JSON of the request (without header and muted)
      *
      * @param method:        HTTP Method
      * @param url:           URL to be called
      * @param classtype:     Classtype to parse the JSON returned from the server
      * @param toBeSent:      Object which will be transformed in JSON via Gson and sent to the server
      * @param listener:      Listener of the request
      * @param errorListener: Error handler of the request
      * @param mute:          Muted (put it to true, to make sense)
      */
     public GenericRequest(int method, String url, Class<T> classtype, Object toBeSent,
                           Response.Listener<T> listener, Response.ErrorListener errorListener, boolean mute) {
         this(method, classtype, url, new Gson().toJson(toBeSent), listener,
                 errorListener, new HashMap<String, String>());
         this.muteRequest = mute;

     }

     /**
      * Method to be called if you want to send something to the server but not with a JSON, just with a defined String (without header and not muted)
      *
      * @param method:        HTTP Method
      * @param url:           URL to be called
      * @param classtype:     Classtype to parse the JSON returned from the server
      * @param requestBody:   String to be sent to the server
      * @param listener:      Listener of the request
      * @param errorListener: Error handler of the request
      * @param mute:          Muted (put it to true, to make sense)
      */
     public GenericRequest(int method, String url, Class<T> classtype, String requestBody,
                           Response.Listener<T> listener, Response.ErrorListener errorListener, boolean mute) {
         this(method, classtype, url, requestBody, listener,
                 errorListener, new HashMap<String, String>());
         this.muteRequest = mute;

     }


     @Override
     protected Response<T> parseNetworkResponse(NetworkResponse response) {
         // The magic of the mute request happens here
         if (muteRequest) {
             if (response.statusCode >= 200 && response.statusCode <= 299) {
                 // If the status is correct, we return a success but with a null object, because the server didn't return anything
                 return Response.success(null, HttpHeaderParser.parseCacheHeaders(response));
             }
         } else {
             try {
                 // If it's not muted; we just need to create our POJO from the returned JSON and handle correctly the errors
                 String json = new String(response.data, HttpHeaderParser.parseCharset(response.headers));
                 T parsedObject = gson.fromJson(json, clazz);
                 return Response.success(parsedObject, HttpHeaderParser.parseCacheHeaders(response));
             } catch (UnsupportedEncodingException e) {
                 return Response.error(new ParseError(e));
             } catch (JsonSyntaxException e) {
                 return Response.error(new ParseError(e));
             }
         }
         return null;
     }

     @Override
     public Map<String, String> getHeaders() throws AuthFailureError {
         return headers != null ? headers : super.getHeaders();
     }

     private void configureRequest() {
         // Set retry policy
         // Add headers, for auth for example
         // ...
     }
 }

Это может показаться немного излишним, но это довольно классно иметь все эти конструкторы, потому что у вас есть все случаи:

(Основной конструктор не предназначался для непосредственного использования, хотя это, конечно, возможно).

  1. Запрос с ответом, обработанным POJO / заголовками вручную / POJO для отправки
  2. Запрос с ответом, обработанным POJO / POJO для отправки
  3. Запрос с ответом, обработанным POJO / String для отправки
  4. Запрос с ответом, обработанным POJO (GET)
  5. Запрос с ответом, обработанным POJO ( GET) / Заголовки вручную установлены
  6. Запрос без ответа (200 - Пустое тело) / Заголовки вручную установлены / POJO для отправки
  7. Запрос без ответа (200 - Пустое тело) / POJO Отправить
  8. Запрос без ответа (200 - Пустое тело) / String to Send

Конечно, для того, чтобы это сработало, у вас должен быть Google GSON Lib ; просто добавьте:

compile 'com.google.code.gson:gson:x.y.z'

к вашим зависимостям (текущая версия 2.3.1).

30
задан Tony R 13 June 2009 в 19:34
поделиться

5 ответов

Вам нужно быть осторожным, потому что erase () аннулирует существующие итераторы. Однако он вернет новый действующий итератор, который вы можете использовать:

for ( it = Entities.begin(); it != Entities.end(); ) {
   if( (*it)->getXPos() > 1.5f ) {
      delete * it;  
      it = Entities.erase(it);
   }
   else {
      ++it;
   }
}
41
ответ дан 27 November 2019 в 23:55
поделиться
if((*it)->getXPos() > 1.5f)
{
   delete *it;
   it = Entities.erase(it);
}
2
ответ дан 27 November 2019 в 23:55
поделиться

«Правильный» способ сделать это - использовать алгоритм:

#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», в которой есть множество советов по STL по этому поводу.

Еще один важный момент - не стирать элементы до конца операции. , элементы не нужно перетасовывать. GMan предлагал использовать список, чтобы избежать этого, но при использовании этого метода вся операция выполняется O (n). Код Нила, приведенный выше, напротив, равен O (n ^ 2), поскольку поиск выполняется O (n), а удаление - O (n).

9
ответ дан 27 November 2019 в 23:55
поделиться

После изменения вектора все невыполненные итераторы становятся недействительными. Другими словами, вы не можете изменять вектор во время его итерации. Подумайте, что это делает с памятью, и вы поймете, почему. Я подозреваю, что ваше assert является «недействительным итератором» assert.

std :: vector :: erase () возвращает итератор, который вы должны использовать для замены того, который вы использовали. См. здесь .

0
ответ дан 27 November 2019 в 23:55
поделиться

Основная проблема заключается в том, что большинство итераторов контейнеров stl не поддерживают добавление или удаление элементов в контейнер. Некоторые аннулируют все итераторы, некоторые аннулируют только итератор, который указывает на удаляемый элемент. Пока вы не почувствуете, как работает каждый из контейнеров, вам нужно внимательно прочитать документацию о том, что вы можете и что не можете делать с контейнером.

Контейнеры stl не обеспечивают соблюдение конкретной реализации, но вектор обычно реализуется массивом под капотом. Если вы удалите элемент в начале, все остальные элементы будут перемещены. Если бы у вас был итератор, указывающий на один из других элементов, он мог бы теперь указывать на элемент после старого элемента. Если вы добавляете элемент, может потребоваться изменить размер массива, поэтому создается новый массив, старый материал скопирован, и теперь ваш итератор указывает на старую версию вектора, что плохо.

Для вашей проблемы должно быть безопасно перебирать вектор в обратном направлении и удалять элементы как ты иди. Это также будет немного быстрее, так как вы не будете перемещать элементы, которые позже собираетесь удалить.

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);
  }
}
0
ответ дан 27 November 2019 в 23:55
поделиться
Другие вопросы по тегам:

Похожие вопросы: