29
C++ for C Programmers at Cursera Module 1: C++ as a Better C // es utilizado para comentarios, reemplazando generalmente el uso de /* y */ iostream como la librería de I/O Se desalienta el uso de .h en la definición de las librerías Las librerías estándar de C siguen existiendo y comienzan con una “c” seguido del nombre estándar como cstdlib o ctime Se crea el concepto de namespaces… use namespace std; como un método de lograr encapsulación. La encapsulación facilita debugear el código en forma aislada y luego reutilizarlo en otros módulos. Use const en lugar de macros permite la definición de tipos y que el compilador pueda verificar la expresión con las reglas del lenguaje en lugar de utilizar un reemplazo textual, por ejemplo puede verificar que se utilice el tipo correcto y que la variable definida como constante no intente ser modificada en ese “scope” o “contexto”. “const” es una decoración de la declaración. Usar const lo más posible, por ejemplo en parámetros que no son modificados, ya que además de permitir al compilador encontrar errores, en algunos casos permite realizar optimizaciones. De la misma forma inline sirve para definir pequeños trozos de código que generalmente en C son definidos como macros, lo que sirve para facilitar la identificación de errores de programación. Dentro de la librería estándar se definen los stream cin y cout. En la sintaxis std::cout los “::” es el scope resolution operator. Los operadores se sobrecargan o son “overloaded”, lo que significa que se le puede dar un nuevo significado. En C++ se pueden sobrecargar “funciones” y “operadores”. Se pueden definir variables en cualquier lugar del bloque, no solamente al inicio, esto permite realizar las definiciones lo más cerca posible de donde se usa, lo cual es una ventaja especialmente en códigos largos. Si el código es corto, aún sigue siendo costumbre colocar las definiciones al principio y comentarlas. La instrucción srand(time(NULL)); inicializa la función que genera números al azar, y la función int rand(void); devuelve un número entre 0 y RAND_MAX. Al dividir el resultado por RAND_MAX como dobles se obtiene una probabilidad entre 0.0 y 1.0. Ambas funciones están en

c for c Programmers at Curse Ra

Embed Size (px)

Citation preview

Page 1: c for c Programmers at Curse Ra

C++ for C Programmers at Cursera

Module 1: C++ as a Better C

// es utilizado para comentarios, reemplazando generalmente el uso de /* y */

iostream como la librería de I/O

Se desalienta el uso de .h en la definición de las librerías

Las librerías estándar de C siguen existiendo y comienzan con una “c” seguido del nombreestándar como cstdlib o ctime

Se crea el concepto de namespaces… use namespace std; como un método de lograrencapsulación. La encapsulación facilita debugear el código en forma aislada y luego reutilizarloen otros módulos.

Use const en lugar de macros permite la definición de tipos y que el compilador pueda verificarla expresión con las reglas del lenguaje en lugar de utilizar un reemplazo textual, por ejemplopuede verificar que se utilice el tipo correcto y que la variable definida como constante no intenteser modificada en ese “scope” o “contexto”. “const” es una decoración de la declaración.

Usar const lo más posible, por ejemplo en parámetros que no son modificados, ya que ademásde permitir al compilador encontrar errores, en algunos casos permite realizar optimizaciones.

De la misma forma inline sirve para definir pequeños trozos de código que generalmente en Cson definidos como macros, lo que sirve para facilitar la identificación de errores deprogramación.

Dentro de la librería estándar se definen los stream cin y cout. En la sintaxis std::cout los “::” esel scope resolution operator.

Los operadores se sobrecargan o son “overloaded”, lo que significa que se le puede dar unnuevo significado. En C++ se pueden sobrecargar “funciones” y “operadores”.

Se pueden definir variables en cualquier lugar del bloque, no solamente al inicio, esto permiterealizar las definiciones lo más cerca posible de donde se usa, lo cual es una ventajaespecialmente en códigos largos. Si el código es corto, aún sigue siendo costumbre colocar lasdefiniciones al principio y comentarlas.

La instrucción srand(time(NULL)); inicializa la función que genera números al azar, y la funciónint rand(void); devuelve un número entre 0 y RAND_MAX. Al dividir el resultado porRAND_MAX como dobles se obtiene una probabilidad entre 0.0 y 1.0. Ambas funciones están en

Page 2: c for c Programmers at Curse Ra

la librería <cstdlib>.

El manejo de la memoria “heap” se hace con malloc() y free(). En C++ estas funciones sonreemplazadas por las palabras clave new y delete los cuales implementan seguridad de tipos.Para borrar arrays la sintaxis es delete[] var;

Es posible definir variables en el bloque de inicialización de una instrucción for(;;). Es usadobásicamente para definir las variables que iteran en el lazo.

Si bien es posible utilizar los métodos antiguos de “cast” como (”nuevo tipo”)“expresión”, C++define nuevos métodos de cambiar el tipo que son más seguros. Uno de ellos esstatic_cast<”nuevo tipo”>(“expresión”)

A endl se lo llama “manipulador de I/O” que especifica cambiar a la próxima línea en un“ostream”.

En C los parámetros son solo pasados “por valor”, es decir que se hace una copia de losvalores que se pasan a la función y cualquier cambio en los parámetros dentro de la función sepierden al salir de la misma.

int* i; debe ser leido como “i es un puntero a un int”. Si luego se encuentra con la expresión *i =1; en ese caso el “*” es una dereferencia, lo que debe leerse como “el int apuntado por i”.

Dada la expresión i = &m; en este cado el “&m” debe leerse como “la dirección de m”

C++ incluye una forma de pasar los parámetros “por referencia”. La sintaxis es int& i; y debeser leído como “la referencia del int i”. Cambios de estos valores dentro la función afectan elvalor de los parámetros al salir de la misma.

El pasaje de parámetros por referencia es más eficiente ya que no necesita realizar la copia delos valores en variables locales a la función.

Es posible definir funciones con el mismo nombre (sobrecarga) siempre y cuando tengandistinta firma o “signature” que se define como “los tipos y cantidad de argumentos”.

Una sugerencia para crear un template es crear una función para un tipo determinado ydebugearla para asegurarse que hace lo que tiene que hacer, y luego anteponer a la definiciónde la función template<class T> y reemplazar el tipo específico por T.

Las funciones con tipos determinados tienen prioridad sobre los templates, lo que hace posiblehacer funciones especializadas que requieran un código especial para determinado tipo deparámetro, y dejar el template para el resto.

Es posible también utilizar sobrecarga de funciones y a la vez templates. Strings separados porwhite spces son automáticamente concatenados por el compilador.

Page 3: c for c Programmers at Curse Ra

bool y wchar_t son tipos nuevos que no existían en C.

C++ tambien tiene el operando sizeof() que devuelve la cantidad de bytes que ocupa undeterminado tipo.

En la librería "limits" de C++ se define el template numeric_limits<T> que permite saber elvalor máximo que se puede almacenar en un tipo. Por ejemplo "numeric_limits<double>::max()"

Se suguiere utilizar los tipos int y double que suelen ser los más eficientes en cadaimplementación.

