Curso de C++ (2014)

33 %
67 %
Information about Curso de C++ (2014)
Technology

Published on February 28, 2014

Author: inniyah

Source: slideshare.net

Una visión global del lenguaje C++ (pensando en sistemas embebidos) Miriam Ruiz <miriam@debian.org> Marzo, 2014

Introducción al lenguaje C++

¿Qué es C++? ● Es un lenguaje de programación... – De tipado estático (static typing): Las comprobaciones de tipos se realizan en tiempo de compilación, no de ejecución. – De tipado fuerte (strong typing): Las variables tienen asociadas un tipo de datos, que define lo que se puede hacer. – De forma libre (free form): La posición de los caracteres en la página es irrelevante. – Multiparadigma: Soporta más de un paradigma de programación, y la combinación de los mismos. – Compilado: Es necesario convertirlo al código de la máquina para poder ejecutarlo. – De propósito general: Puede ser usado para realizar tareas que no pertenecen a un dominio concreto. – De nivel intermedio: Incorporando características tanto de bajo nivel (cercano a la máquina) como de alto nivel (cercano al ser humano). – Independiente de la plataforma: “Write once, compile anywhere” (WOCA) 3 / 136

Principios de diseño de C++ ● ● Lenguaje multipropósito tan eficiente y portable como C. Soportar de forma directa y comprensiva diferentes estilos de programación: – – – – ● ● ● ● ● Procedimental o funcional Abstracción de los datos Orientada a objetos Programación genérica o metaprogramación Permitir que la persona que está programando sea la que tome las decisiones, aunque ello pueda significar que las decisiones sean equivocadas. Ser tan compatible con el lenguaje C como sea posible, para permitir una transición suave desde este lenguaje. Evitar las características que no sean de propósito general, o que sean específicas de una plataforma. No incurrir en un sobrecoste o una penalización por características que no se quieran usar. No necesitar un sofisticado entorno de desarrollo. 4 / 136

Características del lenguaje ● ● Uso de operadores, y sobrecarga de los operadores. 4 tipos de gestión de la memoria: – – – – ● ● Tipos básicos: booleanos, caracteres, números enteros, números en coma flotante. Modificadores: unsigned / signed, short / long. Programación orientada a objetos basada en clases: – – – – ● Memoria estática, asignada en tiempo de compilación Memoria asignada automáticamente, en la pila de ejecución Memoria de asignación dinámica (new/delete o malloc/free) Uso de garbage collectors a través de librerías externas Encapsulación: public, protected, private, friend. Herencia jerárquica, que puede ser simple y múltiple. Herencia virtual. Polimorfismo: Un único interfaz para diferentes implementaciones. ● Estático: Sobrecarga de operadores. Templates. ● Dinámico: Herencia. Funciones virtuales. Gestión de recursos: Constructores y destructores. Plantillas o templates: Permiten que una clase o función trabaje con tipos de datos abstractos, especificándose más adelante cuales son los que se quieren usar 5 / 136

La evolución de C++ ● 1980: C with class – ● 1983-1986: CFront 1.1 – – – ● – Excepciones 1996: C++ 98/03 – – ● Plantillas o templates 1992: HP C++ – ● Herencia múltiple Clases abstractas 1991: CFront 3.0 – ● Funciones virtuales Sobrecarga de operadores Referencias 1989: CFront 2.0 – ● Clases. Herencia STL Espacios de nombres 2011: C++11 – – – Expresiones lambda Tipos automáticos Hilos de ejecución (threading) 6 / 136

Etapas en la compilación de un programa ● ● ● ● ● ● Código fuente (source code) – Archivos .h, .c, .cpp, .hpp, .cc Preprocesado (preprocessing) – Archivos con las cabeceras incluidas y las macros expandidas: .i, .ii Compilación (compilation) – Archivos en código ensamblador: .s Ensamblado (assembling) – Archivos en código máquina: .o Archivado (library) – Bibliotecas estáticas (.a) o dinámicas (.so, .dll) Enlazado (linking) – Archivos ejecutables (.exe, .elf) 7 / 136

Características de C++

El modelo de memoria de C++ ● ● ● La memoria consiste en una serie de objetos referenciados por punteros: Hay una serie de tipos básicos predefinidos, y unos mecanismos para construir estructuras más complejas a partir de ellos El almacenamiento de las estructuras se hace de tal forma que la composición y la herencia son fáciles de implementar 9 / 136

Tipos básicos de datos en C++ ● ● ● Simples – Booleanos ● bool – Enteros (o decimales en coma fija) ● char, short, int, long – Enumeraciones ● enum – Decimales en coma flotante ● float, double, long double Estructurados – Estructuras de datos: arrays, strings (zero-terminated), union – Definición de objetos: class, struct Direcciones – Punteros (*) – Referencias (&) 10 / 136

Modificadores y calificadores de tipos de datos ● ● Modificadores de los tipos de datos: – signed, unsigned, long, short Calificadores de los tipos de datos: – const: Los objetos de este tipo no pueden ser modificados ● const int, const char * – volatile: Indica al compilador que los datos referenciados. pueden ser alterados de formas no explícitamente especificadas por el programa. ● volatile REGISTER * io_ = 0x1234; – restrict: Indica al compilador que ese puntero es el único mecanismo de acceso a los datos a los que apunta. ● size_t *restrict ptrA 11 / 136

Punteros #include <cstdlib> #include <cstdio> int main() { char ch = 'c'; char *chptr = &ch; int i = 20; int *intptr = &i; float f = 1.20000; float * fptr = &f; const char * ptr = "I am a string"; printf("n [%c], [%d], [%f], [%c], [%s]n", *chptr, *intptr, *fptr, *ptr, ptr); return EXIT_SUCCESS; } 12 / 136

Punteros #include <cstdlib> #include <cstdio> int main() { char ch = 'c'; char *chptr = &ch; int i = 20; int *intptr = &i; [c], [20], [1.200000], [I], [I am a string] float f = 1.20000; float * fptr = &f; const char * ptr = "I am a string"; printf("n [%c], [%d], [%f], [%c], [%s]n", *chptr, *intptr, *fptr, *ptr, ptr); return EXIT_SUCCESS; } 13 / 136

Referencias a variables #include <cstdlib> #include <cstdio> void swap(int & i, int & j) { int tmp = i; i = j; j = tmp; } int main() { int x = 0; int y = 1; swap(x,y); printf("x=%d, y=%dn", x, y); int & a = x; int b = x; a = 5; b = 7; printf("x=%d, y=%d, a=%d, b=%dn", x, y, a, b); return EXIT_SUCCESS; } 14 / 136

Referencias a variables #include <cstdlib> #include <cstdio> void swap(int & i, int & j) { int tmp = i; i = j; j = tmp; } int main() { int x = 0; int y = 1; swap(x,y); printf("x=%d, y=%dn", x, y); x=1, y=0 int & a = x; x=5, y=0, a=5, int b = x; a = 5; b = 7; printf("x=%d, y=%d, a=%d, b=%dn", x, y, a, b); return EXIT_SUCCESS; b=7 } 15 / 136

Punteros vs. Referencias ● ● Puntero: – Identifican un contenido ubicado en un lugar de la memoria. – Se define añadiendo el carácter '*' a un tipo de dato. ● char * pChar; int * pInt; Shape * pShape; – Se usa por convenio el valor NULL (igual a 0L) para no señalar ninguna dirección significativa. Referencia: – No es una variable – Es un alias (o nombre alternativo) de un objeto o dato que ya existe. – Se define añadiendo el carácter '&' a un tipo de dato. ● char & rChar; int & rInt; Shape & rShape; – No siempre necesitan ocupar memoria. – No pueden ser NULL. – No pueden ser modificadas. – A menudo son implementados con punteros por parte de los compiladores, pero no son lo mismo. 16 / 136

