Enseñando programación con C++: una propuesta didáctica Jorge Díaz Universidad de Oriente, Departamento de Computación, Santiago de Cuba 90500, Cuba jdiaz@csd.uo.edu.cu Resumen: Se hace un análisis crítico acerca de las características del lenguaje C++ con vistas a su utilización como lenguaje en una primera asignatura de programación de alto nivel orientada a objetos y se expone una metodología para impartir tal asignatura, la cual incluye una selección y secuencia de los contenidos que hacen más viable el uso del lenguaje en la enseñanza de la programación y que ha arrojado resultados superiores en cuanto al aprendizaje de los estudiantes. Palabras clave: lenguajes de programación, orientación a objetos, enseñanza de la programación Abstract: A critical analysis about the characteristics of the programming language C++, regarding its use in a first course of high level programming, is made. It is exposed a methodology for teaching this course using of a fruitful way this language. Key words: programming languages, object oriented, programming teaching. 1. Introducción Siempre ha resultado un dilema la selección de cuál debe ser el lenguaje que de entrada usaremos para enseñar a los estudiantes sus primeros conceptos de programación, máxime cuando el proceso evolutivo de la rama es extremadamente rico y rápido. Diferentes lenguajes se han usado en un primer curso de programación, podemos citar Pascal, Ada, Modula, C, C++, C#, Java. Los criterios de selección no se desligan de las necesidades de programación del sector industrial, científico y empresarial, los cuales, en muchos casos, gravitan en aplicaciones que usan el C/C++ como lenguaje de trabajo, lo cual tiene su influencia a la hora de decidir cuál será el lenguaje básico de partida, con vistas a acortar el trayecto entre aprendizaje y empleo. De hecho, en (Tonella, 2002) se expresa la conveniencia de adoptar ciertas reglas que permitan continuar usando el lenguaje C++ en el desarrollo de sistemas, fundamentalmente donde la introducción del Java aún no parece adecuada. Es bien sabido que el lenguaje C++ presenta ciertos problemas que de entrada no lo hacen aparecer un buen candidato para comenzar los estudios de la programación, tal y como se analiza en (Kolling, 1999), aún así es elegido en una parte no despreciable de cursos universitarios del área informática. __________________________________________________________________________________________________________________ Revista de Informática Educativa y Medios Audiovisuales Vol. 3(7), págs. 12-21. 2006 ISSN 1667-8338 © LIE-FI-UBA. liema@fi.uba.ar 12 Enseñando programación con C++: una propuesta didáctica ____________________________________________________________________________________________________________________ Los propósitos de un primer curso de programación deben concentrarse en ofrecer a los estudiantes: • Conocimientos de los conceptos de programación y un vocabulario para discutirlos; • Habilidades de análisis y de programación; • Un marco de referencia para la teoría y disciplina de la programación. Debemos asegurarnos que los estudiantes aprenden a programar, en vez de aprender un lenguaje de programación. De otro lado, la mayoría de los textos de programación prestan en general poca atención a principios didácticos, presentando los contenidos en un orden conceptual y prestando poca atención a los principios metodológicos de solución de problemas o cuestiones de depuración de los errores, que son áreas probadamente problemáticas para los novicios en la programación. Es por ello que nos proponemos hacer una reflexión crítica de cómo emplear en lenguaje de programación C++ dentro de un primer curso de programación en ambientes donde por cuestiones de diversa índole se insista o aconseje su empleo y consecuentemente dar una metodología de enseñanza en que aún empleando el lenguaje C++ podamos alcanzar los objetivos formativos de programación para los estudiantes que reciben por primera vez la asignatura de programación, basada en el enfoque orientado a objetos, de tal forma que podamos usar de una manera didáctica y sistémica los libros de textos del lenguaje existentes. 2. Requerimientos para un primer lenguaje de programación orientado a objetos Presentamos a continuación algunas características que son deseables en un lenguaje usado en un primer curso de programación, siguiendo las ideas de (Kolling, 1999), junto con un análisis crítico de la característica para el lenguaje C++: 1. Claridad conceptual: Los conceptos del lenguaje deben ser claros, fáciles de comprender y bien definidos. Esos conceptos deben ser representados de la misma manera en que queremos expresarlos cuando los enseñamos. La implementación del lenguaje debe reflejar el nivel de abstracción que queremos usar en nuestros modelos conceptuales. Un lenguaje de programación proporciona a la vez un marco conceptual para pensar acerca de los algoritmos y un medio de expresar esos algoritmos. El lenguaje debe constituir una ayuda para el programador incluso antes de la etapa misma de codificación. Debe proveer un conjunto claro, sencillo y unificado de conceptos que se puedan usar como primitivas en el desarrollo de algoritmos. 2. Modelo puro de objetos: El lenguaje debe exhibir orientación a objetos “pura”, en el sentido de no poseer estructuras alternativas para construir objetos, sino sólo la abstracción básica usada para la programación, en contraposición con lenguajes híbridos, o sea, lenguajes que soportan la orientación a objetos junto con otros paradigmas no orientados a objetos. 3. Seguridad: Los errores deben ser fácilmente detectables lo más tempranamente posible por el compilador o el sistema de tiempo de ejecución y ser reflejados con mensajes que ilustren su causa. Deben evitarse conceptos conducentes a la escritura de programas erróneos. Debe tener un sistema de tipos con chequeo fuertemente estático, evitar punteros explícitos, hacer chequeos de límites de los índices de los arreglos y uso de variables no inicializadas. 4. Alto nivel: El lenguaje no debe contener información concerniente al bajo nivel de máquina, como por ejemplo, administración explícita de almacenamiento dinámico y debe proveer una colección de basura automática. 5. Modelo sencillo de ejecución y de representación de los objetos de datos: Debe poseer un modelo de ejecución bien definido y fácilmente comprensible. Debe ser transparente al programador el modelo de memoria adoptado para los objetos de datos que se manipulan, sean del stack o del heap. 6. Sintaxis legible: El lenguaje debe poseer una sintaxis consistente y fácilmente legible. Un programa legible es más fácil de corregir. Las palabras son más intuitivas, en muchos casos, que los símbolos para expresar las construcciones del lenguaje. 7. No ser redundante: El lenguaje debe constar de pocas construcciones, debe ser claro y evitar redundancias. Para los principiantes la flexibilidad que puede aportar la redundancia se convierte a menudo en confusión, más que en eficiencia. 8. Pocas construcciones: El lenguaje debe ser tan corto como sea posible, siempre que incluya todas ___________________________________________________________________________________________________________________ Revista de Informática Educativa y Medios Audiovisuales Vol. 3(7), págs. 12-21. 2006 ISSN 1667-8338 © LIE-FI-UBA. liema@fi.uba.ar 13 Enseñando programación con C++: una propuesta didáctica ____________________________________________________________________________________________________________________ las características importantes que desean enseñarse en un curso de programación para un primer año. una construcción del lenguaje, como, por ejemplo, cuando se manipula la aritmética de punteros. 9. Fácil transición a otros lenguajes: El aprendizaje del lenguaje debe facilitar una fácil transición a otros lenguajes ampliamente usados, como C/C++. Modelo puro de objetos: En el lenguaje coexisten construcciones que facilitan tanto la programación procedural como la orientada a objetos, pues en realidad C++ representa un paradigma híbrido entre ellos. 10. Facilidades de corrección: El lenguaje debe suministrar recursos para facilitar la corrección del programa, como prueba de aserciones, pre y postcondiciones, depuración de errores y manipulador de excepciones. 11. Ambiente de trabajo amigable: El lenguaje debe poseer un ambiente de desarrollo de fácil uso, que permita al estudiante concentrarse en las tareas de aprender los conceptos de programación en vez del propio ambiente. La presencia de un ambiente de programación adecuado puede facilitar el trabajo con un lenguaje técnicamente débil en comparación con un lenguaje más fuerte con poco apoyo externo. Estos ambientes deben contar, al menos con poderosos medios de edición y desarrollo de aplicaciones, de una implementación confiable, eficiente y bien documentada del lenguaje, así como recursos para una adecuada depuración de los errores en los programas. Características como la eficiencia, que es extremadamente importante para un lenguaje de programación en un entorno de producción de software, tienen poca significación en la enseñanza. Tampoco se considera la flexibilidad del lenguaje para el desarrollo de aplicaciones en tiempo real (por ejemplo, con operaciones de manipulación de bits). 3. Evaluación crítica del C++ El lenguaje C++ viola casi todos los requerimientos de la lista anterior. Es un lenguaje híbrido que no presenta los conceptos de una manera clara, posee un conjunto de construcciones altamente redundante y un sistema de tipos inseguro. Analizamos para el lenguaje cada una de las características enunciadas en el epígrafe 2: Claridad conceptual: Los propósitos principales en la creación del C++ fueron mantener una compatibilidad completa con C y considera la eficiencia en tiempo y espacio como su meta principal. De ahí que el lenguaje se vea influenciado por consideraciones de bajo nivel, que separan el concepto abstracto de su representación concreta. En ocasiones es necesario comprender ciertos detalles de implementación para usar correctamente Tratar de introducir la orientación a objetos usando lenguajes híbridos tiene el peligro de que los estudiantes con experiencias anteriores en programación en un lenguaje procedural no se sienten comprometidos con el cambio a un nuevo estilo. Seguridad: Una de las críticas más fuertes que se hacen al C++ es la falta de un sistema de tipos seguro. La asignación de memoria dinámica explícita, junto con la falta de colección de basura, incrementan los riesgos de errores, tales como referencias nulas o basura. Alto nivel: C++ incluye algunos constructores de bajo nivel, entre ellos las operaciones de manipulación de bits, la aritmética de punteros y la administración de almacenamiento dinámico explícito. Tiene un elemento importante a su favor para la programación de alto nivel, que es el soporte de genericidad, con un chequeo de los tipos parametrizados en tiempo de compilación. Modelo sencillo de representación y ejecución de objetos de datos: El modelo de objetos de datos de C++ es uno de los más complejos entre los lenguajes de programación que soportan orientación a objetos. Ello está motivado en el hecho de que en C++ aparecen variables dinámicas (usando punteros) y no dinámicas (automáticas), lo que también se refleja en la semántica de algunas construcciones importantes del lenguaje. Sintaxis legible: En C++ se favorece el uso de símbolos en vez de palabras, por ejemplo, empleo de la llave { en vez de begin, el uso de la misma palabra clave u operador para diferentes fines, como la sintaxis de una función abstracta virtual void f() = 0; que emplea el operador de asignación. Todo esto redunda en una falta de legibilidad de los programas escritos en el lenguaje. No redundancia: El hecho de mantener C++ la compatibilidad con C hace que arrastre ciertos aspectos redundantes e interacciones de conceptos que aquejan a este último, lo que incrementa la complejidad del lenguaje. ___________________________________________________________________________________________________________________ Revista de Informática Educativa y Medios Audiovisuales Vol. 3(7), págs. 12-21. 2006 ISSN 1667-8338 © LIE-FI-UBA. liema@fi.uba.ar 14 Enseñando programación con C++: una propuesta didáctica ____________________________________________________________________________________________________________________ Facilidad de transición a otros lenguajes: Consideramos que el aprendizaje del lenguaje C++ facilita la comprensión rápida de otros lenguajes de amplio uso, como el Java. De otro lado, aprender el lenguaje C++ puede reportar beneficios en el caso de realización de prácticas profesionales simultáneas por parte de los estudiantes en el sector industrial, que aún emplea con frecuencia esquemas basados en C/C++. Ayuda a la corrección: No hay en C++ soporte para pre ni postcondiciones, existiendo una forma de prueba con aserciones (a través del assert). Posee un sistema de manipulación de excepciones. Ambiente de trabajo amigable: Para C++ se han desarrollado muy buenos ambientes de trabajo, que incluyen herramientas para el desarrollo de aplicaciones, tales como Visual C++ y Borland C++ Builder. 4. Estrategia pedagógica en el uso de C++ para la orientación a objetos en una primera asignatura de programación • Aún cuando un lenguaje híbrido sea una selección de orden práctica, los estudiantes probablemente se sentirán algo confundidos en el intervalo de tiempo en que aprenden los conceptos asociados a objetos. Tomando en cuenta lo arriba planteado y además el hecho de que una asignatura de Programación tiene otras subsiguientes que complementan lo aprendido en ella, como lo puede hacer una asignatura de Estructura de Datos, brindamos algunas orientaciones metodológicas que pueden ser tenidas en cuenta al momento de impartir la asignatura de Programación, a partir de nuestras propias experiencias (Díaz, 2002) y de recomendaciones, como las encontradas en (Tonella, 2002) para definir un subconjunto de C++ que se acerque a las características de Java. En estas orientaciones sugerimos un orden en que puede ser impartida la asignatura. 1. En lenguajes híbridos para la orientación a objetos, como lo es el caso de C++, con la coexistencia para la programación estructurada, es común el uso de una estrategia pedagógica en la que el concepto de objeto no se enseñe como el primer concepto de la programación, sino que algunos otros conceptos, tales como expresiones e instrucciones del lenguaje hasta el concepto de tipo de dato abstracto sean trabajados antes de introducir el concepto de clases como desarrollo de los tipos de datos abstractos (TDA). Este esquema tiene como ventaja que da continuidad a una forma de aprendizaje que normalmente traen los estudiantes de la enseñanza media, basado en el desarrollo de algoritmos y la programación estructurada. Sin embargo, pueden señalársele algunas desventajas: • Uso simultáneo de tipos de datos básicos y de tipos de objetos y sus incompatibilidades. • Uso de punteros en muchos de los lenguajes candidatos para ser usados en la enseñanza, con necesidad de derreferenciación explícita y sin uso de colectores de basura. El concepto de tipo de dato abstracto se vuelve de difícil comprensión, en vez de inmediato y concreto. No abandonar a lo largo del curso un estilo de enseñanza de la programación basada en la resolución de problemas, o sea, del problema a la búsqueda de los algoritmos que le brinden solución y de ahí a la codificación del algoritmo en un lenguaje de alto nivel. El estudiante debe quedar convencido, desde que comienza la asignatura, de que su eficacia como programador no está precisamente en dominar la herramienta de programación, cosa evidentemente necesaria, sino en saber resolver problemas con la computadora para lo cual se requiere dominar una metodología de la programación cuyo eje central es el algoritmo. 2. Continuando la idea de la primera orientación, dedicar las primeras semanas de clase al desarrollo de algoritmos, usando para su expresión alguna forma de seudocódigo próxima al lenguaje natural. Cuando se introduzcan en las características de un lenguajes de programación y se enuncien sus principales estructuras de control (secuencia, alternativa y ciclos) y algunos de sus principales operadores (aritméticos, asignación, etc), podemos entonces, adicionalmente, comenzar a introducirlos en la expresión del algoritmo usando la forma de codificación propia del C++. ___________________________________________________________________________________________________________________ Revista de Informática Educativa y Medios Audiovisuales Vol. 3(7), págs. 12-21. 2006 ISSN 1667-8338 © LIE-FI-UBA. liema@fi.uba.ar 15 Enseñando programación con C++: una propuesta didáctica ____________________________________________________________________________________________________________________ 3. Se sugiere usar solamente las operaciones imprescindibles de estrada/salida, según el formato de C++, o sea, usando la clase <iostream>. Después se puede pasar a estudiar la estructura del programa en C++, donde se introducen las declaraciones de constantes, definiciones de variables, características de algunos tipos primitivos y se estudian con mayor grado de formalización sintáctica y semántica los operadores y las estructuras de control básicas del lenguaje. Ya en estos momentos deben dedicarse una cantidad de clases prácticas en laboratorios de computadoras, de tal forma que los estudiantes se familiaricen con el ambiente de trabajo del lenguaje y puedan correr algunos de los problemas, cuya expresión algorítmica dio en clases anteriores. Es importante además estudian los conceptos de alcance de identificadores y tiempo de vida de las variables, así como la definición de espacios de nombres. 4. El empleo de los operadores en estas primeras semanas debe ser lo más próximo al conocimiento matemático previo del estudiante, por ejemplo, no sería conveniente usar la asignación múltiple v = a = b, sino a = b y después v = a. Tampoco es recomendable usar formas de composición de operadores que sean oscuras, tales como x = (y=5) + (z=4), siendo mejor emplear y = 5; z = 4; x = y + z. Aquí ya hay un espacio para las operaciones de incremento y decremento, los operadores de asignación múltiple y compuestos. Como inmediatamente vamos a proponer el estudio del concepto de clases, entonces resulta aconsejable enseñar parámetros con valores por defecto y sobrecarga de funciones. Sin embargo, no resulta recomendable enseñar en este momento las operaciones de manipulación de bits que ofrece en lenguaje, ni los moldes (casting). Tampoco es recomendable usar valores numéricos como expresiones lógicas para las condiciones que controlan las instrucciones de alternativa (if…) o de ciclos. Es sabido que una expresión aritmética puede ser considerada en el lenguaje como un caso particular de una expresión lógica, que se interpreta como verdadera si es diferente de cero, o falsa si es cero, interpretación que debe evitarse en estos primeros momentos que se aprenden las instrucciones arriba citadas. En vez de escribir if (a) ….. escribir if (a != 0)….. El concepto de función se introduce como es usual, también desde un punto de vista algorítmico, en que se definan los parámetros de entrada, de salida, de entrada/salida y formas que eventualmente usará la función para retornar valores que calcula. El uso del operador & para el traspaso de parámetro por referencia debe quedar en este momento a un nivel de definición, para luego ser explicado el concepto en el capítulo dedicado a los punteros y las referencias. Se puede comenzar con el estudio de funciones recursivas, el cual se puede retomar más adelante con un mejor aparato conceptual, por ejemplo, una vez se hayan estudiado estructuras de datos lineales. En este momento es mejor evitar impartirles funciones genéricas (template de funciones) y funciones con cantidad de parámetros indeterminada. Debe evitarse también la declaración global de constantes y variables. 5. Llega el momento de introducir el concepto de clase. El punto de partida es la definición de tipos de datos abstractos que nos ayuden en la representación de un problema, por ejemplo, la definición de alguna clase para datos numéricos (números fraccionarios, números complejos) donde se implementen algunas operaciones, que para la clase de los números fraccionarios puede ser sumar, multiplicar, simplificar, leer, mostrar, etc. A partir de ahí definimos sus rasgos distintivos (en atributos y operaciones necesarias para el tipo) y presentamos al concepto de clase como una herramienta eficaz para codificar en el ___________________________________________________________________________________________________________________ Revista de Informática Educativa y Medios Audiovisuales Vol. 3(7), págs. 12-21. 2006 ISSN 1667-8338 © LIE-FI-UBA. liema@fi.uba.ar 16 Enseñando programación con C++: una propuesta didáctica ____________________________________________________________________________________________________________________ punteros y cadenas de caracteres, sin embargo en la quinta edición ya se introduce un primer momento del estudio de las clases una vez dados elementos básicos del lenguaje y antes de las estructuras de control. lenguaje de programación la definición de tal tipo y su empleo en el problema. Deben ser estudiadas las formas de visibilidad que implementa C++ para las componentes de clase, las partes de la clase (interfase e implementación), constructores simples y destructor. 6. Al tratar con this, debe ser a un nivel muy simple, que nos evite mezclarnos con los punteros y lograr que en este momento sea aceptado por definición que *this es el objeto actual que envía el mensaje en proceso. Debe explicarse el concepto de estado de un objeto y a partir de aquí las funciones de acceso a un objeto (las que retornan el valor del estado o parte del estado del objeto), su importancia y porqué comúnmente se les declara como funciones const. Pueden ser empleados ejemplos de objetos compuestos. class Valor { private: float elem[100]; int cant; .............. }; Deben estudiarse las cadenas de caracteres, tanto al estilo de C como de C++ (objetos string). Cuando se emplea un lenguaje híbrido en la orientación a objetos para enseñar, como lo es C++, hay criterios divididos en cuanto a: • introducir el concepto de clase en este punto del programa de la asignatura, en que otras construcciones de datos propias del paradigma procedural aún no se han estudiado, • hacer un estudio del lenguaje primeramente desde una óptica completamente procedural y luego introducir los conceptos de clase y otros propios de la orientación a objetos. 7. Estudiamos una primera parte del concepto de herencia. Aquí se introducen el concepto básico de herencia, los conceptos de clases y superclases, las formas en que se heredan atributos y métodos, la visibilidad protected, todo a través de un ejemplo introductorio. Se estudian las características de los constructores y destructores de las clases derivadas. Recordemos que hasta este punto del curso las operaciones que estos métodos realizan son triviales, dado que aún no hemos trabajado con variables dinámicas. Se demuestra, en una práctica de laboratorio a partir del ejemplo de entrada, la importancia de definir métodos virtuales aquellos que se redefinen en diferentes niveles de la jerarquía de clases, denotando además que esta definición constituye una forma de polimorfismo. De nuestra propia experiencia, consistente en emplear el primer enfoque los últimos cursos en tanto que el segundo enfoque en cursos anteriores, defendemos el primero, que ha mostrado una mejor apropiación de los conceptos de la programación orientada a objetos. Ese propio enfoque que escogemos se refleja en la propia evolución de los textos dedicados a la enseñanza del lenguaje C++. Si comparamos la cuarta y la quinta edición del texto de Deitel “C++ How to Program” (Deitel, 2002), (Deitel, 2005) notamos que hasta la cuarta edición el primer estudio del concepto de clases se dejaba hasta después del estudio de los conceptos de estructuras de control, funciones, arreglos y A continuación se introducen tipos enumerativos y fundamentalmente tipos de arreglos. Una vez explicados los principales conceptos sobre arreglos, se introducen las formas en que los arreglos se operan o transforman (lecturas, escrituras, cálculos, búsquedas, etc) construyendo clases en que sus atributos sean la cantidad real de elementos que tiene el arreglo y la definición de los elementos del arreglo, usando el constructor por defecto de un arreglo, por ejemplo: 8. Llegamos al momento de introducir los punteros. Sería deseable no tener que enseñar a los estudiantes el concepto de puntero en un primer curso de programación. Esto sería posible si el lenguaje manipulara sus objetos de datos de forma dinámica por defecto, lo cual no es el caso de C++. Siendo entonces imprescindible el uso de los punteros para ___________________________________________________________________________________________________________________ Revista de Informática Educativa y Medios Audiovisuales Vol. 3(7), págs. 12-21. 2006 ISSN 1667-8338 © LIE-FI-UBA. liema@fi.uba.ar 17 Enseñando programación con C++: una propuesta didáctica ____________________________________________________________________________________________________________________ definir variables dinámicas y todos los conceptos subsecuentes relacionados, se recomiendan las siguientes acciones en un primer momento: • Enseñar el concepto de puntero, fundamentalmente como vía de definir variables dinámicas, con new y dispose. • No enseñar aritmética de punteros. • Definir la relación entre punteros y arreglos, pero insistiendo en el uso del indizado [] como base del acceso a las componentes del arreglo. • Disciplinar a los estudiantes en el chequeo del rango de los índices de sus arreglos. • Dar herramientas didácticas de programación para evitar la aparición de referencias nulas o basura. • Hacer un estudio más detallado de los tipos de cadenas de caracteres definidas a través de punteros y de la biblioteca <string> de C++. • Crear objetos dinámicos. • Explicar qué es el puntero this. • Usando la definición de una clase para manipular arreglos dinámicos, que incluyan entre otras operaciones la asignación de un arreglo para otro, profundizar en la implementación de constructores y destructores para objetos con campos dinámicos. Se debe enseñar además el constructor de copia y su necesidad. • Enseñar los conceptos de listas lineales (incluyendo colas y pilas) y mostrar implementaciones usando arreglos definidos dinámicamente o estructuras enlazadas, en las que emplearán punteros para en enlace entre cada elemento y su contiguo. Retomamos el concepto de herencia, en este caso para enseñar conceptos relativos a la definición de tipos-subtipos en una jerarquía y como esto permite definir objetos polimórficos. Además se enseña cómo se establece la compatibilidad entre objetos, punteros a objetos y parámetros actuales y formales en una jerarquía de clases. Considerando el sistema de conocimientos y habilidades acumulados hasta este punto por el estudiante en el tema de herencia, es aconsejable desarrollar ejercicios en que puedan construir nuevas clases ampliando clases que han sido desarrolladas en los tópicos anteriores, bien buscando una nueva especialización o simplemente para ampliar la funcionalidad de la clase. Se sugiere evitar en estos momentos el uso de la herencia selectiva. Por último se estudian las clases abstractas y las funciones virtuales puras, a través de un ejemplo de una jerarquía con una clase base raíz abstracta, por ejemplo, las figuras en el plano, como en el ejemplo: class TFigura { protected: float x, y; public: TFigura(float cx=0, float cy=0) { x = cx; y = cy; } virtual void Mostrar() = 0; virtual float Area() = 0; // otras funciones................... }; // Clases derivadas class TCirculo: public TFigura { ......... public: // Funciones concretas Area y Mostrar para // TCirculo ........ }; ....... class TCuadrado: public TFigura { ......... public: // Funciones concretas Area y Mostrar para // TCuadrado ........ }; ....... Mostrar como usar la clase con un programa ejemplo, tal como: void main() { TFigura *p[2]; // da espacio e inicializa un círculo ___________________________________________________________________________________________________________________ Revista de Informática Educativa y Medios Audiovisuales Vol. 3(7), págs. 12-21. 2006 ISSN 1667-8338 © LIE-FI-UBA. liema@fi.uba.ar 18 Enseñando programación con C++: una propuesta didáctica ____________________________________________________________________________________________________________________ typedef double tfun(double); double SumaCuadrado(tfun &f,int m,int n) { double sum=0; int k; for (k=m; k<=n; k++) sum += f(k) * f(k); return sum; } p[0] = new TCirculo(0,0,5); // da espacio e inicializa un cuadrado p[1] = new TCuadrado(1,1,4); for (int i=0; i<2; i++) { // Se muestran los datos de la figura adecuada p[i]->Mostrar(); // Ahora se imprime el área de la figura cout << "Su área es: " << p[i]->Area() <<endl; } system("PAUSE"); double g(double x) { return 1.0 / x; } void main() { double a, b; a = SumaCuadrado(g, 1, 5); b = SumaCuadrado(sin, 0, 4); cout <<"a= " <<a <<" b= " <<b <<endl; } } donde se muestra un arreglo de objetos polimórficos con diferentes figuras de la jerarquía. Por último se les muestra un ejemplo en que puedan aprender cómo usar la herencia privada para una clase derivada pueda obviar métodos de su clase base. Hasta aquí se han relacionado conceptos básicos de programación que se pueden encarar en la primera asignatura destinada a la enseñanza de la programación. En dependencia del tiempo disponible para su impartición y de la profundidad conceptual a la que se quiera llegar en la asignatura, proponemos seleccionar algunas de las siguientes temáticas en el orden que se especifica: a) Estructuras y uniones, que ahora constituyen características redundantes en el lenguaje, pero si los estudiantes tienen a la programación como objeto de estudio deben conocer estos tipos. c) Formas de polimorfismo operacional • Se enseña la forma propia que posee C++ y la heredada de C. En este momento se formaliza el estudio de punteros void y su uso en la compatibilización entre tipos punteros mediante la coerción. • b) Tópicos adicionales sobre punteros: • aritmética de punteros, • punteros de tipo const y punteros a objetos dinámicos constantes. • Punteros y referencias para las funciones: aquí lo deseado es poder construir listas de funciones y también facilitar el traspaso de funciones como parámetros formales de funciones. En este último caso sugerimos usar el traspaso por referencia al estilo de C++, como en el ejemplo siguiente para obtener la suma de los cuadrados de una función f evaluada para valores enteros entre m y n dados. Coerción (casting). #include <iostream.h> #include <math.h> Sobrecarga de operadores. Como sabemos, la sobrecarga de operadores es un recurso muy cómodo, que hace permite a los programadores construir tipos de datos de una forma similar a los tipos simples predefinidos (int, long, etc.), dando una interpretación para el tipo de los operadores predefinidos del lenguaje. Esto conlleva el estudio de las funciones operadoras (operator) y el concepto de función amiga de una clase (friend), uso y peligros. Se debe desarrollar alguna clase previamente estudiada, como la clase de manipulación de números fraccionarios o de números complejos u otra afín, usando las funciones operadoras, mostrando el uso o necesidad de usar funciones amigas dentro de clases que implementan funciones operadoras. Por otra parte debe reconocerse las diferencias entre implementar funciones miembros de una clase y funciones no miembros amigas de la clase, desarrollando ___________________________________________________________________________________________________________________ Revista de Informática Educativa y Medios Audiovisuales Vol. 3(7), págs. 12-21. 2006 ISSN 1667-8338 © LIE-FI-UBA. liema@fi.uba.ar 19 Enseñando programación con C++: una propuesta didáctica ____________________________________________________________________________________________________________________ algunos ejercicios en que se pida implementar la misma operación usando los dos esquemas. Algunas sobrecargas interesantes que deben mostrarse, además de los operadores aritméticos, lógicos clásicos, deben ser la sobrecarga del operador de asignación =, en una clase como la de arreglos dinámicos previamente estudiada. Así mismo se recomienda enseñar, usando esta propia clase, la sobrecarga del operador de indización []. Finalmente pueden ser mostradas otras sobrecargas de operadores. • Funciones y clases genéricas. Se define el concepto de genericidad y su importancia práctica y se enseña cómo definir templates de funciones y de clases (puede usarse como ejemplo el bien conocido de la pila genérica). Un estudio más profundo debe ser dejado a una asignatura subsiguiente de Estructuras de Datos, donde pueden explotar además bibliotecas del tipo STL (Standard Template Library). d) El sistema de manipulación de excepciones del lenguaje. Si se decide estudiar este tema, debería ser introducido después de transcurrido el 75% del fondo de tiempo de la asignatura y no precisamente en sus finales. 4. Conclusiones Programar es un desafío. La programación requiere el uso de habilidades de pensamiento abstracto y muchos estudiantes no han recibido asignaturas que requieran o les hayan ayudado a desarrollar estas habilidades en niveles precedentes de enseñanza, considerando que la asignatura de Programación debe ser una de las primeras que enfrente un estudiante de carreras de perfil afín a la computación. Muchos estudiantes entran a la clase de programación con escasas habilidades conceptuales para programar. En el presente trabajo se ha hecho un análisis detallado de algunas deficiencias que presenta el lenguaje C++ al ser considerado como el primer lenguaje en que los estudiantes que comienzan a familiarizarse con la programación orientada a objetos. A partir de tales deficiencias, pero también considerando algunas de sus bondades, como la riqueza de elementos conceptuales, presencia de excelentes ambientes de trabajo y demandas del sector industrial se brindan un conjunto de sugerencias que parten de las experiencias didácticas acumuladas de siete generaciones de estudiantes que han recibido la asignatura de Programación en el primer año de la carrera, usando como lenguaje de programación al C++, además de experiencias del uso del lenguaje Java en otros cursos subsiguientes de programación. Estas sugerencias ofrecen una propuesta de cómo establecer secuencia de contenidos de la asignatura y de posibles ejercicios a desarrollar en algunas de las temáticas. Todo lo anterior nos reafirma una idea que es de todos conocida, pero no tan unánimemente aplicada: el profesor debe enseñar a los estudiantes los principios de la programación (en este caso orientada a objetos), lo cual podríamos hacerlo en el lenguaje que consideremos apropiado. Resulta primaria una metodología de programación y subsidiaria de ésta la codificación en un lenguaje de programación. Otra idea extremadamente importante es que las asignaturas de corte matemático que son enseñadas en el propio año académico que la programación, como lógica matemática, álgebra y análisis matemático, pueden influir muy positivamente en el aprendizaje por parte del estudiante de una buena metodología de programación, cuestión que algunas veces también es obviada. De otro lado los estudiantes no deben abandonar la universidad sin ser capaces de usar un lenguaje de amplio uso en el mundo real actual, como en la actualidad lo continúa siendo C++. En este sentido, el lenguaje usado en la primera asignatura de programación debe ser relevante para el dominio de los lenguajes de más amplio uso científico e industrial del momento. Bibliografía Arnold, K., Gosting, J. (1996) The Java Programming Language, Addison-Wesley. Ben-Ari, M. (1998). Constructivism in Computer Science Education, SIGSCE 98 Atlanta, GA., p.257261, 1998. Deitel, H., Deitel, P. (2002). C++, How to Program, 4th edition. Deitel, H., Deitel, P. (2005). C++, How to Program, 5th edition. Díaz, J. (2002): Introducción a la programación usando C++. Universidad de Oriente, Cuba. ___________________________________________________________________________________________________________________ Revista de Informática Educativa y Medios Audiovisuales Vol. 3(7), págs. 12-21. 2006 ISSN 1667-8338 © LIE-FI-UBA. liema@fi.uba.ar 20 Enseñando programación con C++: una propuesta didáctica ____________________________________________________________________________________________________________________ Eckel, B. (2003): Thinking in Java, Third Edition. Prentice Hall. Kölling, M. (1999). The problem of teaching objectoriented programming. Part I: Environments. Journal of Object-Oriented Programming, 11(8): p.8-15. Stroustrup, B. (1997): The C++ programming language, Third Edition. Addison-Wesley. Tonella, P., Potrich, A. (2002). Cjj: a subset of C++ compliant with Java. Science of Computer Programming, 42, p.229-271. ___________________________________________________________________________________________________________________ Revista de Informática Educativa y Medios Audiovisuales Vol. 3(7), págs. 12-21. 2006 ISSN 1667-8338 © LIE-FI-UBA. liema@fi.uba.ar 21