C++ tiene la posibilidad de definir referencias, identificadores que son como sinónimos de otros.Por ejemplo en la siguientes declaracions int n; int& nn = n; ”n” y “nn” apuntan al mismo lugar,referencian el mismo contenido, pero no son explícitamente punteros. Esta es la base delpasaje de parámetros por referencia.

En la librería “assert” se encuentra definida la funcion void assert(expresion); Si la expresiones falsa la ejecución se para mostrando un error. Para cancelar los assert se debe definir lamarcro NDEBUG.

Module 2: C++: Basics of Generics and ClassesEs posible asignar un valor por “default” a un parámetro, los cuales deben ser colocados al finalde la lista de parámetros, y no es necesario pasar un valor para esos parámetros a no ser quese desee cambiar el valor por “default”.

Si los valores de un parámetro no van a ser modificados dentro de la función, es recomendabledefinirlos como const. El uso de const para definir parámetros sirve para documentar el códigoy para permitirle al compilador realizar los chequeos convenientes.

No se suele definir como const a los parámetros que son llamados por valor.

Es posible definir templates con más de una clase con la siguiente sintaxis template<class T1,class T2> aunque en general no se necesita más que una clase y puede dar lugar a errores,por lo tanto hay que usarlo con cuidado.

En la teoría de grafos se utiliza la letra K para indicar grafos con todos los posibles vínculosentre nodos. Por ejemplo K4 define un grafo de 4 nodos con las 6 relaciones posibles entreellos.

La cantidad de vínculos salientes que tiene un nodo se identifican como grados (degrees). Paratodo grafo completo K<n>, la cantidad de grados de todos los nodos es n­1.

Hay dos estructuras de datos típicas para representar grafos, una es la matriz de conexiones(connectivity matrix) y la otra es la representación de lista de bordes (edge list representation).

Page 4: c for c Programmers at Curse Ra

La matriz de conexiones es generalmente mejor (utiliza menos memoria y resulta en algoritmosmás eficiente) en grafos densos (con muchos grados por nodo), pero la mayoría de losproblemas de la vida real se representan en grafos no densos o escasos (sparse).

Suelen clasificarse los grafos como dirigidos, no dirigidos y también puede indicarse un peso (ocosto) para cada unión entre vértices. En los grafos no dirigidos (undirected graph) los bordes(edges) no tienen orientación, a diferencia de los grafos dirigidos (directed graph) formados porpares ordenados indicando el vértice de origen y el de destino. Los grafos dirigidos puedenpensarse como caminos de una sola mano, mientras que los no dirigidos como rutas de doblemano.

El algoritmo de camino más corto de Dijkstra es para grafos con pesos en cada arista (loscuales no pueden ser negativos) en el cual se define un nodo de origen y otro de destino.

En este algoritmo se mantienen dos conjuntos. Un conjunto cerrado (closed set) con loscaminos más cortos encontrados hasta el momento y otro conjunto abierto (open set) contodos los nodos que se pueden alcanzar.

Se puede encontrar más información en este link de Wikipedia.

Una de las formas de definir tipos es con enum. Por ejemplo la siguientes expresión define eltipo color: “typedef enum color RED, WHITE, GREEN color;”. Si bien se pueden definir comoconstantes independientes, enum tiene la ventaja de agruparlas como si fueran un tipoextendido.

En este caso “enum color RED, WHITE, GREEN” es un tipo y con typedef se define a “color”como un sinónimo de ese tipo. Si no usáramos typedef la siguiente expresión “enum colorRED, WHITE, GREEN color;” define una variable “color” del tipo “enum color RED, WHITE,GREEN”.

Uno de los secretos de una buena programación orientada a objetos es definir tipos queparezcan nativos en el lenguaje.

La sobrecarga de operadores sirve para que los tipos se comporten más naturalmente. Encierta forma es como definir una función, y la sobrecarga es usada depende de la firma deloperador (la cantidad y tipos de parámetros usados).

Uno de los operadores típicamente sobrecargado es “<<” por ejemplo según el siguiente código:“ostream& operator<< (ostream& out, days d) ….; return out; ” donde “operator<<”representaría el equivalente al nombre de la función.

En C podemos definir un punto con la sintaxis “typedef struct point double x, y; point;”. Encambio en C++ podemos definir un punto con la sintaxis “class point public: double x, y; ;” yno necesitamos el uso de typedef ya que el nombre de la clase es a la vez el nombre del tipo.

En C++ struct es prácticamente igual a class donde todos los miembros y métodos son

Page 5: c for c Programmers at Curse Ra

públicos.

Normalmente los datos en bruto (data members) de una clase son privados, y se definenfunciones públicas (methods) para operar sobre esos datos. Generalmente se los llama“accessors methods” y “mutators methods”.

Los métodos “constructors” tienen el mismo nombre que la clase. Pueden tener distinta firmade parámetros, pero cuando no tiene argumentos (es decir que s firma es “void”) se lo llama“default constructor”.

Dentro de la definición de una clase, this es un puntero que referencia a al mismo objeto.

Module 3: C++ and OO; ListsLos métodos constructores sirven para declarar, reservar memoria e inicializar. En lainicialización pueden llevar a cabo una conversión de tipos. Estas etapas se hacenautomáticamente en tipos nativos como en “int i = 3;”. Los métodos también sirven paragarantizar que el objeto se inicializa con valores lógicamente correctos.

En el siguiente ejemplo se utilizan parámetros con valores por default para definir tres posiblesfirmas en el constructor, uno de los cuales es el constructor por default por tener la firma (void).Además se define una “lista de inicialización”: point(x = 0.0, y = 0.0): x(x), y(y) ; Las listas deinicialización son solo posible de usar en constructores.

La diferencia entre los constructores point() x = y = 0; y point():x(0.0),y(0.0); es que en elprimer caso se “asignan valores” a x e y, en cambio en el segundo x e y son “inicializados” convalores. Por ejemplo con la lista de inicialización se pueden inicializar miembros definidos comoconstantes, a los que sería imposible hacer una asignación.

Cuando se declara una variable de una clase se llama un constructor, por ejemplo en ladeclaración point p; se llama al constructor default.

A veces en se definen los parámetros de los contructores con el mismo nombre que losmiembros. En esos casos se tiene que usar el puntero “this”, por ejemplo this­>x = x; paradiferenciarlos. Aunque se sugiere el uso de la lista de inicialización.

La lista de inicialización tiene la forma “:miembro(expresión), …” donde la expresión puede sersimplemente un argumento del constructor.

La expresión int *p = new int(9); reserva memoria para un solo entero y lo inicializa con el valor9. No debe confundirse con char *s = new int[9]; el cual define un array de 9 enteros sininicializar. El primero se libera con delete p; y el segundo con delete [] s;

Cada vez que un objeto utiliza recursos del sistema (como archivos) o reserva memoria con la

Page 6: c for c Programmers at Curse Ra

