Página principal | Lista de namespace | Lista de componentes | Directories | Lista de archivos | Miembros de las clases | Archivos de los miembros

deque.h

Ir a la documentación de este archivo.
00001 // deque.h      (C) 2006 [email protected]
00002 
00003 /** \file  deque.h
00004     \brief Vector extendible a ambos lados, almacenado en un vector circular.
00005 
00006     \author Adolfo Di Mare <[email protected]>
00007     \date   2006
00008 */
00009 
00010 #include <exception> // std::length_error && std::bad_alloc
00011 #include <iostream>  // cin && cout
00012 
00013 // using namespace std;
00014 /// Definido por la biblioteca C++ estándar.
00015 namespace std {} // Está acá para que Doxygen lo documente
00016 
00017 template <class T> class test_deque;
00018 
00019 /** Vector extendible a ambos lados, almacenado en un vector circular.
00020     - Es posible agregar valores al principio o al final del vector extendible.
00021     - El tamaño del vector extendible no puede cambiar, pues se establece en el 
00022       constructor.
00023 
00024     - Why English names??? ==> http://www.di-mare.com/adolfo/binder/c01.htm#sc04
00025 */
00026 template <class T>
00027 class deque {
00028 public:
00029     typedef typename T    value_type; ///< Nombre estándar del objeto contenido
00030     typedef typename T *  pointer;    ///< Nombre estándar del puntero al objeto contenido
00031     typedef typename T &  reference;  ///< Nombre estándar de la referencia al objeto contenido
00032 
00033     typedef typename const value_type const_value_type; ///< Nombre estándar del objeto contenido \c const
00034     typedef typename const T *        const_pointer; ///< Nombre estándar del puntero al objeto contenido \c const
00035     typedef typename const T &        const_reference; ///< Nombre estándar de la referencia al objeto contenido \c const
00036 
00037     typedef typename unsigned size_type;  ///< Nombre estándar del tipo retornado por \c size()
00038 
00039     /// Constructor: Vector extendible con capacidad para almacenar \c "N" valores
00040     deque( size_type N );
00041     ~deque() { if (m_vec != 0) { delete [] m_vec; } } ///< Destructor
00042 
00043     /// Retorna \c "true" si el vector extendible está vacía
00044     bool empty() const { return (size() == 0); }
00045 
00046     /// Cantidad de valores almacenados en el vector extendible.
00047     size_type size()     const { return m_size; }     
00048     /// Cantidad máxima de valores que se pueden almacenar en el vector extendible.
00049     size_type capacity() const { return m_capacity; }
00050 
00051     /// Retorna una referencia al primer valor del vector extendible.
00052     /// - El primero valor es el que está "al frente" en el vector extendible.
00053     /// \pre \c !empty()
00054     /// \see test_deque<T>::test_front()
00055     value_type& front() { return at_check(m_front); }
00056 
00057     /// Retorna una referencia constante al primer valor del vector extendible.
00058     /// - El primero valor es el que está "al frente" en el vector extendible.
00059     /// \pre \c !empty()
00060     /// \see test_deque<T>::test_front_const()
00061     const value_type& front() const { return const_cast<deque*>(this)->front(); }
00062     // const_cast<deque*>(this) hace que "this" ya NO sea un puntero "const".
00063 
00064     /// Retorna una referencia al último valor del vector extendible.
00065     /// - El úlimo valor es el que está "al final" en el vector extendible.
00066     /// \pre \c !empty()
00067     /// \see test_deque<T>::test_back()
00068     value_type& back() { return at_check(m_front+m_size-1); }
00069 
00070     /// Retorna una referencia constante al último valor del vector extendible.
00071     /// - El úlimo valor es el que está "al final" en el vector extendible.
00072     /// \pre \c !empty()
00073     /// \see test_deque<T>::test_back_const()
00074     const value_type& back() const { return const_cast<deque*>(this)->back(); }
00075 
00076     /// Acceso al \c "i"-ésimo valor almacenado en el contenedor.
00077     /// - No levanta ninguna excepción si \c "i" está fuera del rango válido.
00078     value_type& operator[](size_type i) {
00079         size_type  i_real = m_front + i;
00080         i_real = ( i_real < m_capacity ? i_real : i_real - m_capacity );
00081         return m_vec[ i_real ]; // uso "+" y "-" que es más eficiente que "%"
00082     }
00083     /// Acceso al \c "i"-ésimo valor almacenado en el contenedor ( \c const ).
00084     /// - No levanta ninguna excepción si \c "i" está fuera del rango válido.
00085     value_type& operator[](size_type i) const { return const_cast<deque*>(this)->operator[](i); }
00086 
00087     /// Acceso al \c "i"-ésimo valor almacenado en el contenedor.
00088     /// - Levanta la excepción \c std::out_of_range si \c "i" está fuera del rango válido.
00089     value_type& at(size_type i)       { return at_check(m_front+i); }
00090     /// Acceso al \c "i"-ésimo valor almacenado en el contenedor ( \c const ).
00091     /// - Levanta la excepción \c std::out_of_range si \c "i" está fuera del rango válido.
00092     value_type& at(size_type i) const { return const_cast<deque*>(this)->at(i); }
00093 
00094     void push_front( const value_type& val );
00095     void push_back(  const value_type& val );
00096     void pop_front();
00097     void pop_back();
00098 
00099     template <class T> friend std::istream& operator>>( std::istream& CIN,        deque<T>& Q);
00100     template <class T> friend std::ostream& operator<<( std::ostream& COUT, const deque<T>& Q);
00101 private:
00102     bool ok() const;
00103     inline friend bool check_ok( const deque<T>& Q) { return Q.ok(); }
00104     value_type& at_check(size_type i);
00105     explicit deque(const deque&); ///< \c "private" evita que el objeto sea copiado
00106 
00107     template <class T> friend class test_deque; ///< Datos de prueba para la clase
00108 private:
00109     unsigned     m_front;    ///< Indice del primero del vector extendible.
00110     unsigned     m_size;     ///< Cantidad de valores almacenados en el vector extendible (puede ser cero).
00111     unsigned     m_capacity; ///< Cantidad máxima de valores que pueden estar almacenados en el vector extendible.
00112     value_type * m_vec;      ///< Bloque de memoria para para almacenar los \c m_capacity valores que caben en el vector extendible.
00113 };
00114 
00115 template <class T>
00116 deque<T>::deque( size_type N ) : m_front(0), m_size(0), m_capacity(0), m_vec(0) {
00117     if (N == 0) {
00118         throw std::length_error("deque<T>::deque(0)");
00119     }
00120     else {
00121         try {
00122             m_capacity = N;
00123             m_vec = new value_type [ m_capacity ];
00124         }
00125         catch (std::bad_alloc) {
00126             throw; // new[] tira std::bad_alloc si ya no hay memoria
00127         }
00128     }
00129 }
00130 
00131 /** Acceso al \c "i"-ésimo valor del vector extendible.
00132     - Si <code>(i=>m_capacity)</code> retorna el valor \c m_vec[i-m_capacity].
00133     - Verifica que el valor de \c "i" esté en el rango válido.
00134     - Está implementado de manera que el índice de \c at(m_front+m_size-1)
00135       siempre esté en el rango válido.
00136     - Funciona como si los índices que están a partir de \c m_capacity estuvieran en un
00137       vector, a la par de \c m_vec[].
00138     - Rango válido para \c "i": <code>[[ m_front ==> m_front + m_size - 1 ]]</code>.
00139     - Levanta la excepción \c std::out_of_range si \c "i" está fuera del rango válido.
00140     \code
00141       m_vec[]       m_capacity==7    
00142         ||                           
00143         \/        m_front==<5> <6>   
00144       +---+---+---+---+---+---+---+    - at_check(<5>) es m_vec[5]
00145       | 5 | 6 | _ | _ | _ | 3 | 4 |    - at_check(<6>) es m_vec[6]
00146       +---+---+---+---+---+---+---+    - at_check(<7>) es m_vec[0]
00147     +-------+               |-------+  - at_check(<8>) es m_vec[1]
00148     |  #3  #4     m_front---+  #2   |
00149     |                               |  - Sólo estos accesos son válidos
00150     +-<-<-----------------------<-<-+  - los demás levantan \c std::out_of_range
00151                 m_size == 4
00152     \endcode
00153 */
00154 template <class T>
00155 T& deque<T>::at_check(size_type i) {
00156     // uso "+" y "-" que es más eficiente que "%"
00157     size_type i_real = ( i < m_capacity ? i : i - m_capacity );
00158     if ( (m_front <= i) && (i < m_front + m_size) ) {
00159         return m_vec[i_real];
00160     }
00161     else {
00162         throw std::out_of_range("deque<T>::at()");
00163     }
00164 }
00165 
00166 /// Agrega una copia de \c "val" al final del vector extendible.
00167 /// \pre
00168 /// <code>size() < m_capacity</code>
00169 /// \see test_deque<T>::test_push_back()
00170 template <class T>
00171 void deque<T>::push_back(const value_type& val) {
00172     if (m_size < m_capacity) {
00173         size_type i_real = m_front + m_size;
00174         i_real = (i_real < m_capacity ? i_real : i_real - m_capacity);
00175         m_vec[i_real] = val;
00176         m_size++;
00177     }
00178     else {
00179         throw std::out_of_range("deque<T>::push_back()");
00180     }
00181 }
00182 
00183 /// Agrega una copia de \c "val" al principio del vector extendible.
00184 /// \pre
00185 /// <code>size() < m_capacity</code>
00186 /// \see test_deque<T>::test_push_front()
00187 template <class T>
00188 void deque<T>::push_front(const value_type& val) {
00189     if (m_size < m_capacity) {
00190         m_front = ( m_front>0 ? m_front-1 : m_capacity-1 );
00191         m_vec[m_front] = val;
00192         m_size++;
00193     }
00194     else {
00195         throw std::out_of_range("deque<T>::push_front()");
00196     }
00197 }
00198 /// Saca del vector extendible al valor que está al frente.
00199 /// \pre
00200 /// <code>!empty()</code>
00201 /// \see test_deque<T>::test_pop_front()
00202 template <class T>
00203 inline void deque<T>::pop_front() {
00204     if (m_size > 0) {
00205     //  unsigned temp = m_front;
00206         m_front++;
00207         m_size--;
00208         if (m_front >= m_capacity) {
00209             m_front = 0;
00210         }
00211     //  return m_vec[temp];
00212     }
00213     else {
00214         throw std::out_of_range("deque<T>::pop_front()");
00215     }
00216 }
00217 
00218 /// Saca del vector extendible al valor que está al frente.
00219 /// \pre
00220 /// <code>!empty()</code>
00221 /// \see test_deque<T>::test_pop_back()
00222 template <class T>
00223 inline void deque<T>::pop_back() {
00224     if (m_size > 0) {
00225         m_size--;
00226     }
00227     else {
00228         throw std::out_of_range("deque<T>::pop_front()");
00229     }
00230 }
00231 
00232 #if 0
00233     /// Retorna \c "true" si el vector extendible está llena
00234     template <class T>
00235     inline bool deque<T>::full() const {
00236         return (m_size == m_ capacity);
00237     }
00238 #endif
00239 
00240 /** Verifica la invariante de la clase \c deque<T>.
00241     - Regresa \c "true" si \c "*this" contiene un valor correcto.
00242     - Podría retornar \c "true" aún cuando \c "*this" tenga su valor corrupto.
00243     - Podría no retornar si \c "*this" tiene su valor corrupto.
00244     - http://www.di-mare.com/adolfo/binder/c04.htm#sc11
00245     \see deque<T>::ok( )
00246 
00247     \par Rep: Diagrama de la clase
00248     \code
00249     +-------------------------------+ +-----------------------------------+
00250     | m_vec[]                       | |   m_vec[]       m_capacity==7     |
00251     |   ||                          | |     ||                            |
00252     |   \/ <1> <2>==m_front    <6>  | |     \/        m_front==<5> <6>    |
00253     | +---+---+---+---+---+---+---+ | |   +---+---+---+---+---+---+---+   |
00254     | | _ | _ | 3 | 9 | 5 | 6 | _ | | |   | 5 | 6 | _ | _ | _ | 3 | 9 |   |
00255     | +---+---+---+---+---+---+---+ | |   +---+---+---+---+---+---+---+   |
00256     |  <0>      |-----------+       | | +-------+               |-------+ |
00257     | m_front---+  #2  #3  #4       | | |  #3  #4     m_front---+  #2   | |
00258     |                               | | |                               | |
00259     |            m_size == 4        | | +-<-<-----------------------<-<-+ |
00260     +-------------------------------+ +-----------------------------------+
00261     \endcode
00262     - <em>Rep</em>: http://www.di-mare.com/adolfo/binder/c03.htm#k1-Rep
00263 
00264     \par Rep: Descripción en palabras
00265     - Los valores del vector extendible están almacenados en memoria dinámica,
00266       consecutivamente, en el vector \c "m_vec[]" cuyo tamaño no puede
00267       cambiar pues queda definido cómo \c "deque<T>::m_capacity" cuando el
00268       objeto es construido.
00269     - El primer valor del vector extendible siempre está almacenado en 
00270       \c "m_vec[m_front]".
00271     - El último valor del vector extendible siempre está almacenado en
00272       \c "m_vec[ (m_front+m_size-1) % m_capacity ]".
00273     - En el diagrama se muestran 2 formas en que puede estar almacenado el
00274       valor de una cola <code>Q = [3,9,5,6]</code> con \c "3" al frente y
00275       \c "6" al final.
00276     - En esta implementación del vector extendible se usa un vector circular en donde
00277       los valores están almacenados desde el índice \c "m_front" de \c "m_vec[]".
00278     - Como siempre el "frente" del vector extendible debe estar "antes" que el "final",
00279       cuando el valor de \c "m_front" no es \c "0", porque no apunta al
00280       principio del vector, lo que ocurre es que el vector extendible está almacenado
00281       llegando al final del vector y luego volviendo al principio
00282       (<em>wrap-around</em>).
00283     - La otra forma de almacenar el vector extendible en un vector circular es usar
00284       los campos \c "m_front" y \c "m_back", pero esta implementación
00285       tiene el problema de que es necesario desperdiciar uno de los
00286       campos del vector \c "m_vec[]" pues de otra manera no es posible
00287       determinar si el vector extendible está lleno o vacío.
00288 */
00289 template <class T>
00290 bool check_ok( const deque<T>& Q);
00291 
00292 /** Verifica la invariante de la clase.
00293     - Implementación para \c check_ok( const deque<T>& Q ).
00294     \see check_ok( const deque<T>& Q)
00295 
00296     \remark
00297     - Desafortunadamente no fue posible lograr que el compilador aceptara
00298       una implementación directa para \c check_ok( const deque<T>& Q )
00299     - El ligador (<em>linker</em>) emite el error LNK2019. El truco para
00300       lidiar con este problema (<em>work arount</em>) es implementar \c ok()
00301     - Es mejor que \c check_ok() sea una función para evitarle al programador
00302       usuario la obligación de implementar el método \c ok()
00303     - http://www.di-mare.com/adolfo/binder/c04.htm#sc11
00304     - http://www.google.com/search?num=100&as_q=LNK2019+template+c%2B%2B
00305     - http://search.yahoo.com/search?n=100&p=LNK2019+template+c%2B%2B
00306 
00307 */
00308 template <class T>
00309 bool deque<T>::ok() const {
00310     if (this == 0) {
00311         /// - Invariante: ningún objeto puede estar almacenado en la posición nula.
00312         return false;
00313     }
00314 
00315     if ( ! (m_capacity > 0) ) {
00316         /// - Invariante: La capacidad \c m_capacity debe ser mayor que cero.
00317         return false;
00318     }
00319     if ( ! (m_front < m_capacity) ) {
00320         /// - Invariante: El índice \c m_front no puede exceder \c m_capacity.
00321         return false;
00322     }
00323     if ( ! (m_size <= m_capacity) ) {
00324         /// - Invariante: El índice \c m_size no puede exceder \c m_capacity.
00325         return false;
00326     }
00327     if ( ! (size() <= m_capacity) ) {
00328         /// - Invariante: La cantidad de valores almanenados \c size() no puede exceder \c m_capacity.
00329         return false;
00330     }
00331     if ( ! (m_vec != 0) ) {
00332         /// - Invariante: El vector \c m_vec[] debe existir en memoria dinámica.
00333         return false;
00334     }
00335 
00336     // Declaración previa del verificador del objeto contenido
00337     // - Con este se le quita trabajo al programador cliente de la clase
00338     //   pues deberá implementar \c check_ok( const T& ) sólo en el caso
00339     //   de que utilice \c check_ok() para \c deque<T> .
00340     // - En otras palabras: el programador cliente está obligado a implementar
00341     //   este método sólo si invoca \c check_ok( const deque<T>& ).
00342     // - Por eso es preferible que \c check_ok() sea una función y no un método.
00343     bool check_ok( const deque<T>::value_type & );
00344 
00345     for (unsigned i=0; i<m_capacity; ++i) {
00346         if ( ! check_ok( m_vec[i] ) ) {
00347             /// - Invariante: Todos los valores almacenados en el contenedor
00348             ///   deben cumplir con su propia invariante
00349             return false;
00350         }
00351     }
00352     return true;
00353 /*  NOTA: VC++ v7 .net
00354     La documentación del compilador pide que check_ok( const deque<T>& ) sea declarado
00355     dentro de la plantilla deque<T> como una función "friend" de la siguiente forma:
00356         template <class T> friend bool check_ok( const deque<T>& Q);
00357 
00358     Desafortunadamente, el compilador produce un error interno si check_ok() es una
00359     función. Por eso, en esta implementación se usa el método deque<T>::ok(), el que
00360     sí es aceptado por el compilador. Para evitar una pequeña degradación en la
00361     eficiencia, la función check_ok() queda definida dentro de la clase como una
00362     función "inline", que el compilador compila sin dar problemas.
00363 
00364     A primera vista parece inocuo exigir que quien use deque<T> esté obligado a 
00365     implementar el método T::ok(), pero en la práctica eso es contraproducente por
00366     2 razones:
00367     - Algunos "tipos" en C++ no son clases, como "int" o "float", por lo que no
00368       es posible definirles su método ok().
00369     - Para un programador cliente es incómodo agregar un método que no piensa usar
00370       Por eso, si el programador cliente invoca check_ok( const deque<T>& ) deberá
00371       implementar check_ok( const T& ), pero de otra forma ni siquiera tiene que
00372       enterarse de que check_ok() existe.
00373 */
00374 }
00375 
00376 // EOF: deque.h

Generado el Tue Aug 21 14:45:39 2007 para Contenedor deque: por  doxygen 1.4.1
Hosted by www.Geocities.ws

1