Curso de Programación Avanzada en C

28
Curso de programación avanzada en C+ + Normas de estilo en C++ Igual que escribir en un idioma tiene una serie de normas de estilao, que, si se cumplen, hacen el texto más comprensible y elegante, igual los lenguajes de programación tienen una serie de normas de estilo, para hacer su código más elegante, comprensible e incluso fácil de depurar. En C++, las normas de estilo parten del diseño correcto de las clases, de la utilización correcta de la herencia y de la encapsulación, y del aprovechamiento de todas las capacidades de C++. Simultáneamente, veremos como los conceptos fundamentales de la programación orientada a objetos, tales como la herencia y encapsulación se implementan en C++. Forma canónica La forma canónica es la forma ortodoxa de declarar una clase en C++. Permite evitar problemas de programación, y permite que una clase declarada de esta forma se pueda usar de la misma manera que cualquier tipo tradicional de C. En la forma canónica, evidentemente, tienen que entrar el constructor y el destructor. class miClase {

description

Curso baasico de programacion dentro del compilador DEVC++

Transcript of Curso de Programación Avanzada en C

Curso de programacin avanzada en C++Normas de estilo en C++Igual que escribir en un idioma tiene una serie de normas de estilao, que, si se cumplen, hacen el texto ms comprensible y elegante, igual los lenguajes de programacin tienen una serie de normas de estilo, para hacer su cdigo ms elegante, comprensible e incluso fcil de depurar. En C++, las normas de estilo parten del diseo correcto de las clases, de la utilizacin correcta de la herencia y de la encapsulacin, y del aprovechamiento de todas las capacidades de C++.Simultneamente, veremos como los conceptos fundamentales de la programacin orientada a objetos, tales como la herencia y encapsulacin se implementan en C++.Forma cannicaLa forma cannica es la forma ortodoxa de declarar una clase en C++. Permite evitar problemas de programacin, y permite que una clase declarada de esta forma se pueda usar de la misma manera que cualquier tipo tradicional de C.En la forma cannica, evidentemente, tienen que entrar el constructor y el destructor.class miClase { public: /// Constructor miClase() {};

/// Destructor ~miClase() {};}Habitualmente se cae en la tentacin de no declarar ni el constructor ni el destructor si se utilizan los constructores por defecto; de hecho, los compiladores generan automticamente el cdigo para ellos; sin embargo, siempre es bueno hacerlo, aunque sea slo para guardarles sitio, porque ms adelante podra interesar cambiarlos por otro tipo de constructor y no sabramos donde meterlos. Los comentarios tambin son esenciales, aunque sean redundantes. En este caso, se usan la convencion del doc++, que genera automticamente la documentacin; en caso de que no se incluyeran los comentarios, el usuario podra pensar mirando a la documentacin que no existe el constructor, o que se nos ha olvidado incluirlo. Otra razn por la que se debe incluir un constructor vaco es para que se puedan declarar contenedores de las STL que contengan ese tipo; algunos compiladores, tales como el Visual C++, lo exige.No solamente estos elementos son esenciales. El constructor de copia tambin lo es. El constructor de copia, aunque no lo parezca, se usa mucho. Por ejemplo, se est llamando al constructor de copia cuando se hacemiClase zape;miClase zipi=zape;o cuando se llama a un procedimiento o funcin por valor, en vez de por referencia (o usando punteros o referencias).void unMetodo( miClase _zipi ) { // hacer lo que sea con _zipi}

miClase pantuflo;unMetodo(pantuflo);El constructor de copia tambin lo genera el compilador por defecto, pero a veces puede que lo que haga ese constructor generado no sea lo que nosotros pretendemos que haga. Por ejemplo, si el objeto incluye punteros a objetos asignados por el mismo, ese constructor generado copiar los punteros, no a lo que apuntan (copia superficial). Incluyendo el constructor de copia. la forma cannica quedara asclass miClase {public: /// Constructor miClase() {};

/// Constructor de copia miClase( const miClase& _c ) { // Inicializar las variables de instancia }

/// Destructor ~miClase() {};}El objeto que se va a copiar se pasa como una referencia constante. El usar referencias para pasar parmetros a procedimientos y funciones es una buena costumbre: evita tener que enviar muchos bytes a la pila en caso de que se trate de un objeto complejo; adems, la referencia tiene que ser constante como una promesa de que no se le va a hacer nada al objeto dentro de esa funcin.Todava la forma cannica est incompleta. Qu ocurre si queremos copiar un objeto ya construido sobre otro ya construido? Har falta el operador de asignacin. Vamos a por lclass miClase {public: /// Constructor miClase() {};

/// Constructor de copia miClase( const miClase& _c ) { // Inicializar las variables de instancia }

/// Operador de asignacin const miClase& operator=( const miClase& _c ) { // Comprobacin si no es uno mismo if ( _c !=*this) { // Copiar variables de instancia } return *this; }

/// Destructor ~miClase() {};}El operador=devuelve una referencia al objeto para que se puedan hacer cosas tales comomiClase zape, zipi, pantuflo;zape=( zipi=pantuflo );Pero para que no se pueda hacer(zipi=pantuflo)=zapela referencia se hace constante. Dentro del operador de asignacin se tiene que comprobar si se est asignando el propio objeto sobre s mismo; sobre todo si hay que destruir y asignar memoria dinmica, y luego copiarla.En algunos casos puede que no se quiera usar alguno de estos elementos, o que no tenga sentido. En tal caso, deberan declararse protected o bien private.En todo esto, dnde deberan ir las variables de instancia? No en cualquier sitio, no. Aqu es donde entra la encapsulacin. Encapsulacin de un objeto significa regular el acceso a su interior a travs de un interface. Si ponemos tales variables en el interface (es decir, la ponemos en la parte public o protected), la regulacin del acceso se va claramente a tomar por saco. Si est en la parte public, porque cualquiera puede cambiar su valor, y si est en la parte protected, porque puede cambiarlo cualquier objeto de la jerarqua de clases.class miClase {public: /// Constructor miClase(): repVariableInstancia1(),repVariableInstancia2(){};

/// Constructor de copia miClase( const miClase& _c ) : repVariableInstancia1( _c.repVariableInstancia1), repVariableInstancia2( _c.repVariableInstancia2) { // Inicializar las otras variables de instancia }

/// Operador de asignacin const miClase& operator=( const miClase& _c ) { // Comprobacin si no es uno mismoif ( _c !=*this) { // Copiar variables de instancia}return *this; }

/// Destructor ~miClase() {};

private: tipoVariable repVariableInstancia1; otroTipoVariable repVariableInstancia2;}Aparte de introducir las variables de instancia, hemos introducido su inicializacin en los dos constructores que tenemos a mano, y lo hemos hecho usando lalista de inicializacin. Esta forma es mucho ms eficiente, y evita la construccin de variables temporales (sobre todo en el constructor de copia). Pero algunas veces puede uno querer cambiar los valores de estas variables. As no se puede! Bueno, en general, las clases deberan disearse de forma que no se necesitara acceder o siquiera saber el nombre de las variables de instancia (el ideal en programacin dirigida a objetos es separar totalmente el interfaz de la implementacin), pero si alguna clase derivada contumaz quiere acceder a ellas, se deberan declarar funciones dentro del interfaz protegido.class miClase {public: /// Constructor miClase() {};

/// Constructor de copia miClase( const miClase& _c ) { // Inicializar las variables de instancia }

/// Operador de asignacin const miClase& operator=( const miClase& _c ) { // Comprobacin si no es uno mismoif ( _c !=*this) { // Copiar variables de instancia}return *this; }

/// Destructor ~miClase() {};

protected: tipoVariable& variableInstancia1() { return repVariableInstancia1; }

const tipoVariable& variableInstancia1() const { return repVariableInstancia1; }

private: tipoVariable repVariableInstancia1; otroTipoVariable repVariableInstancia2;}El lector avezado podr pensar que estas dos declaraciones son iguales; y efectivamente lo son. Para ser exactos, la funcin variableInstancia1 estsobrecargada,es decir, que hace dos funciones diferentes dependiendo del contexto en el que sea invocada. Cul es en este caso el contexto? El contexto viene determinado por el const delante y detrs de la segunda declaracin. Ese const indica que, aparte de devolver una referencia constante, es decir, que no se puede asignar un valor a lo que devuelva esa funcin, no altera el contenido del objeto (el segundo const), con lo cual podemos llamar a esta funcin desde aquellas otras funciones a las que se les pase un objeto const.Nunca se deben devolver referencias o punteros no constantes a las variables de instancia, ni siquiera en el interfaz protegido, porque si no la encapsulacin se va a hacer pueta: cualquiera podra asignarles un valor desde fuera, o alterar un puntero. Las referencias o punteros que se devuelvan deben ser constantes, a no ser que efectivamente queramos que le asignen valor desde fuera, algo bastante poco aconsejable.Ya tenemos una clase completa declarada; hay que fijarse en el orden de la declaracin. Pensando en los clientes, sobre todo, la parte pblica debe ir la primera, porque ah es donde mirarn los programadores para ver qu es lo que pueden usar; otra categora de programadores mirar a la parte protected, si le interesa heredar de la clase; y los listillos, por ltimo, mirarn a la parte privada pensando: "Hum, este nombre de variable no me convence", o bien "Si pusiera esto en la parte pblica, me ahorrara muchas cosas". Pues no!Tambin hemos hecho algo que no tiene porqu sentarle bien a todo el mundo: incluir la definicin de varias funciones al lado de la declaracin, en el fichero de cabecera. Aunque algunas guas de estilo lo indiquen as, en otros casos, como en casi todos los ejemplos de las STL, aparecen como arriba, y en todo caso es mucho ms cmodo.

El interfaz de una claseEl interfaz de una clase es lo ms importante de la misma, casi me atrevera a decir que ms importante que su implementacin. Cuando se piensa en un programa, se debe pensar en los objetos que hay en el mismo y en sus interfaces; la implementacin vendr luego. Incluso, con un interfaz bien diseado, la mitad de la implementacin est hecha. Por eso es tan importante.Como ya hemos visto, el interfaz tiene dos partes: la pblica y la protegida. La pblica es lo que la clase ofrece a los clientes, y la protegida es la que le ofrece a los clientes y los descendientes. La regla que hay que seguir es queel interfaz debe ser mnimo y completo,mnimo porque debe incluir slo lo necesario, y completo, porque debe incluir todas las operaciones necesarias para que se pueda usar la clase como nosotros pretendemos (y slo como nosotros pretendemos: no olvidemos que si algo se puede hacer, se har). Una clase con 2 o 3 funciones aparte de las de la forma cannica es el ideal.Esta regla se aplica a cada uno de los interfaces. En la parte privada, uno puede meter todo lo que le d la gana, que para eso es privada.Algo que hay que tener en cuenta aqu es que las clasesfriendforman tambin parte del interfaz de una clase, puesto que pueden acceder a sus variables privadas de instancia. Por ello, caben dos opciones. La primera es no usar nunca los friends: meter todo lo necesario directamente dentro de la declaracin de la clase. Segunda, usarlos aplicando la norma anterior: mantener el interfaz mnimo, es decir, dejar en las clases amigas alguna parte del interfaz que por sus caractersticas deba estar aparte.Las funciones constantes (const) se deben decidir desde el principio del diseo. Si una funcin no altera ninguna variable de instancia de un objeto, debe ser declarada constante. Estas sern las nicas funciones que se pueden que invocar desde variables declaradas como punteros o referencias constantes a un objeto. As, adems, controlas claramente el acceso a tu clase: slo aquellos mtodos declarados como no-constantes alterarn al objeto, y tomaremos las medidas pertinentes.void unaFuncion( const miClase&_param ) { tipoVariable unaVar=_param.variableInstancia1(); // Correcto tipoVariable otraVar; _param.variableInstancia1()=otraVar; // Error de compilacin}En este caso, en la primera invocacin del mtodovariableInstancia1()se est llamando a la versin constante del mismo, y por lo tanto no hay problema; en este caso se deduce que es la versin constante por el contexto: no se est haciendo nada para alterar el contenido del objeto. Sin embargo, en el segundo caso, indicado en rojo, se est llamando al mtodo no-constante, el que devuelve una referencia a repVariableInstancia1, lo cual el compilador deduce una vez ms por el contexto: se le est tratando de asignar un valor. Como _param est declarado como una referencia constante, el compilador detecta un error. Los mtodos constantes, a su vez, son los nicos que se pueden llamar desde otros mtodos constantes.Ejercicio:disear una clase Polinomio que permita acceder a cada uno de sus coeficientes, imprimirlo como polinomio, y hacer alguna operacin aritmtica tal como suma y resta.De tal palo, tal astillaYa hemos visto como se construye una clase de forma que la encapsulacin se respete, y el interfaz est bien diseado; pero el otro aspecto que hace de C++ un lenguaje orientado a objetos (o dirigido a objetos) es laherencia: la posibilidad de reusar las clases ya definidas, en todo o en parte, para hacer nuevas clases. Al grupo de todas las clases que descienden de una clase principal se le denominajerarqua de clases. A la clase madre o padre se le puede llamar tambinsuperclaseoclase base, mientras que a las clases descencientes se les llama tambinsubclasesoclases derivadas. Por tanto, una subclase hereda de una superclase, y ambas juntas (y todas las dems) constituyen una jerarqua de clases.En general, la herencia en C++ expresa la relacines-un. Una subclase es-una superclase, pero con algunas diferencias. Lo que no queda claro es que es esa relacin. En otros lenguajes est un poco ms claro. Por ejemplo, en Objective-C hay dos tipos de herencia: herencia de cdigo, y herencia de interfaz (cuando un objeto implementa un interfaz que se ha declarado anteriormente). En Java, e incluso en Visual Basic, se pueden declarar interfaces como objetos de pleno derecho, y la herencia de tales interfaces (habitualmente denominadaimplementacin, va por un lado diferente que la herencia de cdigo. En C++, la herencia es una mezcla de las dos: se hereda a veces interfaces, a veces implementacin, a veces ambos. En el siguiente ejemplo:class miClase { // Dejar todo lo dems, y aadir alguna funcin /// Mtodo puesto solo para heredar void metodo( void ) const; { // Una implementacin del mtodo }}/// Esta subclase es una subclase tonta que no hace nada en realidadclass miSubclase: public miClase { /// Mtodo puesto solo para heredar void metodo( void ) const; { }}Qu est heredando, en realidad,miSubclasedemiClase? Algunas cosas. Est heredando el interfaz, puesto que tiene una funcin con el mismo nombre que la clase base, pero no la implementacin, puesto que est redefiniendo el codigo de un mtodo (apropiadamente llamadometodo) definido en la clase base. Aparte de eso, hereda bien poco: ninguno de los constructores ni destructores. S toma de la clase base parte del cdigo: las funciones protegidas y las variables de instancia, aunque no puede acceder a stas porque estn declaradas como privadas.Sin embargo, esta declaracin puede causar problemas a la hora de trabajar con punteros o referencias a objetos de la clase:miClase& refZipi;miSubclase& refZape;miSubclase zipi;refZipi = zipi;refZipi.metodo(); // Llama a miClase::metodorefZape = zipi;refZape.metodo(); // Llama a miSublase::metodoCon punteros sucederia algo similar, pero los punteros causaran an ms problemas a la hora de destrur el objeto al que apuntan:miClase* pZipi = new miSubclase(); // Conversionautomtica de puntero de clase derivada a clase basedelete pZipi; // Correcto, pero llamara a miClase::~miClase()Para resolver todos estos problemas se inventaron las funcionesvirtuales. En C++, una funcn declarada como virtual se asocia al cdigo en concreto que va a ejecutar en tiempo de ejecucin, no en tiempo de compilacin como ocurre con el resto de las funciones. Para ello, cada objeto perteneciente a una clase con funciones virtuales contiene una estructura de datos denominadavptrque contiene punteros a todas las implementaciones de funciones virtuales a lo largo y ancho de la jerarqua de clases. Todo esto, por supuesto, supone un aumento del espacio de almacenamiento para los objetos de la clase; y adems supone el gasto de un direccionamiento indirecto adicional cada vez que se llama a un mtodo virtual. Algo a tener en cuenta, si la velocidad y el espacio son esenciales. Que no deberan serlo: para solucionar esos problemas est el hardware.Usando funciones virtuales, y declarando la subclase tambin en forma cannica, las dos clases anteriores quedaran as:class miClase {// eliminados los comentarios y cdigo para dejarlo todo ms claropublic: miClase() {}; miClase( const miClase& _c ); const miClase& operator=( const miClase& _c ); virtual ~miClase() {}; virtual void metodo( void ) const;protected: tipoVariable& variableInstancia1(); const tipoVariable& variableInstancia1()constprivate: tipoVariable repVariableInstancia1; otroTipoVariable repVariableInstancia2;}

/// Esta subclase es una subclase tonta que no hace nada en realidadclass miSubclase: public miClase {public: miSubclase(): miClase(), varInstSubclase() {}; miSubclase( const miSubclase& _c ) :miClase( _c ), varInstSubclase( _c.varInstSubclase) {}; const miClase& operator=( const miClase& _c ); virtual ~miClase() {}; virtual void metodo( void ) const;// resto del codigoprivate: tipoVar varInstSubclase;

}En este ejemplo, aparte de declarar el destructor de la clase derivada y el mtodo redefinido como virtual, se han aadido una serie de novedades que indican la forma cannica de inicializar la clase base y las variables de instancia: se deben inicializar por ese orden, y si es posible, usando la lista de inicializacin (es decir, tal como se ha hecho arriba: dos puntos detrs del nombre del constructor, y las variables y superclases a inicializar separadas por comas). El usar la lista de inicializacin permite que se genere cdigo ms eficiente, y permite detectar en tiempo de compilacin los errores en el orden de inicializacin de las variables.Adems, hemos convertido en virtuales todos los destructores y elmetodo. De hecho, todas las funciones virtuales de una jerarqua de clase constituyen elinterfaz de la jerarqua, y no el interfaz de una clase en particular dentro de la jerarqua; por ello hay que prestar especial cuidado al diseo de este interfaz, tanta o ms que al diseo del interfaz de cada clase. Aunque es casi imposible disear una jerarqua de clases que solamente tenga funciones virtuales, se puede intentar, concentrando todas las diferencias entre miembros de la jerarqua en el constructor.No se debe usar la herencia para expresar relaciones tales comoes parte de: en tal caso debera usarse herencia privada; ni tampoco la relacinusa-un, en cuyo caso deber ponerse el objeto usado como variable de instancia. Por ejemplo, un cochees-unvehculo, y un motores-parte-deun coche; por lo tanto, la clase coche debera heredar de la clase vehlo y debera contener una variable de instance de la clase motor.Aunque en este cuadro aparezcan las dos clases juntas, lo ms conveniente es que vayan separadas cada una en su fichero; y si es posible, el interfaz separado tambin de la implementacin, cada uno en su fichero (incluso aunque se trate de templates). Lo ms universal es nombrar los ficheros de cabecera con la extensin.hy los que contienen la implementacin con.cpp; la mayora de los compiladores entienden estas extensiones, aunque para que funcionen correctamente losMakefilesen Unix deber aadirse una orden. Los ficheros tendrn el mismo nombre de la clase, incluso teniendo en cuenta maysculas y minsculas; los tiempo en que tenia uno que poner nombres de ficheros con 8 caracteres estn afortunadamente periclitados, y cualquier sistema operativo decente admite nombres largos (aunque algunos supuestamente decentes, como Windows NT, todava confundan maysculas y minsculas.Las funciones virtuales sirven para una cosa ms: dado que definen el interfaz de una clase, existen en C++ lasfunciones virtuales purasque slo definen un interfaz, sin aportar (habitualmente) ningn cdigo, y obligando adems a las clases derivadas a implementar ese cdigo. Un caso clsico de estas funciones o mtodos virtuales puros es el mtodo que imprime la clase a un canal de salida:class miClase {// eliminados los comentarios y cdigo para dejarlo todo ms claropublic: miClase() {}; miClase( const miClase& _c ); const miClase& operator=( const miClase& _c ); virtual ~miClase() {}; virtual void metodo( void ) const; virtual void printSelf(ostream& _o) const = 0;// mtodo virtual puroprotected: tipoVariable& variableInstancia1(); const tipoVariable& variableInstancia1()constprivate: tipoVariable repVariableInstancia1; otroTipoVariable repVariableInstancia2;}

/// Esta subclase es una subclase tonta que no hace nada en realidadclass miSubclase: public miClase {public: miSubclase(): miClase(), varInstSubclase() {}; miSubclase( const miSubclase& _c ) :miClase( _c ), varInstSubclase( _c.varInstSubclase) {}; const miClase& operator=( const miClase& _c ); virtual ~miClase() {}; virtual void metodo( void ) const; virtual void printSelf(ostream& _o) const;// El cdigo ira en otro ladoprivate: tipoVar varInstSubclase;}Los mtodos virtuales puros convierten a nuestra archiconocida clase base en unaclase base abstracta (CBA). En cierto modo, una CBA declara un interfaz solamente, y deja a las clases derivadas la tarea de definir implementaciones. Este hecho se puede usar en conjuncin con los templates, para definir qu tipo de clases pueden instanciar un template determinado. Habitualmente, cuando se escribe un template se debe de especificar claramente cual es el interfaz que debe tener la clase para que sea posible instanciarlo; sin embargo, esto da lugar a descripciones ms bien largas y puede ser problemtico. En vez de eso, es mejor declarar una CBA que tenga ese interfaz, e indicar simplemente que ese template se puede instanciar solamente con clases que desciendan de esa CBA. Por ejemplo/** Este comentario sigue la sintaxis del doc++Esta funcion solo puede instanciarse con aquellos objetos quetengan los mtodos a() y b( foo& ) */template void baz( const T& _t){ _t.a();// y ms cosas foo miFoo; _t.b( miFoo );}

// Pero es ms fcil hacerlo asclass miCBA{public: void a() = 0; void b( foo& ) = 0;}/** T debe descender de la clase miCBA */template void baz( const T& _t){ _t.a();// y ms cosas foo miFoo; _t.b( miFoo );}Ms formas de construirAunque un constructor propio, con ms o menos sofisticacin es la forma habitual de construir una clase, hay clases que no tienen porqu saber como construirse. O, en algunos casos, toda la complejidad del constructor de un objeto hay que encapsularla en otro objeto, que sabr como construirlo. Por ejemplo, en entornos de aplicaciones tales comoCOM(Common Object Model, de Microsoft), es necesario disear factoras para todos los objetos, de forma que los clientes que los usen no tengan que saber como construir cada objeto especfico.En algunos casos tambin, objetos complejos, compuestos de referencias y punteros a otros objetos, ni siquiera pueden construirse a s mismos, porque no pueden asignar valores a las referencias que usan; tampoco deberan asignar memoria a sus propios punteros, pues en ese caso no quedara claro quin tendra que desasignarlo, si el propio objeto o quien ha creado los punteros. En esta seccin incluiremos una serie de patrones para construir objetos de diferente forma.En algunos casos es necesario tener unconstructor de copia virtual. Ninguno de los constructores se heredan, ni siquiera el operador de asignacin, pero cuando estamos usando referencias o punteros a una clase base, y se quiere copiar el objeto, no se pueden usar los constructores de copia, que copiarn el objeto de la clase base o bien simplemente el puntero. Se puede incluir el constructor de copia virtual de la forma siguienteclass miClase {// eliminados los comentarios y cdigo para dejarlo todo ms claropublic: miClase() {}; miClase( const miClase& _c ); const miClase& operator=( const miClase& _c ); virtual ~miClase() {}; virtual void metodo( void ) const; /** Ctor virtual de copia; devuelve un puntero constante para que no se pueda usar como operador de asignacin. Sin implemtacin: Un objeto que no se puede instanciar no sepuede copiar */ virtual const miClase* clone() const = 0; virtual void printSelf(ostream& _o) const = 0;protected: tipoVariable& variableInstancia1(); const tipoVariable& variableInstancia1()constprivate: tipoVariable repVariableInstancia1; otroTipoVariable repVariableInstancia2;}

class miSubclase: public miClase {public: miSubclase(): miClase(), varInstSubclase() {}; miSubclase( const miSubclase& _c ) :miClase( _c ), varInstSubclase( _c.varInstSubclase) {}; const miClase& operator=( const miClase& _c ); virtual ~miClase() {}; virtual void metodo( void ) const; virtual const miClase*clone() const;{ return newmiSubclase(*this) } virtual void printSelf(ostream& _o) const;private: tipoVar varInstSubclase;}La funcincloneactuara como un constructor de copia virtual, produciendo nuevas copias del objeto dondequiera que est en la jerarqua de clases. Por ejemplo, se pueden hacer cosas as:class miClase {// eliminados los comentarios y cdigo para dejarlo todo ms claropublic: miClase() {}; miClase( const miClase& _c ); const miClase& operator=( const miClase& _c ); virtual ~miClase() {}; virtual void metodo( void ) const; /** Ctor virtual de copia; devuelve un puntero constante para que nose pueda usar como operador de asignacin. Sin implemtacin: Un objeto que no se puede instanciar no sepuede copiar */ virtual const miClase* clone() const = 0; virtual void printSelf(ostream& _o) const = 0;protected: tipoVariable& variableInstancia1(); const tipoVariable& variableInstancia1()constprivate: tipoVariable repVariableInstancia1; otroTipoVariable repVariableInstancia2;}

class miSubclase: public miClase {public: miSubclase(): miClase(), varInstSubclase() {}; miSubclase( const miSubclase& _c ) :miClase( _c ), varInstSubclase( _c.varInstSubclase) {}; const miClase& operator=( const miClase& _c ); virtual ~miClase() {}; virtual void metodo( void ) const; virtual const miClase*clone() const;{ return newmiSubclase(*this) } virtual void printSelf(ostream& _o) const;private: tipoVar varInstSubclase;}Lo que permite usar esta funcin de la forma siguiente:miClase& pMiClase;miClase zipi;miSubclase zape;pMiClase = zipi;miClase* nuevoZipi = pMiClase.clone();// Llama a miClase::clone()pMiClase = zape;miClase* nuevoZape = pMiClase.clone();// Llama a miSubclase::clone()La ltima versin del C++ permite incluso declarar la funcin clone as: virtual const miSubclase* clone() const;{ return new miSubclase(*this) }Es decir, aquellas funciones que devuelvan punteros a la clase base pueden ser sustituidas en la clase derivada por funciones que devuelvan punteros a la misma. Lo mismo ocurre con las referencias.