instrucción new, debe definirse un “miembro destructor” para liberar esos recursos. Solo esposible definir un solo método destructor sin argumentos para cada clase y tiene la sintaxis~point() ; el cual comienza con el caracter “~” y también tiene el mismo nombre que la clase.

Cuando se define un método fuera de la declaración de la clase, se debe utilizar la definición desope con la sintaxis nombre de la clase seguido con “::”, ejemplo point::metodo()

En grafos generados al azar, a pesar de tener una densidad baja, si el grafo tiene más nodos,es mucho más probable de encontrar un camino entre dos nodos.

Durante la clase explica el siguiente código:

// crea en la heap un array 2D de boolsbool** graph;

srand(time(0));graph = new bool*[size];for(int i = 0; i < size; i++)

graph[i] = new bool[size];

const int DENSITY = 0.19; // con DENSITY = 0 se obtiene un grafo desconectado,con DENSITY = 1 se logra un grafo completo.

for(int i = 0; i < size; i++) for(int j = 0; i < size; i++)

if (i == j) graph[i][j] = false; // no loopselse graph[i][j] = graph[j][i] = (prob() < DENSITY);

// algoritmo "breadth first search" o "busqueda en anchura"

bool is_connected(bool *graph[], int size) // utilizarlo para descartar losgrafos desconectados en el ejercicio 2. int old_size, c_size = 0; // c_size es el tamaño del conjunto cerrado. bool* close = new bool[size]; bool* open = new bool[size];

for(int i = 0; i < size; i++)open[i] = close[i] = false;

open[0] = true;

while(c_size < size) for(int i = 0; i < size; i++) old_size = c_size;// add to closeif(open[i] && (close[i] == false))

close[i] = true;c_size++;

// add to open

Page 7: c for c Programmers at Curse Ra

for(int j = 0; j < size; j++) open[j] = open[j] || graph[i][j];

if(c_size == size)return true; // connectedif(old_size == c_size)return false; // unconnected

En C++11 en lugar de 0 está (o la macro NULL como convensión) definido nullptr; lo cualpermite un control más seguros de tipos.

Es común agregar un constructor de copia (“copy constructor”). Si por ejemplo si estuviéramosdefiniendo la clase “list”, la sintaxis del constructor de copia sería list(const list& lst);

Es importante diferenciar la “copia profunda” vs “copia superficial” (o también llamada “copia dereferencia”). El uso de una y otra depende básicamente si lo que se necesita es una copiaindependiente de la original, a fin de modificar a gusto su contenido o no. El primer casorequeriría una copia profunda, la cual es más costosa a nivel de recursos.

En el curso se recomienda usar el programa valgrid para detectar “memory leaks”.

En la “librería de tipos estándar” o STL, un grupo de clases son llamados contenedores, dondela clase “vector” es la más importante. En el curso se recomienda fuertemente usar la clasevector en lugar de array básico de C, con el cual es mucho más facil cometer errores en elmanejo de índices o reserva de memoria.

Otra clase de la STL es “list”, la cual es “doble linked” la cual se puede navegarbidireccionalmente.

El “vector” es más útil cuando se tiene que acceder a cualquier elemento al azar, y “list” es máseficiente cuando se necesita constantemente estar agregando y eliminando elementos.

Árbol de expansión mínima o MST (Minimum Spanning Tree)Un MST es un conjunto de bordes entre vértices que conectan todos los vértices y que no tienebucles. Un bucle puede formarse entre muchos vértices.

Si el grafo tiene tiene 'n' vértices, el árbol tiene 'n­1' bordes.

C++11 incluye clases de enumeración o 'enum classes'. Por ejemplo 'enum class ColorRED,GREEN, BLUE;' y 'enum class TrafficRED, YELLOW, GREEN;' el Color::RED !=Traffic::RED. La sintaxis es “enum class Identifier: Integral_Type enumerator list ;”

Page 8: c for c Programmers at Curse Ra

Hay grafos que no tienen MST, como por ejemplo en el caso que un vértice esté desconectado.Cualquier algoritmo debe tener esta situación en cuenta, por ejemplo devolviendo un númeronegativo.

El algoritmo de Jarnik­Prim comienza con un vértice y repetidamente une los bordes de menorpeso que conectan al árbol (desde cualquier vértice del árbol) con un vértice que no perteneceal árbol (esta última condición garantiza no se formen bucles). A cada paso, el árboll que se vaformando es un MST entre los vértices que se vayan procesando.

Si en un grafo hay forma que desde un vértices se llegue a cualquier otro vértice, entonces hayun ST, y la existencia de un ST, implica que existe un MST.

Podríamos llamar conjunto abierto (open set) a los vértices aún no conectados al árbol yconjunto cerrado (closed set) a los que pertenecen al árbol. La idea del algoritmo es buscar elvértice del conjunto abierto que esté más cerca de cualquiera de los nodos del conjuntocerrado.

El conjunto abierto se puede implementar como una lista con prioridad, de forma de elegirsiempre el vértice que tenga el menor peso de borde.

En el caso de que haya más de un borde con el mismo valor, se puede elegir cualquiera de losdos, y el costo de los MST va a ser el mismo, aunque la topología del árbol en sí sea distinta. Esdecir el MST no es único, pero si el costo mínimo.

El pseudo codigo del algoritmo de Prim se puede encontrar en el siguiente link de wikipedia.

El algoritmo de Kruskal consiste en ordenar todos los bordes posibles de un grafo de menor amayor, e ir tomándolos a o descartándolos cuando conforman un bucle. El pseudo codigo delalgoritmo de Kruskal se puede encontrar en el siguiente link de Wikipedia.

Se define como algoritmo codicioso o voraz (o ‘Greedy Algorithm’ entre los que se encuentranlos algoritmos de Prim y Kruskal) es aquel que, para resolver un determinado problema, sigueuna heurística consistente en elegir la opción óptima en cada paso local con la esperanza dellegar a una solución general óptima. En algunos casos estos algoritmos no encuentran lasolución óptima del problema, pero muchas veces se los emplea, porque por su eficienciaencuentran una solución aceptable.

Uno de los principios de programación orientada a objetos es que los tipos definidos por elusuario, sean indistinguibles de los tipos nativos del idioma.

Agregando explicit a la definición de un constructor (o desde C+11) a un operador deconversión, se impide la conversión implícita de tipos o una inicialización por copia y obliga eluso de static_cast. Por ejemplo daría un error si hacemos a = b; si a fuera un punto y b unentero, aun si existiera un constructor del tipo punto que tuviera un único entero comoparámetro on la sintaxis point::point(int) ;

Page 9: c for c Programmers at Curse Ra

Por el contrario, si hacemos b = a, es imposible definir un constructor de conversión para untipo entero que permita convertir un punto en un entero. C++ permite definir un operador deconversión con la sintaxis point ::operator int() ...; lo que permitiría convertir un punto a unint. Sin embargo no es recomendado a no ser que sea semánticamente natural la conversión detipos (es decir que tenga un sentido claro).

