You are on page 1of 10

Clases en C

Creacin de clases en C++ (II)


Programacin Orientada a Objeto Ing. Civil en Telecomunicaciones

Escribir nuestra propia clase para vectores Vec basada en std::vector Escribir nuestra propia clase para ristras Str basada en std::string

Creando nuestra clase Vec


Clase genrica para representar vectores Versin reducida de clase vector de biblioteca estndar
template <class T> class Vec { public: // interfaz private: // implementacion }

Ejemplo: clase Vec


Definir puntero al primer elemento del vector Definir puntero a posicin despus del ltimo elemento
template <class T> class Vec { public: // interfaz private: T* datos; // primer elemento T* limite; // mas alla del ultimo }

Clase Vec

Instanciacin de plantillas
Clase Vec est definida como una plantilla (template) de tipo T Al crear un nuevo vector Vec de algn tipo, el compilador instancia el cdigo para el tipo adecuado
Al escribir Vec<int>, se instancia cdigo

Elementos del objeto Vec

Vec v datos lmite

para enteros

Al escribir Vec<string>, se instancia

cdigo para objetos string

2013 Mario Medina C.

Constructores para Vec


Definiremos tres constructores
Constructor por omisin

Constructores para Vec


Constructor por omisin
Crea vector vaco Inicializar punteros datos y limite

Vec<InfoAlumno> v_ia; Constructor con tamao Vec<double> v_d(100); Constructor con tamao y valor inicial Vec<int> v_i(100, 0);

Constructor con tamao y valor inicial


Solicita memoria para los n objetos Inicializa los n objetos Inicializa los punteros datos y limite

Constructor con slo tamao


Solicita memoria para los n objetos Inicializa los n objetos usando el constructor por omisin Inicializa los punteros datos y limite

Constructores para Vec


Suponemos existe funcin creaVec() que solicita memoria para los datos
template <class T> class Vec { public: Vec() { // Constructor por omision creaVec(); } // Constructor con tamao y valor inicial explicit Vec(std::size_t n; const T& val = T()) { creaVec(n, val); }

Constructor para Vec


Constructor recibe dos argumentos
explicit Vec(std::size_t n; const T& val = T())
El primero es el tamao n, de tipo

std::size_t El segundo es el valor inicial val


Referencia constante a dato de tipo T Si no se especifica, utiliza el valor por omisin Llama al constructor por omisin del tipo T

Constructores implcitos y explcitos


C++ usa constructores en forma implcita para resolver los parmetros de una funcin
class dato() { public: dato(int i) { // Constructor miDato = i; } int x(void) { // Funcion de acceso return miDato; } private: int miDato; // Dato privado }

Constructores implcitos y explcitos


Supongamos la siguiente funcin
int retornaValor(dato d) { return d.x(); }

Qu debera ocurrir al llamar a retornaValor(32)?


Valor 32 se convierte implcitamente en objeto

dato usando constructor

Este comportamiento puede evitarse usando la clusula explicit en el constructor

2013 Mario Medina C.

Definiciones de tipos
Definir nombres de tipos a ser usados por los usuarios
typedefs para iteradores const y no-const typedefs para referencias const y no-

Definicin de tipos
template <class T> class Vec { public: typedef T* iterator; typedef const T* const_iterator; typedef size_t size_type; typedef T value_type; typedef std::ptrdiff_t difference_type; typedef T& reference; typedef const T& const_reference; Vec() { creaVec() }; explicit Vec(size_type n, const T& val = T()) { creaVec(n, val); } private: T* datos; // primer elemento T* limite; // mas alla del ultimo }

const
typedefs para

el tipo del dato value_type el tamao del dato size_type la diferencia entre iteradores

Funcin miembro size()


Retorna el nmero de elementos de Vec() como tipo Vec::size_type
size_type size() const { return limite datos; }
Ejemplo de uso

Operador ndice []
Necesario para acceder a elementos de un vector Vec usando notacin de ndice []
Operador [] debe ser sobrecargado (overloaded) Comando operator usado para redefinir operadores

Vec<int> v; for (i = 0; i != v.size(); ++i)

T& operator[](size_type i) { return datos[i]; } const T& operator[](size_type i) const { return datos[i]; }

Operaciones que retornan iteradores


Operaciones begin() y end() Retornan iteradores const y no-const
iterator begin() { return datos; } const_iterator begin() const { return datos; } iterator end() { return limite; } const_iterator end() const { return limite; }

Constructor para copia


Constructor para copia (copy constructor) es invocado
Al pasar un objeto por valor a una funcin Al retornar un objeto desde una funcin Al realizar una copia explcita de objetos
Vec<int> v_int1, v_int2, v_int3; v_int2 = func(v_int1); v_int3 = v_int2;

2013 Mario Medina C.

Constructor para copia


Funcin miembro con el mismo nombre que la clase
Recibe como argumento una referencia const

Copia de Clase Vec (errada!)


Elementos del objeto Vec v

a un objeto de la misma clase


Si no se escribe uno, el compilador genera un
Vec v datos lmite Vec v2 datos lmite

constructor para copia por omisin En nuestro caso, el constructor de copia debe copiar los datos y generar nuevos punteros datos y limite

La copia del objeto Vec debiera tener su propia copia de los datos

Copia de Clase Vec (correcta!)


Elementos del objeto Vec v

Constructor para copia


Para la clase Vec, invocaremos a la funcin creaVec() con dos iteradores como argumentos

Vec v datos lmite

Vec v2 datos lmite

template <class T> class Vec { public: Vec(const Vec& v) { // copy constructor creaVec(v.begin(), v.end()); } };

Elementos del objeto Vec v2

Operador de asignacin
Recibe referencia const a objeto a asignar y retorna referencia a objeto existente Asignacin siempre destruye operando que recibe la asignacin
Revisar que el operando que recibe la

Operador de asignacin
Cdigo es similar a constructor para copia
template <class T> class Vec { public: Vec<T>& operator=(const Vec& v) { if (&v != this) { eliminaVec(); creaVec(v.begin(), v.end()); } return *this; };

asignacin no sea igual al operando a asignar

Eliminar el vector existente antes de crear un nuevo vector

2013 Mario Medina C.

Operador this
Operador this es un puntero al objeto al que pertenece la funcin miembro que la usa Funcin anterior retorna *this, que es el mismo objeto

Inicializacin vs. Asignacin


Inicializacin crea un nuevo objeto y le da a ste un valor
Utiliza el constructor para copia

Asignacin destruye un objeto existente y crea un nuevo objeto asignndole un valor


Utiliza el operador operator=
string s1 = abcd; string s2; s2 = s1; // Inicializacion // Inicializacion // Asignacion

Destructor
Mtodo que es invocado al borrar un objeto creado dinmicamente via delete Nombre dado por el nombre de la clase, precedido por ~, sin argumentos
template <class T> class Vec { public: ~Vec() { eliminaVec(); } };

Operaciones por omisin


Si el programador no lo hace, el compilador genera sus propias versiones de
Constructor para copia Operador de asignacin Destructor

Operan en forma recursiva


Copian/asignan/destruyen los objetos

contenidos en el objeto

No liberan memoria!

Regla de tres
Si su clase necesita un destructor, probablemente necesita un constructor para copia y un operador de asignacin Porqu?
Manejo de memoria dinmica Prdidas de memoria (memory leaks) Destruccin de un puntero no recupera la

Clase Vec dinmica


Clase Vec definida hasta ahora no permite agregar elementos
No posee funcin push_back(), que

agregar un elemento al final del vector Funcin push_back() puede hacer crecer el
vector un elemento a la vez Ineficiente!
Alternativa ms eficiente: preasignar espacio

memoria solicitada al sistema

para datos

2013 Mario Medina C.

Funcin push_back()
Solucin: asignar ms memoria de la necesaria
Solicitar ms memoria slo cuando sta se acaba

Clase Vec dinmica


Elementos inicializados Datos no inicializados

Agregar memoria en bloques de tamao fijo Duplicar la memoria


Requiere agregar otro puntero: disponible Ejemplo: al declarar

Vec v
datos disponible lmite

Vec v[100], crear vector con espacio para 200 elementos

Funcin miembro push_back()


template <class T> class Vec { public: size_type size() const { return disponible datos; } iterator end() { return disponible; } const_iterator end() const { return disponible; } void push_back(const T& val) { if (disponible == limite) creceVec(); agrega(val); } private: iterator datos; iterator disponible; iterator limite; };

Solicitando memoria al sistema


Creacin de nuevos elementos para Vec Uso de malloc() y calloc()
Funciones de biblioteca estndar de C

Uso de comandos new y delete


Comando new asigna memoria e inicializa

objeto creado usando constructor por omisin


No nos sirve: objetos son inicializados dos veces Objetos no inicializados son escritos por push_back()

Funciones de solicitud de memoria


Definidas en <memory>
Parte de la biblioteca estndar
template <class T> class allocator { public: T* allocate(size_t); void deallocate(T*, size_t); void construct(const T&); void destroy(T*); . . . }; template <class In, class For> uninitialized_copy(In, In, For); template <class For, class T> uninitialized_fill(For, For, const T&);

Funciones de solicitud de memoria


Algunas de las funciones de <memory>
T* allocate(size_t) asigna size_t objetos de tipo T no inicializados Funcin void deallocate(T*, size_t) libera size_t objetos de tipo T Funcin void construct(T*, const T&) construye un objeto de tipo T en rea apuntada por T* con los valores contenidos en T& Funcin void destroy(T*) destruye el objeto
Funcin de solicitud de memoria

2013 Mario Medina C.

Funciones de solicitud de memoria


Funciones que construyen e inicializan objetos en memoria ya solicitada via allocate()
template <class In, class For> uninitialized_copy(In i1, In i2, For f);
Copia objetos entre iteradores i1 e i2 a partir de

Clase Vec (I)


template <class T> class Vec { public: // Typedefs para la clase typedef T* iterator; typedef const T* const_iterator; typedef size_t size_type; typedef T value_type; typedef std::ptrdiff_t difference_type; typedef T& reference; typedef const T& const_reference; // Constructores Vec() { creaVec(); } explicit Vec(size_type n, const T& t = T()) { creaVec(n, t); }

iterador f

template <class For, class T> uninitialized_fill(For f1, For f2, const T& t);
Inicializa objetos entre iteradores f1 e f2 con el

objeto t

Clase Vec (II)


// Constructor para copia Vec(const Vec& v) { creaVec(v.begin(), v.end()); } // operador de asignacion Vec& operator=(const Vec&); // Destructor ~Vec() { eliminaVec(); } T& operator[](size_type i) { return datos[i]; } const T& operator[](size_type i) const { return datos[i]; }

Clase Vec (III)


void push_back(const T& t) { if (disponible == limite) creceVec(); agrega(const T&); } size_type size() const { return disponible datos; } iterator begin() { return datos; } const_iterator begin() const { return datos; } iterator end() { return disponible; } const_iterator end() const { return disponible; }

Clase Vec (IIII)


private: iterator datos; iterator disponible; iterator limite; // Allocator para solicitudes de memoria allocator<t> alloc; // Funciones para crear vectores Vec void creaVec(); void creaVec(size_type, const T&); void creaVec(const_iterator, const_iterator); // Funciones para destruir elementos en Vec void eliminaVec(); // Funciones para push_back() void creceVec(); void agrega(const T&); };

Funciones privadas para creacin


template <class T> void Vec<T>::creaVec() { datos = disponible = limite = 0; } template <class T> void Vec<T>::creaVec(size_type n, const T& val) { datos = alloc.allocate(n); limite = disponible = datos + n; uninitialized_fill(datos, limite, val); } template <class T> void Vec<T>::creaVec(const_iterator i, const_iterator j) { datos = alloc.allocate(j - i); limite = disponible = uninitialized_copy(i, j, datos); }

2013 Mario Medina C.

Funcin privada para eliminar Vec


template <class T> void Vec<T>::eliminaVec() { if (datos) { // Destruir los elementos construidos, en orden // inverso iterator it = disponible; while (it != datos) alloc.destroy(--it); // Retornar el espacio al sistema alloc.deallocate(datos, limite datos); } // Reinicializar punteros datos = limite = disponible = 0; }

Funciones privadas para push_back()


template <class T> void Vec<T>::creceVec() { size_type nuevo_size = max(2*(limite datos), ptrdiff_t(1)); iterator nuevo_datos = alloc.allocate(nuevo_size); iterator nuevo_disponible = uninitialized_copy(datos, disponible, nuevo_datos); eliminaVec(); datos = nuevo_datos; disponible = nuevo_disponible; limite = datos + nuevo_size; } template <class T> void Vec<T>::agrega(const T& val) { alloc.construct(disponible++, val); }

Creando nuestra clase Str


Crearemos la clase Str, modelada en los objetos String de la biblioteca estndar
Implementada como un vector Vec de

Clase Str
class Str { public: typedef Vec<char>::size_type size_type; Str() { } Str(size_type n, char c): datos(n, c) {} Str(const char *cp) { std::copy(cp, cp + std::strlen(cp), std::back_inserter(datos)); } template <class In> Str(In b, In e) { std::copy(b, e, std::back_inserter(datos)); } private: Vec<char> datos; };

caracteres char
Clase Str delega el manejo de los datos a la

clase Vec
Clase Str tiene 4 constructores, que invocan a los constructores de Vec

Constructores para Str


Primeros dos constructores son similares Dos ltimos constructores
Crean un vector datos no inicializado

Constructor para copia


Nueva clase Str no necesita
Constructor para copia Operador de asignacin Destructor

(vaco) Usan std::copy para copiar caracteres al nuevo vector datos Primeros dos argumentos son iteradores al comienzo y final de los caracteres a copiar Usan back_inserter() para agregar caracteres, haciendo crecer el vector

Utiliza implcita y automticamente las funciones correspondientes de la clase

2013 Mario Medina C.

Conversiones desde char *


Constructor para copia por omisin
Usado para crear un nuevo Str a partir de Str

Operaciones sobre Str


Operador ndice para acceder a datos
Llaman a los mtodos de la clase Vec Versin const y no-const
char& operator[](size_type i) { return datos[i]; } const char& operator[](size_type i) const { return datos[i]; }

existente

Str t = hola;

Operador de asignacin por omisin


Usado para asignacin s

= chao;

Constructor Str(const char *cp)


Usado para crear un nuevo Str dndole una

ristra como valor inicial


Str t(hola);

Operadores de entrada/salida
Objetos Str pueden
recibir datos desde un flujo de entrada escribir a un flujo de datos de salida

Funcin para escribir a flujo de salida


Debe ser funcin no-miembro
ostream& operator<<(ostream& os, const Str& s) { for (Str::size_type i = 0; i != s.size(); ++i) { os << s[i]; } return os; }

Sobrecargar operadores >> y << de las clases istream y ostream


Pero, no tenemos acceso a su cdigo fuente! Solucin: escribir mtodos no-miembros
std::istream& operator>>(std::istream&, Str&); std::ostream& operator<<(std::ostream&, const Str&);

Necesario agregar mtodo size a clase Str


class Str { public: size_type size() const { return datos.size(); } }

Funcin para leer de flujo de entrada


Debe ser funcin no-miembro
istream& operator>>(istream& is, Str& s) { s.datos.clear(); // Elimina datos existentes // Lee y elimina espacios en blanco char c; while (is.get(c) && isspace(c)) ; if (is) { // mientras haya algo que leer do { s.datos.push_back(c); } while (is.get(c) && !isspace(c)); if (is) { // Si leimos un espacio, retornarlo is.unget(); } return is; }

Funcin para leer de flujo de entrada


La funcin anterior falla en las lneas en rojo
Funcin no-miembro trata de acceder a datos

datos es una variable privada


Solucin: funcin de acceso a datos

Problema: esto debilita seguridad de la clase Str


Otra solucin: permitir que la funcin no-miembro

operator>> acceda a los datos privados de la clase Str


class Str { friend std::istream& operator>>(std::istream&, Str&);

2013 Mario Medina C.

Operador +=
Funcin miembro de Str
Modifica objeto Str actual
Str& operator+=(const Str& s) { std::copy(s.datos.begin(), s.datos.end(), std::back_inserter(datos)); return *this; }

Operador +
Funcin no-miembro de Str
Retorna nuevo objeto Str Hace uso de operador += recin definido
Str operator+(const Str& s, const Str& t) { Str r = s; r += t; return r; }

Clase Str (I)


class Str { friend std::istream& operator>>(std::istream&, Str&); public: Str& operator+=(const Str& s) { std::copy(s.datos.begin(), s.datos.end(), std::back_inserter(datos)); return *this; } typedef Vec<char>::size_type size_type; Str() { } Str(size_type n, char c) : datos(n , c) { } Str(const char * cp) { std::copy(cp, cp + std::strlen(cp), std::back_inserter(datos)); }

Clase Str (II)


template <class In> Str(In b, In e) { std::copy(b, e, std::back_inserter(datos)); } char& operator[](size_type i) { return datos[i]; } const char& operator[](size_type i) const { return datos[i]; } size_type size() const { return datos.size(); } private: Vec<char> datos; };

2013 Mario Medina C.

10

You might also like