Sobrecarga de funciones ● ● ● ● ● Permite asignar el mismo nombre a funciones distintas, siempre que tengan diferentes parámetros. Para el compilador estas funciones no tienen nada en común a excepción del identificador. Debe haber diferencia en los parámetros. Si se diferencian únicamente en el valor de retorno, no es suficiente. Orden de preferencia (reglas de congruencia estándar de argumentos: – – – – – Concordancia exacta en número y tipo, incluyendo conversiones triviales como de nombre de matriz a puntero, de nombre de función a puntero a función, o de tipo de datos a tipo de datos constante. Concordancia después de realizar promociones de los tipos asimilables a enteros, o de los tipos float a double. Concordancia después de realizar conversiones estándares, como de int a double, de double a long double, de un puntero de una clase derivada a una superclase, o de un puntero a un puntero a void. Concordancia después de realizar conversiones definidas por el usuario o usuaria. Concordancia usando la elipsis (...) en funciones con número variable de parámetros. 17 / 136

Expresiones y operadores ● ● ● ● ● Variable: es una entidad que permite a un programa almacenar información, y cuyo valor puede cambiar a lo largo de su ejecución. Operando: cada una de las constantes, variables o expresiones que intervienen en una expresión. Operador: cada uno de los símbolos que indican las operaciones a realizar sobre los operandos, así como los operandos a los que afecta. Expresión: es una combinación gramaticalmente válida de operadores y operandos, que dan como resultado un valor. Sobrecarga de operadores: es uno de los mecanismos que nos permite ampliar las capacidades de los lenguajes de programación orientados a objetos. En C++, la declaración y definición de una sobrecarga de operador es muy similar a la declaración y definición de una función cualquiera. 18 / 136

Operadores en C++ ● ● ● ● ● ● ● ● ● Aritméticos: suma, resta, multiplicación, división y módulo Asignación: Operadores de asignación simple ('=') y compuestos Manejo de bits: Manejo de bits (bitwise) entre enteros: complemento, desplazamientos, AND, XOR y OR Lógicos: Producen resultados booleanos: AND, OR y NOT. Los operandos no son siempre evaluados. De Puntero: Operadores de indirección (*) y de referencia (&) Relacionales: ==, !=, <, >, <=, >= Manejo de memoria: new, delete, new[], delete[] Modelado de tipos (cast): Convierte datos de un tipo a otro Otros: – – – – – – – '::' - Acceso a ámbito (también llamado de resolución) '.*' - Dereferencia punteros a miembros de clase '->*' - Dereferencia punteros a punteros a miembros de clases '?' - Operador ternario condicional ',' - Operador en la expresiones con coma 'typeid' - Obtiene identificación de tipos y expresiones en tiempo de ejecución 'sizeof' - Obtiene el tamaño de memoria utilizado por el operando 19 / 136

Operadores en C++ ● ● ● Operadores unitarios: solo requieren un operando y operan generalmente de izquierda a derecha (el operador a la izquierda, el operando a la derecha). C++ dispone de los siguientes operadores unitarios: – '!' - Negación lógica – '*' - Indirección – '&' - Referencia – '~' - Complemento de bits – '++' - Incremento (prefijo o postfijo: ++a, a++) – '--' - Decremento (prefijo o postfijo: --a, a--) – '-' - Menos unitario – '+' - Más unitario Operadores binarios: Operadores de asignación simple ('=') y compuestos Operadores ternarios: El único operador ternario es el operador relacional ? : 20 / 136

Espacios de nombres (namespace) ● ● ● ● ● ● ● Cuando un programa alcanza un gran tamaño, la probabilidad de colisiones en los nombres o identificadores aumenta. El problema puede llegar a ser aún mayor cuando se usan librerías externas. Un espacio de nombres es una zona donde se pueden declarar y definir identificadores de cualquier tipo (clases, estructuras, funciones, etc), a la que se asigna un nombre o identificador propio. Esto permite diferenciar estos elementos de los que puedan tener identificadores iguales en otros espacios. El identificador del espacio, usado como prefijo, permite acceder a los nombres o identificadores declarados en su interior a través del operador de especificador de ámbito (“::”). También se puede indicar al programa que busque por defecto en los espacios que se quiera (“using namespace”). Incluso sin utilizar explícitamente los subespacios, dentro de un mismo ámbito, C++ distingue automáticamente varios espacios de nombres distintos. ● El espacio de nombres sencillo, que contiene las variables, funciones, definiciones de tipo (typedef) y enumeradores (enum). ● El espacio de nombres de miembros de clases ● El espacio de nombres de miembros de estructuras ● El espacio de nombres de miembros de uniones ● El espacio de nombres de etiquetas 21 / 136

Espacios de nombres (namespace) namespace EspacioA long double LD; float f(float y) namespace Sub1 { namespace Sub2 { } namespace EspacioB long double LD; float f(float z) namespace Sub1 { } { { return y; } long double LD; } long double LD; } { { return z; } long double LD; } EspacioA::LD = 1.1; EspacioA::f(1.1); EspacioB::LD = 1.0; EspacioB::f(1.1); EspacioB::X = 1 EspacioA::Sub1::LD = 3; EspacioA::Sub2::LD = 4; EspacioB::Sub1::LD = 5; // // // // // // // // Aceso a variable LD del espacio A Aceso a función f de A Aceso a variable LD del espacio B Aceso a función f de B Error: Elemento X no definido en B Aceso a variable LD del subespacio 1 de A Aceso a variable LD del subespacio 2 de A Aceso a variable LD del subespacio 1 de A ::EspacioA::LD = 1.1; ::EspacioB::LD = 1.0; // Acceso comenzando a buscar desde la raiz // Acceso comenzando a buscar desde la raiz 22 / 136

Espacios de nombres automáticos int x = 1, y = 2, z = 3; principal struct E { int x; int y; int z; } e1 = {1, 2, 3}; class C { int x; int y; int z; } c1 = {1, 2, 3}; union U { int x; char y; float z; } u1 = { x }; func(1, 2, 3); // Espacio // Estructuras // Clases // Uniones ... int func(int x, int y, int z) { if (x == 0) goto x; return (x + y + z); x: return (1 + y + z); // Etiquetas } 23 / 136

Decoración de Nombres (Name Mangling) ● ● ● ● ● El mecanismo tradicional de asignación de nombres a los elementos de C, ya no es válido para C++ (espacios de nombres, sobrecarga de funciones). El compilador cambiará el nombre de la función, añadiendo un identificador del espacio de nombres en el que está declarada, y de los parámetros aceptados por la función. Cada compilador de C++ realiza la decoración de los nombres de una forma diferente, incompatible con los demás. La posible estandarización de la decoración de los nombres, de todas formas, sería insuficiente para garantizar la compatibilidad entre diferentes compiladores (es necesario que haya compatibilidad también en la ABI: gestión de excepciones, distribución de la tabla virtual, etc.) Cuando se quiere buscar la compatibilidad con otros componentes, se usa la expresión extern “C” { } Ejemplos de Name Mangling para diferentes compiladores: http://en.wikipedia.org/wiki/Name_mangling#How_different_compilers_mangle_the_same_functions 24 / 136

Programación Orientada a Objetos

Complejidad en la programación estructurada 26 / 136

Pensar en objetos ● ● ● ● ● ● ● La programación orientada a objetos es un paradigma diferente de la programación estructurada, y no una simple ampliación del mismo. Requiere una forma diferente de pensar. Permite trabajar con sistemas más complejos de una forma más sencilla. En la programación estructurada, descomponemos el problema en acciones, en verbos. En la programación orientada a objetos, lo descomponemos en objetos, en nombres. Al analizar el problema, lo primero que tenemos que ver son las entidades que lo forman. Estas entidades poseen un conjunto de propiedades o atributos, y un conjunto de métodos mediante los cuales se puede interaccionar con ellas. Las entidades se interrelacionan entre ellas mediante mensajes, respondiendo a ellos mediante la ejecución de ciertas acciones. 27 / 136

Pensar en objetos 28 / 136

Programación orientada a objetos ● ● ● ● ● Es un paradigma de programación que usa los objetos como elemento base. Los objetos están constituidos por propiedades (o variables) y por métodos (o funciones). Las propiedades de un objeto almacenan su estado en un instante determinado. Los métodos de un objeto permiten interaccionar con él para obtener información, y cambiar su estado. La POO está basada en varias técnicas, incluyendo: – Herencia: Permite reutilizar el código mediante la especialización de unos objetos a partir de otros. – Cohesión: Pretende que cada objeto del sistema se refiera a un único proceso o entidad. – Abstracción: Consiste en aislar un componente de su entorno y del resto de elementos, haciéndo énfasis en qué hace, en lugar de cómo lo hace. – Polimorfismo: Se refiere a la posibilidad de interactuar con objetos heterogéneos de una forma homogénea, a través del mismo interfaz. – Acoplamiento: Cuanto menor acoplamiento entre unos objetos y otros, más sencillo será de desarrollar y mantener. – Encapsulamiento: Se refiere al ocultamiento del estado interno y a la protección del mismo respecto a modificaciones externas. 29 / 136

Un ejemplo de sistema organizado en objetos 30 / 136

Un ejemplo de sistema organizado en objetos 31 / 136

Un ejemplo de sistema organizado en objetos 32 / 136

Clases y Objetos en C++

Clases vs. Objetos ● ● Clase: – Es la definición abstracta de un tipo de objeto. – Sólo tiene sentido en tiempo de programación. – Definen el aspecto que tendrán los objetos que se creen durante la ejecución del programa. – Son una especie de molde o plantilla. – Definen qué atributos tendrán los objetos, y las cosas que se pueden hacer con él. Objeto o instancia: – Suponen la concreción del concepto de clase en un elemento concreto con características determinadas. – Existen durante la ejecución del programa, e interaccionan entre ellos. Nacen, viven, se comunican y mueren. – El valor concreto de cada atributo es único e independiente para cada objeto.. – Los conceptos de clase y objetos son análogos a los de tipo de datos y variable: definida una clase, podemos crear objetos de esa clase. 34 / 136

Definición de una clase en C++ class Rectangle { public: Rectangle(int h, int w); int getHeight(); int getWidth(); private: int height; int width; }; Rectangle::Rectangle(int h, int w) : height(h), width(w) { } int Rectangle::getHeight() { return height; } int Rectangle::getWidth() { return width; } 35 / 136

¿Cómo se almacena una clase en memoria? class Shape { public: Shape(const char* n); inline const char* getName() const { return name; } inline int getLayer() const { return layer; } inline char getColor() const { return color; } protected: const char* name; int layer; char color; }; name layer color alignment 36 / 136

Definición de una clase en C++ Implementación en C++ class Shape { public: Shape(); ~Shape(); void draw(); double x, y; Implementación equivalente en C Typedef struct ShapeT { double x, y; } Shape; void Shape_new(Shape * this); void Shape_delete(Shape * this); void Shape_draw(Shape * this); }; 37 / 136 37 / 70

Constructores ● ● ● ● ● ● ● ● Son funciones miembro especiales que sirven para inicializar los objetos Tienen el mismo nombre que la clase a la que pertenecen. No pueden devolver ningún valor. No pueden ser heredados. Cuando no especifiquemos un constructor para una clase, el compilador crea uno por defecto sin argumentos. Pueden definirse diferentes constructores para cada clase, siempre que tengan diferentes parámetros. Un constructor copia crea un objeto a partir de otro objeto existente. Si no se especifica ninguno, el compilador también crea uno por defecto. Se pueden invocar de forma directa de forma excepcional para inicializar una zona de memoria ya reservada, con un objeto de un tipo dado. 38 / 136

Destructores ● ● ● ● ● ● ● ● ● Son funciones miembro especiales que sirven para eliminar un objeto de una determinada clase. Tienen el mismo nombre que la clase a la que pertenecen, pero con el símbolo tilde ('~') delante. No retornan ningún valor. No tienen parámetros. No pueden ser heredados. Deben ser públicos. No pueden ser sobrecargados. Si la clase tiene algún método virtual, es conveniente declarar el destructor también como virtual. Son llamados automáticamente cuando un objeto deja de existir. 39 / 136

Gestión de recursos ● ● Recursos: Son aquellos elementos que, al ser limitados o tener que ser compartidos por diferentes componentes del sistema, han de ser adquiridos y liberados en determinados momentos: – Memoria – Descriptores de archivos – Hilos, procesos, elementos de bloqueo – Eventos, mensajes, modelos – Etc. Resource Acquisition Is Initialization (RAII): Los recursos son adquiridos durante la inicialización de los objetos que los usan o gestionan (cuando no hay posibilidad de que sean usados antes de ser disponibles), y liberados cuando se destruyan los mismos objetos, que se garantiza que sucede incluso cuando hay errores. 40 / 136

Herencia ● ● ● ● Permite crear una clase nueva a partir de una ya existente. Esta nueva clase contiene los atributos y métodos de la clase primaria. Este mecanismo permite construir una jerarquía de objetos cada vez más especializados. La principal ventaja de la herencia es la capacidad para definir atributos y métodos nuevos para la subclase, que luego se aplican a los atributos y métodos heredados. Herencia Simple Herencia Múltiple 41 / 136

Herencia simple class Shape { public: Shape(const char * n) : name(n) { } const char * getName() const { return name; } protected: const char * name; }; class Rectangle : public Shape { public: Rectangle(int h, int w); inline int getHeight() const { return height; } inline int getWidth() const { return width; } private: int height; int width; }; Rectangle::Rectangle(int h, int w) : Shape("Rectangle"), height(h), width(w) { } 42 / 136

¿Cómo funciona la herencia simple? class Shape { public: Shape(const char * n); inline const char * getName() const { return name; } inline int getLayer() const { return layer; } inline char getColor() const { return color; } virtual draw(int x, int y); protected: const char * name; int layer; char color; }; name layer class Circle : public Shape { public: Circle(int r) : Shape("Circle"), radius(r) { } inline int getRadius() const { return radius; } virtual draw(int x, int y); private: int radius; }; color alignment radius 43 / 136

Implementación de la herencia Implementación en C++ class Shape { public: Shape(); ~Shape(); void draw(); double x, y; Implementación equivalente en C typedef struct ShapeT { double x, y; } Shape; void Shape_new(Shape * this); void Shape_delete(Shape * this); void Shape_draw(Shape * this);; }; class Box : public Shape { public: Box(); ~Box(); double area(); double h, w; typedef struct BoxT { struct Shape shape; double h, w; } Box; void Box_new(Box* this); void Box_delete(Box* this); double Box_area(Box * this); }; 44 / 136 44 / 70

Herencia múltiple class Shape { public: Shape(const char * n) : name(n) { } const char * getName() const { return name; } protected: const char * name; }; class Storage { public: Storage(int type); }; class Rectangle : public Shape, public Storage { public: Rectangle(int h, int w); inline int getHeight() const { return height; } inline int getWidth() const { return width; } private: int height; int width; }; Rectangle::Rectangle(int h, int w) : Shape("Rectangle"), Storage(1), height(h), width(w) { } 45 / 136

Herencia múltiple class Shape { public: Shape(const char * n); inline const char * getName() const { return name; } inline int getLayer() const { return layer; } inline char getColor() const { return color; } void draw(int x, int y); protected: const char * name; int layer; char color; }; class Storage { public: bool save(int len, const char * buffer); bool load(int maxlen, char * buffer); private: FILE * file; }; class Circle : public Shape, public Storage { public: Circle(int r) : Shape("Circle"), radius(r) { } inline int getRadius() const { return radius; } virtual draw(int x, int y); private: int radius; }; name layer color alignment file radius 46 / 136

Implementación de la herencia múltiple Implementación en C++ class Shape { public: Shape(); ~Shape(); void draw(); double x, y; }; class Storage { public: bool save(int len, const char * buffer); bool load(int maxlen, char * buffer); private: FILE * file; }; class Box : public Shape, public Storage { public: Box(); ~Box(); double area(); double h, w; }; Implementación equivalente en C typedef struct ShapeT { double x, y; } Shape; void Shape_new(Shape * this); void Shape_delete(Shape * this); void Shape_draw(Shape * this);; typedef struct StorageT { FILE * file; } Storage; int Storage_save(Storage *this, int len, const char * buffer); int Storage_load(Storage *this, int maxlen, char * buffer); typedef struct BoxT { Shape shape; Storage storage; double h, w; } Box; void Box_new(Box* this); void Box_delete(Box* this); double Box_area(Box * this); 47 / 136 47 / 70

La necesidad de funciones virtuales #include <cstdlib> #include <cstdio> class Shape { public: const char * getName() const{ return "Shape"; }; }; class Rectangle : public Shape { public: const char * getName() const { return "Rectangle"; } }; class Circle : public Shape { public: const char * getName() const { return "Circle"; } }; class Triangle : public Shape { public: const char * getName() const { return "Triangle"; } }; int main() { Rectangle r1, r2; Circle c1, c2, c3; Triangle t1; Shape * shapes[] = { &r1, &c1, &t1, &r2, &c2, &c3}; for (int i=0; i<sizeof(shapes)/sizeof(Shape); ++i) printf("%sn", shapes[i]->getName()); return EXIT_SUCCESS; } 48 / 136

La necesidad de funciones virtuales #include <cstdlib> #include <cstdio> class Shape { public: const char * getName() const{ return "Shape"; }; }; class Rectangle : public Shape { public: const char * getName() const { return "Rectangle"; } }; class Circle : public Shape { public: const char * getName() const { return "Circle"; } }; class Triangle : public Shape { public: const char * getName() const { return "Triangle"; } }; Shape Shape Shape Shape Shape Shape int main() { Rectangle r1, r2; Circle c1, c2, c3; Triangle t1; Shape * shapes[] = { &r1, &c1, &t1, &r2, &c2, &c3}; for (int i=0; i<sizeof(shapes)/sizeof(Shape*); ++i) printf("%sn", shapes[i]>getName()); return EXIT_SUCCESS; } 49 / 136

Funciones virtuales #include <cstdlib> #include <cstdio> class Shape { public: virtual const char * getName() const = 0; }; class Rectangle : public Shape { public: virtual const char * getName() const { return "Rectangle"; } }; class Circle : public Shape { public: virtual const char * getName() const { return "Circle"; } }; class Triangle : public Shape { public: virtual const char * getName() const { return "Triangle"; } }; int main() { Rectangle r1, r2; Circle c1, c2, c3; Triangle t1; Shape * shapes[] = { &r1, &c1, &t1, &r2, &c2, &c3}; for (int i=0; i<sizeof(shapes)/sizeof(Shape); ++i) printf("%sn", shapes[i]->getName()); return EXIT_SUCCESS; } 50 / 136

Funciones virtuales #include <cstdlib> #include <cstdio> class Shape { public: virtual const char * getName() const = 0; }; class Rectangle : public Shape { public: virtual const char * getName() const { return "Rectangle"; } }; class Circle : public Shape { public: virtual const char * getName() const { return "Circle"; } }; class Triangle : public Shape { public: virtual const char * getName() const { return "Triangle"; } }; Rectangle Circle Triangle Rectangle Circle Circle int main() { Rectangle r1, r2; Circle c1, c2, c3; Triangle t1; Shape * shapes[] = { &r1, &c1, &t1, &r2, &c2, &c3}; for (int i=0; i<sizeof(shapes)/sizeof(Shape); ++i) printf("%sn", shapes[i]->getName()); return EXIT_SUCCESS; } 51 / 136

¿Cómo funcionan las funciones virtuales? class Shape { public: Shape(const char * n); inline const char * getName() const { return name; } inline int getLayer() const { return layer; } inline char getColor() const { return color; } virtual draw(int x, int y); vptr protected: const char * name; int layer; char color; }; name layer color alignment 52 / 136

¿Cómo funcionan las funciones virtuales? vptr type_info name type_info draw draw layer color alignment vtable 53 / 136

Implementación de las funciones virtuales Implementación en C++ Implementación equivalente en C class Virt { int a, b; virtual void foo(int); }; typedef struct VtableT { void *(*foo)(Virt * this, int x); } Vtable; void f(Virt *v) { v->foo(x); } typedef struct VirtT { int a, b; Vtable * vptr; } Virt; void f(Virt *v) { (*(v->vptr.foo))(v, x); } 54 / 136 54 / 70

Herencia múltiple con métodos virtuales class Shape { public: Shape(const char * n); inline const char * getName() const { return name; } inline int getLayer() const { return layer; } inline char getColor() const { return color; } virtual void draw(int x, int y); protected: const char * name; int layer; char color; }; class Storage { public: bool save(int len, const char * buffer); bool load(int maxlen, char * buffer); private: FILE * file; }; class Circle : public Shape, public Storage { public: Circle(int r) : Shape("Circle"), radius(r) { } inline int getRadius() const { return radius; } virtual draw(int x, int y); private: int radius; }; vptr name layer color alignment file radius 55 / 136

Funciones virtuales puras. Interfaces class IName { public: virtual const char * getName() const = 0; }; class Shape { public: Shape(const char * n) : name(n) { } virtual const char * getName() const { return name; } // IName protected: const char * name; }; class Rectangle : public IName, public Shape { public: Rectangle(int h, int w); inline int getHeight() const { return height; } inline int getWidth() const { return width; } private: int height; int width; }; Rectangle::Rectangle(int h, int w) : Shape("Rectangle"), height(h), width(w) { } 56 / 136

Herencia virtual Herencia no virtual Herencia virtual 57 / 136

El problema del diamante: Herencia Virtual class Storable { public: Storable(const char*); virtual void read(); virtual void write(); virtual ~Storable(); private: ... } class Transmitter: public Storable { public: void write(); ... } class Receiver: public Storable { public: void read(); ... }; class radio: public Transmitter, public Receiver { public: void read(); ... }; 58 / 136

El problema del diamante: Herencia Virtual class Transmitter: public virtual Storable { public: void read(); ... } class Receiver: public virtual Storable { public: void read(); ... } class Radio: public Transmitter, public Receiver { public: void read(); ... }; Radio::Radio () : Storable(10), Transmitter(), Receiver() { } 59 / 136

Modificadores de métodos de clase ● ● ● ● ● Métodos en línea (inline): El código generado para la función se insertará en el punto donde se invoca a la función, en lugar de invocarlo mediante una llamada. Métodos constantes (const): Cuando una función miembro no deba modificar ningún elemento de la clase, es conveniente declararla como constante. Esto impedirá que la función modifique los datos del objeto y permitirá acceder a este método desde objetos declarados como constantes. Métodos con un valor de retorno constante: Cuando se devuelvan punteros o referencias a elementos internos que no deban ser modificados, es conveniente devolverlos como valores constantes. Miembros estáticos (static): Este tipo de componentes son esencialmente elementos globales, de los que sólo existirá una copia que compartirán todos los objetos de la misma clase. Constructores explícitos (explicit): Indica al compilador que no use de forma automática las conversiones implícitas. 60 / 136

El modificador const int main() { int var = 1; const int const_var = 1; int * pnt_to_var = &var; const int * pnt_to_const_var = &var; int * const const_pnt_to_var = &var; var = 2; const_var = 2; // ERROR *pnt_to_var = 2; *pnt_to_const_var = 2; // ERROR *const_pnt_to_var = 2; pnt_to_var = NULL; pnt_to_const_var = NULL; const_pnt_to_var = NULL; // ERROR return EXIT_SUCCESS; } 61 / 136

Métodos constantes class A { public: A() : state(0) { } void setState(int new_state) { state = new_state; } int getState() const { return state; } private: int state; }; int main() { A a; a.setState(5); a.getState(); const A b; b.setState(5); // Error b.getState(); return EXIT_SUCCESS; } 62 / 136

Métodos constantes y punteros class A { public: A() : pointer(NULL) { } A(const A & other) : pointer(other.pointer) { } void setPointer(int * new_pointer) { pointer = new_pointer; } void setPointer(int & new_pointer) { pointer = &new_pointer; } int * getPointer() const { return pointer; } private: int * pointer; }; int main() { int val; A a; a.setPointer(&val); a.setPointer(val); a.getPointer(); const A b = a; b.setPointer(&val); // Error b.setPointer(val); // Error b.getPointer(); *b.getPointer() = 5; return EXIT_SUCCESS; } 63 / 136

Métodos constantes y retornos constantes class A { public: A() : pointer(NULL) { } A(const A & other) : pointer(other.pointer) { } void setPointer(int * new_pointer) { pointer = new_pointer; } void setPointer(int & new_pointer) { pointer = &new_pointer; } int * getPointer() const { return pointer; } const int * getConstPointer() const { return pointer; } private: int * pointer; }; int main() { int val; A a; a.setPointer(&val); a.setPointer(val); a.getPointer(); *a.getPointer() = 5; *a.getConstPointer() = 5; // Error const A b = a; b.setPointer(&val); // Error b.setPointer(val); // Error b.getPointer(); *b.getPointer() = 5; *b.getConstPointer() = 5; // Error return EXIT_SUCCESS; } 64 / 136

Métodos constantes class A { public: A() : pointer(NULL) { } A(const A & other) : pointer(other.pointer) { } void setPointer(int * new_pointer) { pointer = new_pointer; } void setPointer(int & new_pointer) { pointer = &new_pointer; } int * getPointer() { return pointer; } const int * getPointer() const { return pointer; } private: int * pointer; }; int main() { int val; A a; a.setPointer(&val); a.setPointer(val); a.getPointer(); *a.getPointer() = 5; const A b = a; b.setPointer(&val); // Error b.setPointer(val); // Error b.getPointer(); *b.getPointer() = 5; // Error return EXIT_SUCCESS; } 65 / 136

Variables y métodos de clase (static) ● ● ● ● Pertenecen a la clase, y no a los objetos que pueden crearse de la misma. Sus valores son compartidos por todos los objetos creados a partir de esa clase. Van precedidas del modificador static. Para referenciar una variable estática, o invocar a un método estático, no es necesario crear un objeto de la clase. class Particle { public: Particle(); Particle(const & Particle p); ~Particle(); static inline int NumParticles() const { return num_particles; } static int num_particles; }; int Particle::num_particles; Particle::Particle() { ++num_particles; } Particle::Particle(const & Particle p) { ++num_particles; } Particle::~Particle() { --num_particles; } Particle::NumParticles(); Particle::num_particles(); 66 / 136

Relaciones entre clases

Relaciones entre clases: herencia La clase derivada es semejante a la clase original, pero con algunas propiedades o métodos añadidos o modificados. La clase derivada -o subclase- es una especialización -o extensión- de la clase base -o superclase-. Ejemplo: Una moto, o un coche, son un tipo especial de vehículos. 68 / 136

Relaciones entre clases: herencia class Vehicle { public: int Length; int Width; int Height; int Weight; }; class MotorVehicle : public Vehicle { public: int HorsePower; int MaxSpeed; }; class Car : public MotorVehicle { public: int NumDoors; }; 69 / 136

Relaciones entre clases: agregación La clase agregada tiene una relación “débil” con la clase que contiene la agregación. La destrucción de un objeto de la clase contenedora no implica la destrucción de los objetos que están agregados por el mismo. Ejemplo: Yo tengo una relación con mis amigos, pero no les “poseo”. 70 / 136

Relaciones entre clases: agregación class Car { public: int NumDoors; }; class Road { public: static const int MaxNumVehicles = 4; Vehicle * Vehicles[MaxNumVehicles]; }; 71 / 136

Relaciones entre clases: composición La composición define una relación “fuerte” entre objetos. El objeto contenedor “posee” los objetos que están contenidos en él, de tal forma que la destrucción del mismo implica la destrucción de todas sus partes. Ejemplo: Una mesa contiene un tablero y varias patas. 72 / 136

Relaciones entre clases: composición class Wheel { int Radius; int Wear; int Pressure; }; class Car { public: static const int NumWheels = 4; Wheel Wheels[NumWheels]; int NumDoors; }; 73 / 136

Relaciones entre clases: implementación La implementación de un interfaz equivale a la aceptación de un contrato entre las partes relacionadas, de tal forma que el interfaz define de qué formas puede interaccionar la clase con el resto de objetos. En C++ no existe específicamente la figura del interfaz, y se implementan con clases abstractas en las que todos sus métodos son virtuales. Un interfaz no debe contener atributos. Unos interfaces pueden heredar de otros, y añadir nuevos métodos. 74 / 136

Relaciones entre clases: implementación class IDrivable { virtual void SpeedUp(int value) = 0; virtual void Brake(int value) = 0; virtual void Turn(int value) = 0; }; class Car : public: virtual virtual virtual public IDrivable { void SpeedUp(int value) { ... } void Brake(int value) { ... } void Turn(int value) { ... } int NumDoors; }; 75 / 136

Habitualmente se usan todas ellas class Vehicle { public: int Length; int Width; int Height; int Weight; }; class MotorVehicle : public Vehicle { public: int HorsePower; int MaxSpeed; }; class Wheel { int Radius; int Wear; int Pressure; }; class IDrivable { virtual void SpeedUp(int value) = 0; virtual void Brake(int value) = 0; virtual void Turn(int value) = 0; }; class Car : public: virtual virtual virtual public MotorVehicle, public IDrivable { void SpeedUp(int value) { ... } void Brake(int value) { ... } void Turn(int value) { ... } static const int NumWheels = 4; Wheel Wheels[NumWheels]; int NumDoors; }; class Road { public: static const int MaxNumVehicles = 4; Vehicle * Vehicles[MaxNumVehicles]; }; 76 / 136

Algunos patrones de diseño

Los Patrones de Diseño ● ● ● Un patrón de diseño es una solución estandarizada a un problema de diseño. Para que una solución sea considerada un patrón debe poseer ciertas características: ● Debe haber comprobado su efectividad resolviendo problemas similares en ocasiones anteriores. ● Debe ser reutilizable, lo que significa que es aplicable a diferentes problemas de diseño en distintas circunstancias. Los patrones de diseño pretenden: ● Proporcionar catálogos de elementos reusables en el diseño de sistemas software. ● Evitar la reiteración en la búsqueda de soluciones a problemas ya conocidos y solucionados anteriormente. ● Formalizar un vocabulario común entre diseñadores. ● Estandarizar el modo en que se realiza el diseño. ● Facilitar el aprendizaje de las nuevas generaciones de diseñadores condensando conocimiento ya existente. 78 / 136

Abstract Factory Pattern (estructural) Proporciona una interfaz para crear familias de objetos relacionados o dependientes, sin especificar sus clases concretas. Se utiliza este patrón cuando se necesita: ● Crear una familia de objetos relacionados, diseñada para ser utilizada en conjunto. ● Que un sistema sea independiente de la forma en que sus productos son creados, compuestos o representados. ● Que un sistema sea configurado con una de múltiples familias de productos. ● Proporcionar una biblioteca de clases de productos, y sólo se quiere revelar sus interfaces, no sus implementaciones. Fuente: http://design-patterns-with-uml.blogspot.com.ar/search/label/Abstract%20Factory%20Pattern 79 / 136

Abstract Factory Pattern (estructural) Fuente: http://design-patterns-with-uml.blogspot.com.ar/search/label/Abstract%20Factory%20Pattern 80 / 136

Adapter Pattern (estructural) Transforma la interfaz de un objeto existente en otra que los clientes puedan utilizar. De este modo, una clase que no puede utilizar la primera, puede hacer uso de ella a través de la segunda. Se utiliza este patrón cuando se necesita: ● Utilizar una clase existente, pero su interfaz no coincide Usando composición con la que se necesita. ● Crear una clase reutilizable que coopera con clases que no tienen interfaces compatibles. ● Utilizar varias subclases existentes, pero como es poco práctico adaptar cada interfaz, se crea un objeto que adapte la interfaz de la clase padre. Usando herencia múltiple Fuente: http://design-patterns-with-uml.blogspot.com.ar/search/label/Adapter%20Pattern 81 / 136

Adapter Pattern (estructural) Usando composición Usando herencia múltiple Fuente: http://design-patterns-with-uml.blogspot.com.ar/search/label/Adapter%20Pattern 82 / 136

Decorator Pattern (estructural) Extiende la funcionalidad de un objeto en forma dinámica, proporcionando una alternativa flexible a la creación de subclases. Se utiliza este patrón cuando se necesita: ● Añadir responsabilidades a objetos individuales dinámica y transparente, es decir, sin afectar a otros objetos. ● Retirar responsabilidades de algunos objetos. ● La extensión por subclases es poco práctica. Por ejemplo, un gran número de extensiones independientes son posibles y produciría una explosión de subclases por cada combinación. También cuando la definición de clase puede estar oculta o no disponible para subclases. Fuente: http://design-patterns-with-uml.blogspot.com.ar/search/label/Decorator%20Pattern 83 / 136

Decorator Pattern (estructural) Fuente: http://design-patterns-with-uml.blogspot.com.ar/search/label/Decorator%20Pattern 84 / 136

Facade Pattern (estructural) Proporciona una interfaz unificada para un conjunto de interfaces de un sistema, y hace que los subsistemas sean mas facil de usar. Se utiliza este patrón cuando se necesita: ● Minimizar la comunicación y las dependencias entre subsistemas. ● Proporcionar una interfaz sencilla para un subsistema complejo. ● Eliminar dependencias entre clientes y clases de implementación, porque son demasiadas. ● Dividir conjuntos de subsistemas en capas. Fuente: http://design-patterns-with-uml.blogspot.com.ar/search/label/Facade%20Pattern 85 / 136

Facade Pattern (estructural) Fuente: http://design-patterns-with-uml.blogspot.com.ar/search/label/Facade%20Pattern 86 / 136

Iterator Pattern (de comportamiento) Proporciona una forma de acceder secuencialmente a los elementos de un objeto agregado, sin exponer su representación interna. Se utiliza este patrón cuando se necesita: ● Para acceder al contenido de un objeto agregado sin exponer su representación interna. ● Para apoyar recorridos múltiples sobre objetos agregados. ● Para proporcionar una interfaz uniforme para atravesar diferentes estructuras agregadas (es decir, para apoyar iteración polimórfica). Fuente: http://design-patterns-with-uml.blogspot.com.ar/search/label/Iterator%20Pattern 87 / 136

Iterator Pattern (de comportamiento) Fuente: http://design-patterns-with-uml.blogspot.com.ar/search/label/Iterator%20Pattern 88 / 136

Observer Pattern (de comportamiento) Define una dependencia de uno-a-muchos con otros objetos, de forma que cuando un objeto cambia de estado todos sus dependientes son notificados y actualizados automáticamente. Se utiliza este patrón cuando se necesita: ● Cuando una abstracción tiene dos aspectos dependientes. Encapsular éstos en objetos separados permite modificarlos y usarlos de forma independiente. ● Cuando un cambio en un objeto requiere cambiar a los demás, y no se sabe cuáles ni cuántos objetos serán. ● Cuando un objeto debe ser capaz de notificar a otros objetos sin hacer suposiciones acerca de quién son estos objetos. En otras palabras, no se quiere que estos objetos estén estrechamente acoplados. Fuente: http://design-patterns-with-uml.blogspot.com.ar/search/label/Observer%20Pattern 89 / 136

Observer Pattern (de comportamiento) Fuente: http://design-patterns-with-uml.blogspot.com.ar/search/label/Observer%20Pattern 90 / 136

Singleton Pattern (creacional) Garantiza que una clase tenga únicamente una instancia y proporciona un punto de acceso global a la misma. Se utiliza este patrón cuando se necesita: ● Debe haber exactamente una instancia de una clase, y debe ser accesible a los clientes desde un punto de acceso conocido. ● La instancia única debe ser extensible por medio de subclases, y los clientes deben ser capaces de utilizar esta instancia extendida sin modificar su código. Fuente: http://design-patterns-with-uml.blogspot.com.ar/search/label/Singleton%20Pattern 91 / 136

Singleton Pattern (creacional) Fuente: http://design-patterns-with-uml.blogspot.com.ar/search/label/Singleton%20Pattern 92 / 136

State Pattern (de comportamiento) Altera el comportamiento de un objeto según el estado interno en que se encuentre. Así, el objeto aparentará cambiar de clase. Se utiliza este patrón cuando se necesita: ● El comportamiento de un objeto depende de su estado, y tiene que cambiar su comportamiento en tiempo de ejecución en función de ese estado. ● Se presentan muchos condicionales en el código. Fuente: http://design-patterns-with-uml.blogspot.com.ar/search/label/State%20Pattern 93 / 136

State Pattern (de comportamiento) Fuente: http://design-patterns-with-uml.blogspot.com.ar/search/label/State%20Pattern 94 / 136

Strategy Pattern (de comportamiento) Define un conjunto de clases que representan comportamientos similares: se trata de hacer lo mismo, pero de diferente manera. Permite que un algoritmo varíe independientemente de los clientes que lo utilizan, y puedan intercambiarlos en tiempo de ejecución. Se utiliza este patrón cuando se necesita: ● Muchas clases relacionadas difieren sólo en su comportamiento. ● Se necesita diferentes variantes de un algoritmo. ● Se presentan muchos condicionales en el código, es posible que sea necesario aplicar este patrón. ● Si un algoritmo utiliza información que no deberían conocer los clientes, la utilización del patrón estrategia evita la exposición de dichas estructuras. Fuente: http://design-patterns-with-uml.blogspot.com.ar/search/label/Strategy%20Pattern 95 / 136

Strategy Pattern (de comportamiento) Fuente: http://design-patterns-with-uml.blogspot.com.ar/search/label/Strategy%20Pattern 96 / 136

Template Pattern (de comportamiento) Define el esqueleto de un algoritmo, dejando que las subclases redefinan ciertos pasos, pero sin cambiar su estructura. Se utiliza este patrón cuando se necesita: ● Aplicar las partes invariables de un algoritmo una vez y dejar en manos de las subclases la implementación del comportamiento que puede variar. ● Evitar la replicación de código mediante generalización: se factoriza el comportamiento común de varias subclases en una única superclase. ● Controlar las extensiones de las subclases. El Método Plantilla utiliza métodos especiales (métodos de enganche o hooks) en ciertos puntos, siendo los únicos puntos que pueden ser redefinidos y, por tanto, los únicos puntos donde es posible la extensión. Fuente: http://design-patterns-with-uml.blogspot.com.ar/search/label/Template%20Pattern 97 / 136

Template Pattern (de comportamiento) Fuente: http://design-patterns-with-uml.blogspot.com.ar/search/label/Template%20Pattern 98 / 136

La Programación Genérica

La Programación Genérica ● ● ● En su definición más sencilla, es un estilo de programación centrado en los algoritmos, en el que éstos se programan en función de tipos de datos que serán especificados más tarde. Este enfoque permite escribir funciones y estructuras que se diferencian sólo en el conjunto de tipos de datos sobre los que se opera, lo que reduce la duplicación de código. En un sentido más específico, describe un paradigma de programación en el que los requisitos fundamentales sobre los tipos se abstraen de ejemplos concretos de algoritmos y estructuras de datos y se formalizan como conceptos, y en el que las funciones se implementan de forma genérica en términos de estos conceptos. En C++ se usan templates (o plantillas) para permitir el uso de técnicas de programación genéricas. 100 / 136

Ejemplo de templates en C++ template <typename T> T max(T x, T y) { return x < y ? y : x; } Cuando especializa esa función templatizada, el compilador instancia una versión se esa función, en la que el tipo genérico T es sustituido por el tipo de dato que queramos usar: std::cout << max(3, 7); // el resultado es 7 El compilador genera entonces, de forma automática, una función correspondiente a la sustitución de T por int: int max(int x, int y) { return x < y ? y : x; } La función resultante de la especialización de una función genérica puede ser usada como cualquier otra función dentro del lenguaje. Como se puede ver en el código, los requisitos necesarios para poder usar esta función con un tipo de datos cualquiera son: ● Existencia del operador < para el tipo de dato T ● Existencia de un operador asignación para T 101 / 136

Templates: Singleton template<typename T> class Singleton { public: Singleton(T & instance) { sfpInstance = &instance; } ~Singleton() { sfpInstance = 0L; } static T & getInstance() return *sfpInstance; } { private: static T * sfpInstance; }; template<typename T> T * Singleton<T>::sfpInstance(0L); 102 / 136

Metaprogramación basada en plantillas ● ● ● ● La Metaprogramación basada en plantillas o Template metaprogramming (TMP) es una técnica en la que, mediante el uso de plantillas, el compilador genera un código fuente temporal que es añadido al resto del código del programa antes de que éste sea compilado. La salida de estas plantillas incluyen constantes en tiempo de compilación, estructuras de datos y funciones completas. El uso de plantillas puede ser interpretado como “ejecución en tiempo de compilación”. Históricamente, la TMP fue descubierta accidentalmente durante el proceso de estandarización del lenguaje C++, al darse cuenta de que el sistema de plantillas de C++ es Turing-completo (es decir, que tiene un poder computacional equivalente a la máquina universal de Turing y, por tanto, se puede programar cualquier cosa en él). 103 / 136

TMP: Valores y Funciones #include <cstdlib> #include <iostream> struct NumberTwoHolder { enum { value = 2 }; }; template<int N> struct ValueHolder { enum { value = N }; }; template<int X, int Y> struct Adder { enum { result = X + Y }; }; struct TypeHolder { typedef int value; }; int main() { std::cout << NumberTwoHolder::value << std::endl; // 2 std::cout << ValueHolder<3>::value << std::endl; // 3 std::cout << Adder<3,4>::result << std::endl; // 7 std::cout << sizeof(TypeHolder::value) << std::endl; // 4 return EXIT_SUCCESS; } 104 / 136

TMP: Diferentes líneas de ejecución template<typename X, typename Y> struct SameType { enum { result = 0 }; }; template<typename T> struct SameType<T, T> { enum { result = 1 }; }; if (SameType<SomeThirdPartyType, int>::result) { // ... codigo optimizado, asumiendo que el tipo es un entero } else { // ... codigo defensivo que no hace ninguna suposicion sobre el tipo } 105 / 136

TMP: Recursión template <unsigned n> struct factorial { enum { value = n * factorial<n-1>::value }; }; template <> struct factorial<0> { enum { value = 1 }; }; int main() { // Como los calculos se realizan en tiempo de compilacion, // se puede usar para cosas como el tamaño de los arrays int array[ factorial<7>::value ]; } 106 / 136

TMP: Ejemplo: “if” en tiempo de compilación #include <cstdlib> #include <iostream> template <bool Condition, typename TrueResult, typename FalseResult> class if_; template <typename TrueResult, typename FalseResult> struct if_<true, TrueResult, FalseResult> { typedef TrueResult result; }; template <typename TrueResult, typename FalseResult> struct if_<false, TrueResult, FalseResult> { typedef FalseResult result; }; int main() { typename if_<true, int, void*>::result number(3); typename if_<false, int, void*>::result pointer(&number); typedef typename if_<(sizeof(void *) > sizeof(uint32_t)), uint64_t, uint32_t>::result integral_ptr_t; integral_ptr_t converted_pointer = reinterpret_cast<integral_ptr_t>(pointer); std::cout << sizeof(void *) << std::endl; std::cout << sizeof(uint32_t) << std::endl; std::cout << sizeof(integral_ptr_t) << std::endl; return EXIT_SUCCESS; } 107 / 136

TMP: Ejemplo: Comprobaciones estáticas template<class T, class B> struct Derived_from { static void constraints(T * p) { B * pb = p; pb = pb; } Derived_from() { void(*p)(T*) = constraints; p = p; } }; template<class T1, class T2> struct Can_copy { static void constraints(T1 a, T2 b) { T2 c = a; b = a; c = a; b = a; } Can_copy() { void(*p)(T1,T2) = constraints; } }; template<class T1, class T2 = T1> struct Can_compare { static void constraints(T1 a, T2 b) { a == b; a != b; a < b; } Can_compare() { void(*p)(T1,T2) = constraints; } }; 108 / 136

TMP: Ejemplo: Lista de Valores (1) #include <cstdlib> #include <iostream> struct NullNumList {}; template<size_t H, typename T> struct NumList { enum { head = H }; typedef T tail; }; template<typename NL> struct binaryOr; template<size_t NL_H, typename NL_TAIL> struct binaryOr<NumList<NL_H, NL_TAIL> > { enum { value = NL_H | binaryOr<NL_TAIL>::value }; }; template<> struct binaryOr<NullNumList> { enum { value = 0 }; }; 109 / 136

TMP: Ejemplo: Lista de Valores (y 2) template<char T> struct MyList; template<> struct MyList<'a'> { Typedef NumList<1, NumList<2, NullNumList> > data; // 1 | 2 = 3 }; template<> struct MyList<'b'> { Typedef NumList<1, NumList<4, NumList<8, NullNumList> > > data; // 1 | 4 | 8 = 13 }; int main() { std::cout << binaryOr<MyList<'a'>::data >::value << std::endl; // 3 std::cout << binaryOr<MyList<'b'>::data >::value << std::endl; // 13 return EXIT_SUCCESS; } 110 / 136

C++ como lenguaje multiparadigma

¿Qué quiere decir multiparadigma? ● ● ● C++ no es solamente un lenguaje orientado a objetos, sino que es compatible con muchos estilos diferentes de programación, o paradigmas. La programación orientada a objetos es sólo uno de ellos. Ningún paradigma es capaz de resolver todos los problemas de forma sencilla y eficiente, por lo que es muy útil poder elegir el que más conviene para cada tarea. Habitualmente se combinan diferentes tipos de paradigmas dentro de los programas. 112 / 136

C++ como ensamblador de alto nivel ● ● ● ● ● ● C++ permite programar de una forma muy cercana al propio funcionamiento de la máquina. La programación imperativa describe las operaciones en términos del estado de un programa, y de operaciones que cambian dicho estado. Los programas imperativos son un conjunto de instrucciones que le indican al ordenador cómo realizar una tarea. El hardware habitual de los ordenadores actuales implementa el concepto de máquina de Turing y, por tanto, está diseñado para ejecutar código escrito en forma imperativa. C++ hereda de C un sistema de tipos de datos que se pueden mapear de una forma muy cercana a los tipos nativos de la CPU, y operaciones primitivas que también se pueden mapear de una forma muy directa a instrucciones en código máquina. Se pueden incorporar instrucciones directas en ensamblador dentro del programa, lo que permite un control muy exacto del programa resultante, a cambio de perder en claridad y portabilidad. Esta forma de programación puede resultar muy críptica y oscura a veces, pero a la hora de hacer desarrollos de bajo nivel, incluidos los componentes que interaccionan con el hardware o aquellos que necesitan un alto nivel de optimización, es imprescindible. 113 / 136

C++ como ensamblador de alto nivel uint8_t mask = 0x3F; mask |= 0x40; uint16_t reg1 = 0x25; uint16_t reg2 = reg1 << 3; if (tp->pending & tp->ewmask) != 0) { do_e(tp); } else { queue->add(tp); } 114 / 136

Programación estructurada en C++ ● ● ● ● ● Este es el estilo de programación clásico heredado de C. El programa comienza en una función principal, llamada main, que llama a otras funciones, que a su vez llaman a otras. El flujo de ejecución del programa está controlado por expresiones estructuradas como for, while, switch o until. Las variables son visibles dentro del bloque en el que han sido definidas, y no son accesibles directamente desde fuera de ese bloque. Las funciones tienen un único punto de entrada, y una única forma de terminación. Este estilo de programación incluye también la estructuración de los datos. Para ello existe un mecanismo, struct, que permite organizar los datos en forma de una única entidad que los englobe, y que puede ser copiada por valor, transferida por valor a otras funciones, etc. 115 / 136

Programación estructurada en C++ void funcC(void) { for (int i = 0; i < MaxI(); ++i) { if (funcB(i)) { doSomething() } } while (funcA()) { whatever(); }; } 116 / 136

Abstracción de datos en C++ ● ● ● ● ● Consiste agrupar en componentes, de forma unificada, tanto los datos como los métodos que permiten manipularlos. Ejemplos clásicos de esta abstracción de tipos de datos son las pilas y las listas. En lugar de usar estructuras de datos con funciones separadas para manipular estos datos, las funciones son incorporadas al propio componente que los contiene. En el caso de una pila, estas funciones serían las operaciones push y pop, por ejemplo. El sello distintivo de este paradigma es el uso de tipos de datos concretos. Por ello, las funciones virtuales apenas se usan, y los objetos son copiados y asignados libremente. En concreto, los operadores de copia y construcción existen para poder dar soporte a este estilo de programación, y Tienen una gran importancia en permitir la creación de nuevos tipos de datos que se comporten casi igual que los tipos nativos, como int o double. 117 / 136

Abstracción de datos en C++ struct Queue { Queue(); void push(int v); int pop(); static const MAX = 29; int data[NAX] }; Queue q; q.push(5); q.push(3); int a = q.pop(); 118 / 136

Programación orientada a objetos en C++ ● ● ● ● ● ● ● La programación orientada a objetos en C++ se implementa en base a clases. Las propiedades de los objetos se almacenan en estructuras (class o struct). En ellas se almacena de forma estructurada toda la información relativa a un objeto. C++ da soporte también a métodos y propiedades de clase (que son, en muchos aspectos, semejantes a las variables y funciones globales). Los mensajes entre objetos se implementan en base a métodos o funciones, que se comportan de una forma parecida a las funciones de C, añadiéndoles un primer parámetro oculto que referencia la estructura de datos que contiene las propiedades del objeto, y que pueden llevar información adicional en forma de parámetros. Se puede restringir el acceso a las propiedades o a los métodos de una clase mediante los modificadores public, protected y private. El paradigma de orientación a objetos hace un uso extensivo de las funciones virtuales y del polimorfismo. En C++, los tipos polimórficos son la referencia y el puntero. Hay que tener mucho cuidado con ellos a la hora de copiar o asignar objetos, puesto que la asignación de un objeto derivado a una clase base puede implicar que el objeto sea truncado, copiándose sólo la información perteneciente al tipo base. 119 / 136

Programación orientada a objetos en C++ class IDrivable { virtual void SpeedUp(int value) = 0; virtual void Brake(int value) = 0; virtual void Turn(int value) = 0; }; class Car : public: virtual virtual virtual public IDrivable { void SpeedUp(int value) { ... } void Brake(int value) { ... } void Turn(int value) { ... } int NumDoors; }; IDrivable auto = new Car(); Car.SpeedUp(15); 120 / 136

Programación genérica en C++ ● ● ● ● ● ● ● ● La programación genérica es la programación con conceptos (concepts) . Los algoritmos son abstraidos, para permitirles trabajar sobre cualquier tipo de dato que pueda satisfacer el interfaz usado por el algoritmo. En la librería estándar de C++, hay componentes como container, iterator o algorithm que forman una triada para dar soporte a la programación genérica. Este paradigma está construido también sobre la abstracción de los datos, pero tomándola en una dirección diferente a la de la programación orientada a objetos. El mecanismo de C++ que permite soportar la programación genérica son las plantillas o templates, que es el mecanismo de parametrización de tipos provisto por el lenguaje. EL uso de plantillas tiene un precio: las librerías son más difíciles de usar, y los problemas se traducen en mensajes de error confusos. Tal y como están definidas en C++98, las plantillas no tienen restricciones, y la comprobación de tipos se realiza al final del proceso de compilación, es decir, después de combinar la plantilla con definición del tipo de datos. C++11 incluye los conceptos (concepts) para expresar el comportamiento sintáctico y semántico de los tipos, y para restringir los parámetros de tipo que se pueden usar en una plantilla de C++. 121 / 136

Programación genérica en C++ template <unsigned n> struct factorial { enum { value = n * factorial<n-1>::value }; }; template <> struct factorial<0> { enum { value = 1 }; }; int main() { // Como los calculos se hacen en tiempo de compilacion, // se puede usar para cosas como el tamaño de los arrays int array[ factorial<7>::value ]; } 122 / 136

Programación funcional en C++ ● ● ● ● ● ● ● La esencia de la programación funcional consiste en trabajar con valores, en lugar de hacerlo con identidades. La principal diferencia entre un valor y una identidad, es que el valor es constante e inalterable (el número 1 es siempre el mismo), mientras que el valor asociado a una identidad puede cambiar (el contenido de una variable). El modificador const provee un método eficiente para el paso de objetos grandes a la hora de trabajar con sus valores, y no con sus identidades. Mediante el uso del modificador const, C++ permite tipos de datos inmutables, semejantes a los que se encuentran en la mayor parte de los lenguajes de programación funcionales. Sin embargo, trabajar con estos tipos de datos inmutables en C++ es difícil y engorroso. En particular, la necesidad de hacer copias completas de objetos de gran tamaño para cada pequeño cambio, no es eficiente. C++11 incorpora una serie de herramientas nuevas para facilitar el estilo de porgramación funcional en C++. La más importante de ellas quizás sean las funciones lambda (también llamadas closures o funciones anónimas). Esta funcionalidad, de todas formas, ya estaba accesible en base al uso de functors, muy poderosos pero menos prácticos de usar. De hecho, entre bastidores, C++ implementa las funciones lambda como functors. C++11 también provee estructuras para trabajar con información compartida, de tal forma que se facilite el trabajo eficiente con tipos de datos inmutables. 123 / 136

Programación funcional en C++ #include <iostream> // abstract base class for a ontinuation functor struct continuation { virtual void operator() (unsigned) const = 0; }; // accumulating continuation functor struct accum_cont: public continuation { private: unsigned accumulator_; const continuation &enclosing_; public: accum_cont(unsigned accumulator, const continuation &enclosing) : accumulator_(accumulator), enclosing_(enclosing) { }; virtual void ope

Add a comment

Related presentations

Related pages

FIESTA FIN DE CURSO C.P. RODRIGUEZ CRUZ 2014 - YouTube

FIESTA FIN DE CURSO C.P. RODRIGUEZ CRUZ 2014 villafrancatv. ... FIESTA FIN DE CURSO 2014 CRA NTRA SEÑORA DE LA PAZ - Duration: 25:03.
Read more

FIESTA FIN DE CURSO C.P. PILAR 2014 - YouTube

FIESTA FIN DE CURSO C.P. RODRIGUEZ CRUZ 2014 - Duration: 1:09:40. villafrancatv 1,916 views. 1:09:40
Read more

Plan Lector Curso 2014 2015 by C.E.I.P.FUENTE DEL BADEN ...

Documento que recoge Plan Lector C.E.I.P. Fuente del Badén | Issuu is a digital publishing platform that makes it simple to publish magazines, catalogs ...
Read more

Plan de curso conceptual c 2014 by Instituto Alberto ...

escolar: evaluación académica y evaluación actitudinal. O Elección de estudiantes representantes, comisionados éticos, comisionados deportivos y ...
Read more

CURSO De Verano 2014 -Instituto de Desarrollo Artístico A ...

CURSO De Verano 2014 -Instituto de Desarrollo Artístico A.C. De Ofelia Cano. 307 likes · 3 were here. ¡El Mejor Curso de Verano en este 2014! Actúa,...
Read more

FGUMA: CURSO B2.2 F.C.E. (2013-2014) - fguma.cv.uma.es

Fundación General de la Universidad de Málaga. Omitir Navegación. Navegación
Read more

CURSO ESCOLAR 2014-2015 - Fergomez_SSC

1º CURSO C.F.G.S. E.D.I. DESARROLLO COGNITIVO Y MOTOR (D.C.M.) CURSO ESCOLAR 2012-2013. ... Bienvenid@s al curso escolar 2014-2015.
Read more

Cuaderno de Prácticas - 1er. C.: Gimnasia. Curso 2014-2015 ...

Cuaderno de Prácticas - 1er. C.: Gimnasia. Curso 2014-2015: Amazon.de: Consuelo Martínez Martínez, José Francisco Sánchis Bertomeu: Fremdsprachige Bücher
Read more

Curso 2013-2014 - Fergomez_SSC

Curso 2013-2014. 2º A.P.S.D. Curso 2013-2014. FCT. Fotos_Diferentes Promociones. ... 2º CURSO C.F.G.S. E.D.I. Expresión y Comunicación. Proyecto Musical.
Read more

www.microsoft.com

Object Moved This document may be found here
Read more