Para elegir la función a ejecutar, el compilador primero busca la función cuya firma cohincidaexactamente con los parámetros usados, si no la encuentra usa las promociones estandard(como de int a double). Luego usa las conversiones de tipo estandard (como por ejemplo de inta short) y como siguiente criterio usa las conversiones definidas por el usuario.

La librería de templates estándar (o STL) se basa en Contenedores, Iteradores y Algoritmos.

Las clases vector, list, set, dict y map son ejemplos de contenedores. vector y list son lo quellamamos contenedores secuenciales (lo que implica que sus elementos se pueden accedercon un índice). Por otro lado, por ejemplo a set, dict y map se los llama contenedoresasociativos y la forma de acceder generalmente es con una clave que nos permite acceder alcontenido.

Los contenedores se pueden expandir a medida que es necesario.

Los iteradores son algo similar a punteros pero generalizados y con funciones estandarizadas.Hay cuatro tipo de iteradores: forward, backward, random y access. Una de las ventajas de lositeradores es que no permiten leer elementos fuera del rango del array, que es el típico error quegenera un error segment fault.

Los algoritmos (como por ejemplo sort o permute) se pueden aplicar a múltiples clases.

Por ejemplo la programación con estilo típico de C: for (int i = 0; i < 100; i++) ...

es reemplazada en el estilo de C++por el uso de iteradores: for (vector<int>::iterator p = v.begin(); p != v.end; p++) ...

a su vez en C++11 se sugiere reemplazar por: for (auto p = v.begin(); p != v.end; p++) ...

En el primer código se accede a cada elemento con v[i], en cambio en la versión con iteradoresdebe usarse '*p'.

La palabra clave auto es usado para declarar el tipo deducido del contexto. auto se puede usaren otros contextos como por ejemplo 'auto i = 3;', aunque es muy útil en situaciones como'auto c = d;' donde d es de un tipo complejo, elimina tener que re­tipear el tipo.

Algunos métodos comunes son: v.size(); // get current size v.resize(int size);

Page 10: c for c Programmers at Curse Ra

Constuctores:

vector<T> v; // secuencia vacía. vector<T> v(n); // crea un vector con n elementos y llama al constructorT() para cada elemento. vector<T> v(n, valor> // Value de tipo T o convertible a T y el valor quese asigna a cada elemento.

Para manipular archivos usar la librería fstream. Por ejemplo ifstream ifp("data", ios::in); creala variable ifp (puntero a un archivo de ingreso de datos) para su lectura. La variable ifp tiene elmétodo eof();

Una nueva funcionalidad de C++11 es el for para rangos. La sintaxis es 'for (declaracion:expresión) ...'. Por ejemplo for (double d: data) sum += d;

El rango es implícito de principio a fin.

Dentro de la expresión de un for se pueden usar un array, un contenedor de secuencia o unstring.

Por ejemplo for (auto &element: data) element = element + 2; // al poner como referencia sepuede cambiar el valor de cada elemento.

Hay 5 categorías de iteradores (de más simple a más complejo):

­ input: Solo lectura, una sola pasada. Permite recorrer los elementos uno por uno y debe tenerel operador++ y el operador* de de­referencia definidos y el operador ­>. Los iteradores tambiéndeben definidos == y !=. Por ejemplo es el requerimiento de la función 'find' un elemento en unalista.

­ output: Solo escritura, una sola pasada

­ forward: Lectura y escritura, multipasada en una sola dirección. Un típico algoritmo que usaeste tipo de iterador es “replace”.

­ bidirectional: Multipasada en ambas direcciones. Este iterador debe implementar el operador­­.Un típico algoritmo que usa este tipo de iterador es “reverse”.

­ random: accede a cualquier elemento, es indexable. Este tipo de operador permite acceder acualquier elemento en un tiempo fijo. Necesita tener definidos los operadores de comparaciónpor mayor, menor e igualdad. Generalmente se implementa con aritmética de direcciones dememoria, como lo sería un array en C, con una dirección base y un offset teniendo en cuenta eltamaño en bytes de cada elemento. Un típico algoritmo que usa este tipo de iterador es “sort”.

Cuando se designa un algoritmo se debe pensar la versión más eficiente del algoritmo que usala versión más simple de iterador.

La librería 'numeric' contiene algoritmos como 'accumulate(input_it_begin, input_it_end,

Page 11: c for c Programmers at Curse Ra

start_value);'. Este algoritmo utiliza un iterador tipo input.

C++ 11 Standard; Containers, Iterators and AlgorithmsLos algoritmos de la STL se pueden clasificar en algoritmos que no cambian la secuencia (Findy Count), los que cambian la secuencia (Copy, Random y Shuggle), los relacionados al ordende los elementos (Sort y Merge) y los algoritmos numéricos (Accumulate, Inner product).

Algunas de las nuevas librerías son:

<regex>: Expresiones regulares<thread>: Programación paralela (multicore, multiprosesador)<unordered_map>: Mapa basado en Hash<array>: Arreglo de longitud fija. Tiene muchas similitudes con vector.<forward_list>: Lista de vínculos simples en una sola dirección.

Por ejemplo el algoritmo replace es un algoritmo que cambia el contenido de un contenedorreemplazando las ocurrencias de un valor “x” por otro “y”. Este algoritmo tiene la siguiente firmay utiliza iteradores del tipo forward:

template<class T>void replace(ForwardIterator first,

ForwardIterator last,const T& x, const T& y);

first se suele definir con “x.begin()” y last cond “x.end()”.

Por ejemplo esta es una función que usa iteredores del tipo forward y reemplaza cada elementode un contenedor por su cuadrado:

template <typename ForwardIterator>void square(ForwardIterator first, ForwardIterator last)

for(; first != last; first++)*first = (*first) * (*first);

Hay un algoritmo en la STL llamado random_shuffle() que mezcla al azar el contenido de uncontenedor.

Una forma interesante de recorrer en un contenedor una determinada cantidad de elementoscon un iterador desde un determinado elemento puede ser:

int i = 0;for (auto p = x.begin(); i < 5; p++)

y[i++] = *p;

Page 12: c for c Programmers at Curse Ra

Con este código copiamos 5 elementos de un contenedor a otro comenzando desde la posiciónde un iterador.

Por ejemplo el algoritmo replace es un algoritmo que cambia el contenido de un contenedorreemplazando las ocurrencias de un valor “x” por otro “y”. Este algoritmo tiene la siguiente firmay utiliza iteradores del tipo forward:

