Programación en C++ Plantillas: un mecanismo de polimorfismo Prof. José A. Rodríguez Mondéjar Prof. Álvaro Sánchez Miralles UPCO ICAI Departamento de Electrónica y Automática 1 Programación en C++ Polimorfismo a través de programación genérica • • • • Las plantillas permiten programar un clase o una función de forma genérica: es decir que valga para diferentes tipos de datos. Ejemplo: la clase vector definida para enteros, tiene el mismo código para doubles. El tipo genérico utilizado en la plantilla debe soportar todos las operaciones que se realizan en dicha plantilla: – En la plantilla de vectores genéricos no se admite las operaciones de sumar o multiplicación escalar porque los elementos pueden no se numéricos (modelos de coche, ventanas, etc) Se puede sobrecargar las funciones miembro de las plantillas para casos especiales: – Cuando se multiplican vectores numéricos se aplica la definición de producto escalar – Cuando se multiplican vectores no numéricos se da un error. Prof. José A. Rodríguez Mondéjar Prof. Álvaro Sánchez Miralles UPCO ICAI Departamento de Electrónica y Automática 2 Programación en C++ Plantilla para la clase de vectores template <class tipo> class TVec { tipo es el tipo genérico. Cuando private: se crea un objeto de dicha clase int m_nEle; hay que concretar el tipo. tipo *m_p; Tipo genérico Puede haber más de un tipo genérico. public: TVec(int nEleP=0); TVec(const TVec<tipo>& P); ~TVec(); TVec& operator=(const TVec<tipo>& P); tipo& operator[](int n); tipo& operator[](int n) const; friend ostream& operator<<(ostream &o,TVec<tipo>& v); friend istream& operator>>(istream &o,TVec<tipo>& v); private: void Ini(int nEleP, tipo *pP); void Borra(); TVec<tipo> es en general el tipo #ifdef DEBUGVECTOR de la clase mientras no haya que void DebugCrea(); concretar el tipo #endif }; Prof. José A. Rodríguez Mondéjar Prof. Álvaro Sánchez Miralles UPCO ICAI Departamento de Electrónica y Automática 3 Programación en C++ Implantación de la plantilla de vectores (I) #ifndef vector_h #define vector_h vec.h #define DEBUGVECTOR #include <iostream.h> #include <assert.h> template <class tipo> class TVec { private: int m_nEle; tipo *m_p; public: TVec(int nEleP=0); TVec(const TVec<tipo>& P); ~TVec(); TVec& operator=(const TVec<tipo>& P); tipo& operator[](int n); tipo& operator[](int n) const; friend ostream& operator<<(ostream &o,TVec<tipo>& v); friend istream& operator>>(istream &o,TVec<tipo>& v); private: // Resto en transparencia anterior }; Prof. José A. Rodríguez Mondéjar Prof. Álvaro Sánchez Miralles UPCO ICAI Departamento de Electrónica y Automática 4 Programación en C++ Implantación de la plantilla de vectores (II) vec.h template <class tipo> TVec<tipo>:: TVec(int nEleP){ #ifdef DEBUGVECTOR DebugCrea(); #endif Ini(nEleP,0); } Se ha modificado Ini respecto de la versión con int: no hay valor por defecto. Si se hubiera incluido supone crear un objeto del tipo correspondiente, inicializado con 0. Para ser más eficiente se ha quitado. Hay que inicializar los elementos uno a uno, o tener una función miembro de inicialización. template <class tipo> TVec<tipo>:: TVec(const TVec<tipo>& o) { #ifdef DEBUGVECTOR DebugCrea(); #endif Ini(o.m_nEle,o.m_p); } #ifdef DEBUGVECTOR template <class tipo> void TVec<tipo>:: DebugCrea(){ cout<<"Creado objeto TVec "<<this<<endl; } #endif Prof. José A. Rodríguez Mondéjar Prof. Álvaro Sánchez Miralles UPCO ICAI Departamento de Electrónica y Automática 5 Programación en C++ Implantación de la plantilla de vectores (III) template <class tipo> void TVec<tipo>:: Ini(int nEleP, tipo *pP){ m_nEle=nEleP; if (m_nEle<=0) { // Vector nulo m_nEle=0; m_p=0; return; } m_p=new tipo[m_nEle]; assert(m_p!=0); if (pP!=0) { // Inicializar con otro for (int i=0;i<m_nEle;i++) m_p[i]=pP[i]; return; } } template <class tipo> TVec<tipo>:: ~TVec(){ #ifdef DEBUGVECTOR cout<<"Destruido objeto TVec "<<this<<endl; #endif Borra(); } Prof. José A. Rodríguez Mondéjar Prof. Álvaro Sánchez Miralles vec.h El código de las plantillas no es código final porque no se sabe el tipo. Sólo cuando se instancia la clase con un tipo concreto el compilador genera el código final. Por esta razón los compiladores no suelen admitir el código de las plantillas en los ficheros *.cpp. Hay que colocarlos en los *.h en los *.hpp (y se incluyen en el *.h). UPCO ICAI Departamento de Electrónica y Automática 6 Programación en C++ Implantación de la plantilla de vectores (IV) template <class tipo> tipo& TVec<tipo>:: operator[](int n) { assert((n>=0)&&(n<m_nEle)); return m_p[n]; } template <class tipo> tipo& TVec<tipo>:: operator[](int n) const { assert((n>=0)&&(n<m_nEle)); return m_p[n]; } vec.h Las funciones constantes permiten asegurar que esa función no va a cambiar el contenido del objeto. template <class tipo> TVec<tipo>& TVec<tipo>:: operator=(const TVec<tipo>& o){ if (this!=&o) { Borra(); Ini(o.m_nEle,o.m_p); } return *this; } Prof. José A. Rodríguez Mondéjar Prof. Álvaro Sánchez Miralles UPCO ICAI Departamento de Electrónica y Automática 7 Programación en C++ Implantación de la plantilla de vectores (V) template <class tipo> istream& operator>>(istream &o,TVec<tipo>& v) { v.Borra(); o>>v.m_nEle; v.Ini(v.m_nEle,0,0); for(int i=0;i<v.m_nEle;i++) { o>>v[i]; } return o; } template <class tipo> ostream& operator<<(ostream &o,TVec<tipo>& v) { o<<v.m_nEle<<" "; for (int i=0;i<v.m_nEle;i++) { o<<v[i]<<" "; } o<<endl; return o; } Prof. José A. Rodríguez Mondéjar Prof. Álvaro Sánchez Miralles vec.h También se pueden construir plantillas de funciones. Este caso son las funciones amigas del vector encargadas de escribir y leer UPCO ICAI Departamento de Electrónica y Automática 8 Programación en C++ Prueba de la plantilla de vectores #include <iostream.h> #include "vector.h" main() { TVec<int> vi1(2),vi2(3); vi1[0]=-2; vi1[1]=-30; vi2[0]=-4; vi2[1]=8; vi2[2]=104; cout<<"vi1: "<<vi1; cout<<"v12: "<<vi2; TVec<double> vd(3); vd[0]=1.2; vd[1]=2.3; vd[2]=3.4; cout<<"vd: "<<vi2; TVec< TVec<int> > vvi(2); vvi[0]=vi1; vvi[1]=vi2; cout<<vvi; cin>>vvi; cout<<vvi; } Creado objeto TVec 0x0012ff84 Creado objeto TVec 0x0012ff7c vi1: 2 -2 -30 v12: 3 -4 8 104 Creado objeto TVec 0x0012ff74 vd: 3 1.2 2.3 3.4 Creado objeto TVec 0x0012ff6c Creado objeto TVec 0x00883214 Creado objeto TVec 0x0088321c 2 2 -2 -30 3 -4 8 104 Destruido objeto TVec 0x0088321c Destruido objeto TVec 0x00883214 2 Creado objeto TVec 0x00883214 Creado objeto TVec 0x0088321c 3 -1 -2 -3 5 0 1 234 2 3 -1 -2 -3 5 01234 Destruido objeto TVec 0x0012ff6c Destruido objeto TVec 0x0088321c Destruido objeto TVec 0x00883214 Destruido objeto TVec 0x0012ff74 Destruido objeto TVec 0x0012ff7c Destruido objeto TVec 0x0012ff84 Prof. José A. Rodríguez Mondéjar Prof. Álvaro Sánchez Miralles Vector de vectores o de lo que quiera con una sola línea de código UPCO ICAI Departamento de Electrónica y Automática 9 Programación en C++ Resumen sobre plantillas • • • • Mediante el uso de tipos genéricos un solo código sirve para diferentes clases. Cada clase concreta debe implantar todas las operaciones que pide la clase o función plantilla. Puntos que se deben chequear que no haya problemas (especialmente si hay manejo de memoria dinámica): – Constructor sin parámetros. – Constructor de copia – Asignación desde otro elemento del mismo tipo Al igual que en la sobrecarga de operadores, se debe vigilar la eficiencia del código. Prof. José A. Rodríguez Mondéjar Prof. Álvaro Sánchez Miralles UPCO ICAI Departamento de Electrónica y Automática 10