c++ - trucos - size() Vs empty() en vector-¿por qué empty() es preferido?
guia garrys mod (9)
Mientras depuraba algo, vi la implementación de STL vector :: empty ():
bool empty() const
{return (size() == 0); }
Creo que, cada vez que exploramos el vacío del vector, siempre se recomienda usar empty over size (). Pero al ver esa implementación, me pregunto, ¿cuál es el beneficio de hacerlo? En cambio, hay una llamada a la función de sobrecarga en las llamadas vacías, ya que internamente llama a size () == 0.
Pensé que empty () podría ser útil en el caso de la lista, ya que size () no garantiza el tiempo constante en la lista. Para verificar mi suposición, verifiqué la implementación de la lista y, sorprendentemente, encontré la misma implementación en la lista también,
return (size() == 0);
Estoy un poco confundido ahora. Si el vacío internamente usa el tamaño () entonces ¿por qué preferiríamos el vacío sobre el tamaño ()?
Además de las razones expuestas anteriormente, también es más claro que foo.size () == 0 y / o! Foo.size ()
Además del punto de lectura, que es muy válido, lo que experimentó es simplemente los artefactos de una implementación particular, no la única posible.
Es decir, no hay ninguna razón o requisito para que empty () se implemente en términos de tamaño () tanto en el caso del vector y de la lista, como en el de cualquier otro contenedor. Si hay alternativas de mejor rendimiento, se deben usar, a menos que el autor (es) de la biblioteca sea incompetente, o más razonablemente, flojo.
En cuanto a la lista y la O (1) -ness de size (), o la falta de la misma, debe tener en cuenta que la lista puede implementar tamaño () como O (1) o empalme (), pero no ambos (pensamiento el motivo es un ejercicio interesante.) Entonces, en su caso, la biblioteca que inspeccionó puede haber implementado el tamaño () como O (1) (en cuyo caso el empalme () sería O (n)) y por lo tanto podría implementar vacío () en términos de tamaño () sin sacrificar el rendimiento, de lo contrario, sería una biblioteca realmente mala.
Bueno, como dices, eso es solo un detalle de implementación. std::list
se puede implementar con un tamaño almacenado (tamaño de tiempo constante size()
pero con splice()
tiempo lineal splice()
) o sin ( splice()
tiempo constante splice()
pero con size()
tiempo lineal size()
). Al elegir usar empty()
, evita apostar por un detalle de implementación, cuando no necesita saber el tamaño.
En primer lugar, usar una función llamada empty()
cuando quiere saber si algo está vacío hace que el código sea más legible, y significa que no tiene que preocuparse por los detalles de la implementación. También significa que su código se puede adaptar más fácilmente a otros tipos de contenedores, con otras características.
Segundo, eso es solo una implementación de STL. Mi GNU C ++ se parece a esto:
bool
empty() const
{ return begin() == end(); }
Esto eventualmente resultará en una comparación de punteros, mientras que usar size () resultaría en una resta (en esta implementación).
En tercer lugar, no es muy probable que se incurra en la sobrecarga de una llamada de función adicional, ya que la función empty()
probablemente esté en línea (en ambas implementaciones).
Porque si cambia de std :: vector a std :: list u otro contenedor, puede ser diferente.
Por ejemplo, algunas implementaciones de std::list::size
toman O(n)
y no O(1)
.
Siguiendo el estándar, se debe preferir empty () ya que tiene complejidad de tiempo constante independientemente del tipo de contenedor.
En el estándar C ++ 03, capítulo 23.1, tabla 65: Requisitos del contenedor
Operation: empty()
Return_type: convertible to bool
Semantics: equivalent to size()==0
Complexity: constant
Parece que en su implementación de STL, tomaron la semántica como la implementación real que ignora los requisitos de complejidad, o el tamaño () es un tiempo constante en la implementación (campo almacenado).
Si size () no es un tiempo constante, comuníquese con su proveedor acerca de std :: list <> :: empty () que no cumpla con los requisitos de contenedor estándar.
empty () tiene implementaciones O (1) para TODAS las clases de contenedor. size () solo puede proporcionar implementaciones O (n) para algunos contenedores; esta es la razón por la cual se prefiere empty ().
Prefiere usar use empty () que size () porque cada contenedor puede implementar empty () de una manera diferente para obtener la operación de tiempo constante.
ejemplo:
vector implementa como: bool empty () const {// prueba si la secuencia está vacía return (size () == 0); }
lista de implementos como:
bool empty() const
{ // test if sequence is empty
return (_Mysize == 0);
}
Debería escribir la condición cada vez que use size()
. Es conveniente usar empty()
. Esto es por supuesto, siempre que no cambie contenedores. Como otros han señalado, depende de la implementación usar size()
en empty()
o no. Sin embargo, el estándar garantiza que: empty()
es una operación de tiempo constante para todos los contenedores estándar.