template <class BidirectionalIterator>void reverse(BidirectionalIterator first,

BidirectionalIterator last;

El iterador “x.end()” apunta más allá del final del contenedor. Por lo tanto los algoritmos quenecesitan acceder al último elemento del contenedor comienzan decrementando el puntero.

En la librería <cstddef> se encuentra definido el tipo ptrdiff_t el cual se utiliza para almacenardiferencia entre punteros en aritmética de direcciones.

El diseño de la STL está basado en los principios de eficiencia (es una de las razones de ser deC, “cerca del metal” y sacar el mayor jugo posible al hardware), paramétrico (con el uso detemplates, los tipos son parámetros y permite utilizar el mismo código bajo distintasnecesidades) y ortogonal (cubrir todas las necesidades dentro del alcance de la librería).

Una interfaz típica de un contenedor incluye:­ Constructores (default constructors, copy constructors, move constructors ­ nuevo enC++11)­ Acceso a los elementos­ Inserción de elementos­ “Emplacement” de elementos (nuevo en C++11)­ Borrado de elementos­ Destructures­ Iteradores

Los contenedores también tienen la capacidad de reservar y liberar memoria para los objetosque contienen.

Los contenedores asociativos, como set, multiset, map y multimap asocian un valor a otro yse basan en algoritmos de inserción y búsqueda que trabajan con árboles balanceados lo queminimiza el tiempo de búsqueda, pero necesitan que los elementos se puedan comparar.

Con C++11 se introducen los contenedores asociatios unordered_map y unordered_set quetienen tiempos de búsqueda constante y se basan en tablas “hash”. En general reemplazan lasversiones basadas en árboles balanceados especialmente cuando no se necesita el conceptode orden y en contenedores con muchos elementos.

Los elementos de los contenedores asociativos son pair y por lo tanto el valor clave se obtiene

Page 13: c for c Programmers at Curse Ra

con (*p).first y el valor asociado con (*p).second (siendo “p” un iterador).

Los algoritmos de la librería STL se basan en los contenedores e iteradores. Son eficientes yutilizan el tipo de iterador que facilita el algoritmo más eficiente.

Los algoritmos se pueden catalogar en algoritmos de ordenamiento, algoritmos que nomodifican al contenedor, algoritmos que cambian el contenedor y algoritmos numéricos querealizan cálculos en base a los elementos del contenedor.

El algoritmo stable_sort() garantiza que los elementos con el mismo valor mantienen la mismaposición relativa dentro del contenedor.

La función típica de un algoritmo que no cambia los valores de los elementos de un contenedores la de buscar un elemento y devolver su posición.

En vez de busca por un elemento determinado el algoritmo de búsqueda puede buscar por unpredicado o también llamado "functor" que devuelve True o False si se cumple una condición.

El uso de "functores" aumenta la ortogonalidad de la librería permitiendo su aplicación a muchosmás casos.

Un algoritmo muy util es for_each() el cual ejecuta una función con los elementos de uncontenedor. Si firma es:

template<class InputIter, Class Function>void for_each(InputIter b, InputIter e, Function f);

C++11 introduce las funciones lambda, o funciones sin nombre. for_each() se ve muybeneficiada con las funciones lambda que se definen dentro de la llamada a la función.

En las funciones lambda, se suelen utilizar variables static para retener valores entre múltiplesllamadas a la función. Por ejemplo “[](int &i) static int n = 1, i = n++”.

No es imprescindible definir el tipo que devuelve una función lambda ya que el compilador lopuede deducir de la expresión tras el return. Sin embargo es posible “[](int n)­>intreturn n++”.

copy() es un algoritmo que modifica el contenedor.

El algoritmo accumulate() puede recibir como parámetro un operador binario en el caso esposible usar una funcion lambda como [](T a, T b) ....;

STL también usa objetos funciones, que son clases que tienen definido el operator(). Unejemplo de uso podría ser “sum = accumulate(v1, v1 * LENGTH, minus<int>() );” donde minuses una clase con el operador “()” definido.

Otro concepto que se usa junto con STL son los objetos generadores como muestra elsiguiente código:

Page 14: c for c Programmers at Curse Ra

class gen public:

gen(double x_zero, double increment): x(x_zero), incr(increment) double operator()() x += incr; return x * x;

private:double x, incr;

double integrate(gen g, int n)

vector<double> fx(n);

std::generate(fx.begin(),fx.end(), g);return

(accumulate(fx.begin(), fx.end(), 0.0) / n);

int main()

const int n = 1000;

gen g(0.0, 1.0/n);cout << “integration program x**2” << endl;cout << integrate(g, n) << endl;

El comportamiento del algoritmo generate() de la STL es equivalente a:

template <class ForwardIterator, class Generator>void generate ( ForwardIterator first, ForwardIterator last, Generator gen ) while (first != last) *first = gen(); ++first;

Dentro de la STL hay objetos de funciones predefinidos que se pueden clasificar como objetosaritméticos, de comparación o lógicos.

Los objetos aritméticos son plus, minus, times, divides, modulus y negate que tienen laforma:

template <class T>struct plus<T>

que en este caso suma dos operadores del tipo T.

El algoritmo transform() aplica una función a los elementos de un contenedor y guarda el

Page 15: c for c Programmers at Curse Ra

resultado en otro contenedor.

Es posible definir el rango de destino igual al rango de entrada de forma de realizar la operaciónen el lugar.

template <class InputIterator, class OutputIterator, class UnaryOperator> OutputIterator transform (InputIterator first1, InputIterator last1, OutputIterator result, UnaryOperator op) while (first1 != last1) *result = op(*first1); ++result; ++first1; return result;

La función bind2nd() construye un objeto función unitaria desde un objeto función binaria “op”asociando el segundo parámetro a un valor fijo x como se muestra en el siguiente ejemplo deaplicación

transform( data, data + LENGTH, data, bind2nd( times<int>(), 2)):

La función transform() necesita un objeto función unitaria, pero times<int>() es un objetofunción unitaria. Con el uso de bind2nd() logramos duplicar el valor de los elementos de uncontenedor.

Hex, the game and C++ InheritanceUn juego con información perfecta es uno donde el oponente tiene toda la informacióndisponible.

Una jugada razonable sería hacer el camino lo más largo posible y bloquear al oponente paraque éste lo haga.

La metodología de OOP se basa en la apropiada elección de tipos apropiados para el dominio(por ejemplo en Hex el tablero y los grafos) y definir sus interrelaciones.

La sintaxis en C++ es class classname:(public|protected|private)opt basename .... Laherencia pública es la más importante y se aplica a más del 90% de los casos.

La clase pair en C++11 fue generalizada por tuple.

Si un miembro de una clase se define como protected puede ser utilizable por las clases queheredan de esa clase, para cualquier otro objeto es inaccesible como los miembros definidoscomo privados.

Page 16: c for c Programmers at Curse Ra

El mecanismo de la herencia permite obtener nuevas clases de clases existentes (llamadasclases base).

Tiene como objetivo reutilizar código y las clases obtenidas se desarrollan de la clase baseagregando nuevo código o código alternativo. En generar las clases heredadas agreganinformación.

La jerarquía de tipos relacionados permite compartir código e interfaz.

En C++ es posible definir simple o múltiple herencia.

Las clases permiten definir una clasificación taxonómica, es decir grupos de clases quecomparten un grupo de características.

Entre las clases se pueden definir distintos tipos de relaciones. Por ejemplo un elefante ISAmamífero, y la clase circo HASA elefante.

Una método virtual es un método que es declarado en la clase base y puede ser reemplazado(overridden) en la clase derivada. Este reemplazo se diferencia de la sobrecarga en que puedetener la misma firma. Mediante el puntero this C++ accede a la función adecuada para cadaclase en tiempo de ejecución en forma dinámica. A este mecanismo se lo llama polimorfismopuro.

Para que esto sea posible, el objeto debe tener información de tipo que pueda ser usadadinámicamente.

Para evitar la sobrecarga asociada a las tablas de búsqueda dinámica en tiempo de ejecucióndebe definirse un método como no virtual.

Por ejemplo si definimos class point3d:public point ...; el constructor de la clase point3dpodría ser point3d():point(),z(0.0); el cual llama al constructor de su ancestro.

C++ permite impedir que se pueda heredar de una clase con la palabra clave final. Por ejemploen la definición class point3d final:public point... la clase point3d no se pude utilizar paraderivar una nueva clase.

El concepto de puentes es una estrategia muy usada en Hex y recomienda incorporar comoparte del programa.

Para identificar un nodo (i,j) se puede utilizar la fórmula n = i * lado + j; dónde lado es la longitudde celdas por lado del tablero. De esta forma se puede pasar de la representaciónbidimensional del tablero a una lineal de nodos. Para pasar del número de no=do a larepresentación bidireccional se puede usar la división y el módulo de enteros i = n / lado; j = n% lado; En ambos casos i, j y n comienzan en 0.

Un ejemplo de la clase hexGraph podría ser:

Page 17: c for c Programmers at Curse Ra

clase hexGraphpublic: hexGraph() int count = 0; edgelist.resize(121); for(int i = 0; i < 11; i++) for(int j = 0; j < 11; j++) makeNode(i, j, edgelist[count++]); private: vector<deque<int>> edgelist;

Por ejemplo, para nodos internos las conexiones son:

else elist.push_front(i * 11 + j + 1); elist.push_front(i * 11 + j ­ 1); elist.push_front((i + 1) * 11 + j); elist.push_front((i + 1) * 11 + j + 1); elist.push_front((i ­ 1) * 11 + j); elist.push_front((i ­ 1) * 11 + j ­ 1);

La expresión void print() const; indica que el puntero this no se verá afectado dentro delmiembro print().

Se recomienda utilizar protected en lugar de private para definir miembros de una clase, yaque si no usa esa clase para crear subclases, el encapsulamiento se logra igual, pero deja lapuerta abierta a crear subclases.

Grady Booch escribió varias reglas de buenas prácticas de OOP. Una de ellas indica que esconveniente entender los objetos en el mundo real antes de crear el programa, y muchas vecesel programador debe trabajar con un especialista en el dominio para el cual se aplica elprograma.

Punteros del tipo de la clase base pueden ser utilizados para apuntar objetos de las subclases.

Si la clase grad_student hereda de student, entonces se puede reemplazar el método print() destudent en grad_student con esta sintaxis:

void gad_student::print() student::print(); cout << newstaff;

Es importante destacar el uso de operador “::” para poder llamar al miembro de la clase base.

Page 18: c for c Programmers at Curse Ra

Cuando los métodos no son virtuales, cada llamado a print(), dependiendo del tipo del puntero,imprime la información distinta, limitada a su tipo. Este es el comportamiento por default.

student s(), *ps = &s;grad_student gs(), *pgs;ps­>print(); // student::printps = pgs = &gs;ps­>print(); // student::printpgs­>print(); // grad_student::print

El puntero “ps” puede apuntar a objetos del tipo “student” y “grad_student”, pero el puntero “pgs”solo puede apuntar a objetos del tipo “grad_student”.

Con el uso de virtual, el comportamiento no depende del tipo del puntero (como en el ejemploanterior) sino al tipo del objeto que apunta.

El uso de virtual hace que el compilador no pueda definir de antemano que miembro llamar, sinoque crea una tabla de punteros a funciones que es usada en tiempo de ejecución. En tiempo deejecución el programa verifica a que tipo de objeto está apuntando (información que se guardajunto con el objeto) y ejecuta la función adecuada para ese tipo.

Esto agrega un gasto adicional al llamado de la función, y por eso en C++ no es elcomportamiento por default y requiere el uso de virtual.

Una vez que se utiliza la decoración virtual para definir un miembro, esa característica persiste,y no es necesario utilizar virtual en las subclases.

El siguiente código ejemplifica el comportamiento de virtual:

class B public: int i; virtual void print_i() const cout << i << “inside B” << endl; ;

class D: public B public: void print_i() const cout << i << “inside D” << endl; // vitual;

main() B b, pb = &b; D f;

f.i = 1 + (b.i = 1); pb­>print_i(); // call B::print_i() pb = &f; pb­>print_i(); // call D::print:i()

Page 19: c for c Programmers at Curse Ra

Se puede generar confusiones entre funciones sobrecargadas (overloaded) y las funcionesreemplazadas (overrided), especialmente en casos de conversión automática de tipos.

Es deseable no realizar el siguiente código porque puede dar lugar a errores ocultos

class B public: virtual foo(int); virtual foo(double); // overloading …;

public D: public B public: foo(int); // override …;

main() D d; B b, *pb = d; b.foo(9.5); // B::foo(double) d.foo(9.5); // D::foo(int) pb­>foo(9.5); // B::foo(double)

Solo es posible definir métodos virtuales en aquellos que no son estáticos (es decir que estánasociados a la clase y no a los objetos).

Tampoco pueden ser virtuales los constructores (aunque la funcionalidad se puede codificarcon el patrón fábrica), pero si los destructores. La mayoría de las veces que usamos herenciase definen los destructores como virtuales.

Min-Max and C++11 Classes; Alpha-beta and Polish

template <class T, int n>class my_container public: my_container() a = new T[n]; ~my_container() delete[] a; private: T* a;;

Una principal diferencia al incluir el tamaño del array en la descripción del Template en lugar deen la firma del constructor es que una clase de 5 elementos es de un tipo distinto a una clase de10 elementos.

Page 20: c for c Programmers at Curse Ra

explicit my_container(T *b):my_container() for(int i = 0; i < n; i++) a[i] = b[i];

Este constructor tiene un solo argumento, y generalmente los constructores con un soloargumento se utilizan para conversión de tipo. A su vez explicit evita las conversionesautomáticas.

La sintaxis :my_containter() es similar a la sintaxis de un constructor de una subclasellamando a la clase base, pero en realidad expresa una nueva característica llamada delegaciónde construcción (“Delegate construction”). Esta nueva característica facilita la utilización decódigo existente.

El siguiente es un típico constructor de copia que también usa la delegación de construcción.

my_container(const my_conteiner &b):my_container() for(int i = 0; i < n; i++) // clona la memoria. a[i] = b.a[i];

Los constructores típicos de una clase son el default, que no tiene parámetros; los deconversión, con un solo parámetro y el de copia.

En C++11 se agrega el constructor “move”

my_container(my_conteiner &&b) noexcept a = b.a; // referencial copy o shallow copy. b.a = nullptr; //

En el ejemplo anterior se agrega noexcept, que también se agrega en C++11, garantizando quedentro del código el programador garantiza que no se van a generar excepciones.

&& hace referencia a que es un constructor “move”.

El constructor “move” es para el caso que la variable se utiliza en el lado derecho de unaexpresión. El puntero original es asociado a NULL, por lo tanto no se puede utilizar.

Algunos ejemplos de declaraciones:

int& a = b; // a is an lvalue reference.int* ptr_a = &b; // ptr_a is address of b.

int&& b; // new declaration rvalue reference

Page 21: c for c Programmers at Curse Ra

Una referencia a un rvalue asocia a un objeto temporal, que típicamente no se va a volver ausar.

Cuando se crea un constructor “move” también normalmente se sobrecarga el operador =

my_container& operator=(my_conteiner &&b) noexcept a = b.a; b.a = nullptr; return *this; // regresando el puntero al objeto el operador se puede // encadenar.

Nuevamente, en este caso, el rvalue deja de ser válido.

El nuevo concepto “move” es utilizado cuando se trabaja con conjuntos de datos, para valoressimples como int o double, no tiene sentido.

Un tipo uso del nuevo concepto “move” es en la función swap()

void swap(my_conteiner &b) my_container temp = move(b); // b deja de ser un puntero válido. b = move(*this); *this = move(temp);

El uso de del operador move() luego del “=” indica que se desea utilizar el operador= con lafirma de && para mover y no copiar.

La definicion de move es std:move() static_cast<T&&>(t);

Es conveniente para ejercitar agregar el operador[](index) a la clase y implementar la lógica deiteradores.

Vislumbrar el futuro (lookahead) es muy usado en AI de juegos. A veces se puede llegar al finaldel juego, en otras hasta llegar a una posición prometedora.

Esta visión a futuro se puede implementar con el algoritmo Minmax. La idea del algoritmo eselegir la jugada que haga menos posible que el oponente minimice nuestro puntaje con supróxima jugada, y así en adelante. Se aplica a juegos con información perfecta y Minimax es unalgoritmo recursivo.

Este algoritmo se debe llamar cada vez que la computadora debe hacer una jugada. Acontinuación un ejemplo:

Page 22: c for c Programmers at Curse Ra

[6] MAX / | \[1] [6] [3] MIN

/ \ | / | \ [5] 1 [6] 7 [3] [9] MAX / \ / | | | 2 5 6 1 3 9 MIN

En la jerga se define “Ply” a cada paso en la profundidad total del árbol y “Branching rate” a lacantidad de nodos que cuelgan de cada nodo.

Para recortar el árbol es bueno definir un algoritmo que genere lo que se llama “Plausible Move”,que son las jugadas más prometedoras.

En Hex este algoritmo podría analizar:­ Si se pueden generar jugadas ganadoras.­ Si se puede bloquear o prevenir que el oponente gane.­ Si es posible extender el camino más largo.­ Si es posible bloquear el camino más largo del oponente.­ Si es posible extender o hacer un puente para un camino.­ Si es posible tomar una de las casillas centrales­ Si se puede mover hacia una columna(o fila) aún no ocupada

Toda algoritmo de IA requiere la posibilidad de evaluar una posición estática para comparar yelegir una jugada en lugar de otra. En Hex, quien está más cerca de hacer un camino completode extremo a extremo, es una forma rápida de realizar esta evaluación.

Alfa­Beta es una evolución de Minmax. El algoritmo “poda” las ramas que no influyen en ladecisión del algoritmos Minmax.

La poda alfa­beta toma dicho nombre de la utilización de dos parámetros que describen loslímites sobre los valores hacia atrás que aparecen a lo largo de cada camino.

α es el valor de la mejor opción hasta el momento a lo largo del camino para MAX, estoimplicará por lo tanto la elección del valor más alto. α se suele inicializar en ­infinito.

β es el valor de la mejor opción hasta el momento a lo largo del camino para MIN, estoimplicará por lo tanto la elección del valor más bajo. β se suele inicializar en +infinito.

Esta búsqueda alfa­beta va actualizando el valor de los parámetros según se recorre el árbol. Elmétodo realizará la poda de las ramas restantes cuando el valor actual que se está examinandosea peor que el valor actual de α o β para MAX o MIN, respectivamente.

[5] MAX

Page 23: c for c Programmers at Curse Ra

/ | \ [5] [3*] [4*] MIN / | / | \ | \

[5] [7*] [8] [3] [*] [4] [*] MAX / | / | \ / | | | \ | \ / | \ 4 5 7 3 8 8 4 3 9 5 3 4 5 4 7 * * * * * * *

Una vez que encuentra el maximizador encuentra el 7*, sabe que al minimizador de ordensuperior le deja de interesar toda la rama, porque aunque encuentre un valor menor él mismo (elmaximizador) lo va a descartar.

Lo mismo pasa cuando el minimizador encuentra el 3*, sabe que al maximizador de ordensuperior le deja de interesar toda la rama, porque aunque encuentre un valor mayor él mismo (elminimizador) lo va a descartar.

La ejecución de una expresión matemática se la puede ver como un árbol, donde las hojas sonlos valores y los nodos internos las operaciones matemáticas. Se puede expresar conparéntesis o en notación polaca inversa.

El recolector de basura por referencia es un algoritmo que lleva la cuenta de cuantos punteroshacen referencia a un objeto, y no lo borran hasta que esa cuenta llegue a cero.

class Node friend class Tree; friend ostream& operator<<(ostream&, const Tree&); int use; // reference count ­ GCprotected: Node() use = 1; virtual void print(ostream&) = 0; // pure virtual virtual ~Node() // note virtual desctructor virtual void print(ostream& o) = 0; // pure virtual virtual int eval() = 0; // pure virtual;

class Tree friend class Node; // okay ­ between cousin friend ostream& operator<<(ostream&, const Tree&); Node *p; // polymorphic hierarchypublic: Tree(int); // constant Tree(char); // variable Tree(char*, Tree); // unary operator Tree(char*, Tree, Tree); // binary operator Tree(const Tree& t) p = t.p; ++p­>use; // referential copy constructor ~Tree() if( ­­p­>use == 0) delete p; void operator=(const Tree& t); int eval() return p­>eval(); ;

ostream& operator<<(ostream& o, const Tree& t)

Page 24: c for c Programmers at Curse Ra

t.p­>print(o); return(o);

class LeftNode: public Node friend class Tree; virtual void print(ostream& o) = 0; // pure virtual virtual int eval() = 0; // pure virtual

Al tener miembros puramente virtuales, no pueden crear instancias de la case Node, pero sipuede haber punteros a Nodos. A estas clases se las llama clases abstractas.

Al ser Node.eval() una función virtual, cuando en Tree se ejecuta p­>eval() se ejecuta la funciónadecuada para cada subclase. Lo mismo pasa con print();

La idea del colector de basura se basa que cada vez que se asigna la instancia a una variablese hace una copia de referencia y se incrementa la variable use.

Como en el caso de LeftNode, una clase abstracta puede heredar de otra clase abstracta. Alfinal debe definirse una clase concreta derivada de la clase abstracta como las siguientes:

class IntNode: public LeftNode friend class Tree; int n; virtual void print(ostream& o) o << n; IntNode(int k): n(k) public: int eval() return n; ;

class IdNode: public LeftNode friend class Tree; char name; virtual void print(ostream& o) o << name; IdNode(char id): name(id) public: int eval() return valtab[name]; ;

El primer ejemplo es para una constante, y el segundo para una variable representada por uncaracter y almacenada en un array de int.

class UnaryNode: public Node friend class Tree; char* op; Tree opnd; UnaryNode(char *a, Tree b): op(a), opnd(b) void print(ostream& o) o << “(“ << op << opnd << “)”; public: int eval();

Page 25: c for c Programmers at Curse Ra

;

int UnaryNode::eval() switch(op[0] case ‘­’: return(­opnd.eval()); case ‘+’: return(+opnd.eval()); default: err << “no operand\n” << endl; return 0;

class BinaryNode: public Node friend class Tree; char* op; Tree left; Tree right; BinaryNode(char *a, Tree b, Tree c): op(a), left(b), right(c) void print(ostream& o) o << “(“ << left << op << right << “)”; public: int eval();;

int BinaryNode::eval() switch(op[0] case ‘­’: return(left.eval() ­ right.eval()); case ‘+’: return(left.eval() + right.eval()); case ‘*’: return(left.eval() * right.eval()); default: err << “no operand\n” << endl; return 0;

Con esta forma de programar se logra código fácilmente extensible.

A continuación se muestran los constructores de la clase Tree:

Tree::Tree(int n) p = new IntNode(n);

Tree::Tree(char id) p = new IdNode(id);

Tree::Tree(char* op, Tree t) p = new UnaryNode(op, t);

Tree::Tree(char* op, Tree left, Tree right) p = new BinaryNode(op, left, right);

Page 26: c for c Programmers at Curse Ra

Monte Carlo Evaluation; the C++11 StandardIncluyendo la librería <cassert> se dispone de la función void assert(int expresion); Si laexpresión es cero imprime un error y aborta el programa. Este enfoque produce una sobrecargade ejecución, por lo tanto se habilita solo la macro NDEBUG no está definida (NDEBUG significaNo Debug).

Ira propone el uso intensivo de assert() ya que además de verificar el programa complementa ladocumentación indicando que se espera en determinados puntos del programa.

Las excepciones suele ser utilizadas con recursos limitados, o errores en I/O. No debe usarsecomo una forma de lograr un salto tipo goto. En lo posible acotar a estos casos el uso deexcepciones.

Los assert estáticos pueden ser verificados por el compilador y es un concepto nuevo enC++11 (que ya venía de la librería Boost). La sintaxis es static_assert(bool_contexpr, string);El siguiente ejemplo muestra el concepto:

#include <type_traits>template <class T> void swap(T& a, T& b) static_assert(std::is_copy_constructible<T>::value, “Swap requierescopying”); auto c = b; // expectation of copy b = c a = c;

La librería <type_traits> permite definir reglas de los tipos que se pueden usan en un template,que suelen ser fuente de errores difíciles de encontrar.

throw es la instrucción que lanza una excepción, y acepta cualquier tipo de valor, aunquegeneralmente se utiliza un texto.

#include <iostring>using namespace std;main() try cout << “test” << endl; throw “Abort”; catch(...) cout << “Dont abort” << endl;

La sintaxis de catch es catch(some type). El valor es “...” es especial y significa que cualquierexcepción será capturada.

Page 27: c for c Programmers at Curse Ra

El tipo del valor usado en throw debe coincidir con el tipo capturado.

La STL define en <exceptions> las excepciones estándar que son usadas en la librería. Lostipos están organizados en una jerarquía de clases que los agrupa y relaciona.

Es posible definir nuevas exceptions con la siguiente extructura:

#include <exceptions>using namespace std;

class myexception: public exception virtual const char* what() const throw() return “My exception happened”; myex;

int main() try throw myex; catch(exception& e) cout << e.what() << endl; return 0;

En este caso, en catch() en lugar de “exception” podría haberse especializado con“myexception”.

Para la multitarea C++11 incluye las librerías <thread>, <mutex>, <future>, <atomic> y<condition_variable>.

Simple ejemplo de multithread:

#include <iostream>#include <sting>#include <thread>

void message1() cout << “Write a Message!\n”

int main() thread foo( []() // declare as a thread a lambda expresion cout << “Hello World\n”; ); thread thr1(message1); thr1.join(); foo.join();

Page 28: c for c Programmers at Curse Ra

Para compilar en gcc se debe usar la opción “­pthread”.

Es importante hacer verificaciones que el la sobrecarga del uso de thread justifique la solución yresulte en un programa más eficiente.

La librería <tuple> define el template tuple<lista de tipos> que permite asociar una pequeñacantidad de elementos de cualquier tipo.

Por ejemplo, si definimos la variable p como tuple<int, double, int>, para asociar un nuevotuple a la variable podríamos usar el código p = make_tuple(1, 1.5, 2); y para obtener losvalores independientes se debe usar get<0>(p); get<1>(p); y get<2>(p);

Tuple ayuda a la programación basada en genéricos.

A continuación un resumen de distintas funcionalidades de C++11 (las nuevas están resaltadasen negrita).

#include <iostream>#include <string>

using namespace std;

class simple public: simple() = default; // compiler generated constructor simple(double x) = delete; // suppressed conversion constructor // impide que esa firma sea válida simple(int x, int y): p(x), q(y) simple(int x): simple(x, 1) // delegate constructor simple(const simple& x): simple(x.p, x.p) // copy constructor simple(simple&& x): simple(x.p, x.p) x.p = x.p = 0; // move semanticsprivate: int p = 0, q = 1; // default initializer can be overridden;

class rational: public simple public using simple::simple; // implicitly declares constructors but use base. // importa los constructores de la clase base.

Los patrones de diseño deben tener un nombre, resolver un problema y su utilización tieneconsecuencias.

Hay patrones de creación (ej Factory Method y Singleton), patrones estructurales (ej Façade yProxy).

El patrón “Factory Method” se utiliza en los casos que el código sabe acerca de las clases

Page 29: c for c Programmers at Curse Ra

abstractas base pero no sabe nada sobre las subclases concretas. La creación de lassubclases se delega al tiempo de ejecución.

En el patrón participan las clases abstractas (que definen la categoría de los objetos) y losobjetos concretos.

El creador llama a métodos para la creación y el creador concreto sobreescribe el “factorymethod” para crear el objeto concreto.

Sigue un ejemplo:

class Creator public: virtual Object* create(ObjectType) = 0;;

Object* Creator::create(ObjectType id) if(id == obj1) return new Obj_1; if(id == obj2) return new Obj_2; … else return 0; // error ­ null object

Cualquier objeto concreto que se cree debe poder ser apuntado por un puntero de la case base.

Otro patrón útil es el Adaptador, cuyo propósito es convertir la interfaz de una clase en la deotra. La motivación es para reutilizar código.

Por ejemplo en STL la clase stack es un adaptador de vector.