martes, 23 de septiembre de 2014

CSS: Bordes redondeados CSS

Buenas a todos!
Para tener divs con los bordes redondeados compatibles con todos los navegadores la clase css contendrá los siguientes atributos:

-webkit-border-radius: 10px;
-moz-border-radius: 10px;
border-radius: 10px;

Div con los bordes redondeados!

10px lógicamente hace referencia al radio.

Un saludete!

CSS: Fondo transparente con CSS

Buenas a todos!

Esta es una solución cross browser, para tener fondos transparentes en CSS.
El color lo indicamos con los valores rgb, el último parámetro de rgba indica el porcentaje de opacidad.

div.bg_opacity{
    background: rgb(0, 0, 0) transparent;
    background: rgba(0, 0, 0, 0.6);
    filter:progid:DXImageTransform.Microsoft.gradient(startColorstr=#99000000, endColorstr=#99000000);
    -ms-filter: "progid:DXImageTransform.Microsoft.gradient(startColorstr=#99000000, endColorstr=#99000000)";
}

Un saludete!

miércoles, 17 de septiembre de 2014

Apuntes de C++: Sobrecarga de funciones, constructores de copias y argumentos implícitos

Buenas a todos!
El siguiente post, continua con los apuntes sobre C++, esta vez resumo un poco el tema de la sobrecarga de funciones contructoras, los constructores de copias y los argumentos implícitos. Finalizo con algo curioso, obtener punteros que apunten a funciones sobrecargadas.

Las funciones constructoras de clase se pueden sobrecargar, pero las funciones destructoras no.


class Persona{
    int x;
public:
    Persona(){ x=0; }       //Constructor sin inicializador
    Persona(int n){ x=n; }  //Constructor con inicializador
};

En el código anterior tenemos la función constructora sobrecargada.
Sin la versión de constructor sin inicializador, no podría inicializarse un array dinámico, y se generaría un error en tiempo de compilación.

Persona *persona;
persona = new Persona[20];

Con el constructor sin inicializador el array dinámico persona, contiene 20 objetos de tipo persona, con x = 0.

Cuando se pasa un objeto a una función o esta lo devuelve pueden presentarse dificultades, ya que automáticamente se generan copias del objeto.

Al pasar un objeto a una función se realiza una copia bit a bit de ese objeto. Hay cosas que no deseamos que sean idénticas en la copia, como por ejemplo si el objeto original apunta a la memoria asignada por un puntero, la copia apuntará a la misma dirección de memoria, y la copia podría alterar la memoria original.

Cuando una función devuelve un objeto, normalmente el compilador generará un objeto temporal que mantiene una copia del valor devuelto por la función. Esta acción es automática y no controlable.
Este objeto desaparece al devolver su valor y al desaparecer llama a su función destructora.

Para evitar estos problemas se puede definir un "constructor de copias", un tipo especial de función constructora sobrecargada.

Situaciones en las que se aplica un constructor de copias

Los constructores de copias sólo se aplican a situaciones de inicialización, no de asignación.
Hay tres situaciones de inicialización típicas:
- Se emplea un objeto para inicializar otro en una declaración.
myclass x = y;
- Se pasa un objeto como parámetro a una función.
funcionX(y);
- Se crea un objeto temporal al devolver un objeto por una función.
y = funcionX();

class Persona{
    int x;
public:
    Persona(){ x=0; }       //Constructor sin inicializador
    Persona(int n){ x=n; }  //Constructor con inicializador
    Persona(const Persona &obj){…}//Constructor de copias
};

Argumentos implícitos

Usar argumentos implícitos es una forma abreviada de sobrecarga de funciones.
void f(int a = 0, int b = 0);
La función anterior emplea como argumentos implícitos los parámetros a y b.
Este tipo de declaración permite llamar a la función f de las siguientes formas:

f();    //a=0, b=0
f(5);   //a=5, b=0
f(5,2); //a=5, b=2

No hay forma de dar valor por defecto a 'a' y especificar 'b'.
Los argumentos implícitos deben ser constantes o variables globales.

Búsqueda de la dirección de una función sobrecargada

Igual que en C es posible asignar la dirección de una función a un puntero y acceder a la misma a través del puntero:

p = zap;  //donde tenemos zap();

Para obtener la dirección de una función sobrecargada, es el modo de declarar el puntero el que determina la dirección de la función sobrecargada.

void space(int count);
void space(int count, char ch);
main(){
    void (*fp1)(int);   //puntero a una función void con un parámetro int
    void (*fp2)(int)(char);
    
    fp1 = space;    //void space(int count);
    fp2 = space;    //void space(int count, char ch);
    
    fp1(22);
    fp2(30, 'x');
}


En el siguiente post, continuaré con la sobrecarga de operadores.
Un saludo a todos!


domingo, 7 de septiembre de 2014

Apuntes de C++ : Arrays y punteros

Continuando con los resúmenes de mis apuntes del libro "C++ guia de autoenseñanza" de Herbert Schildt, en este post trataré los Arrays, los punteros y las referencias.

Cómo crear un array

int lista[20];
Esta línea crea un array de 20 elementos de tipo entero.

Para crear un array de objetos de una clase concreta, se emplea la misma sintaxis:

class Nave_espacial{
    int r;
    int g;
    int b;
public:
    Nave_espacial(int r, int g, int b);
    void setColor(int r, int g, int b);
};

void Nave_espacial::setColor(int r, int g, int b){...}

main(){
    Nave_espacial naves[20];
    naves[0].setColor(255, 0 , 0);  
}

Creamos un array para 20 objetos de tipo Nave_espacial y modificamos el color de la primera nave de la lista.

Nave_espacial naves[2] = { Nave_espacial(1,2,3) ,  Nave_espacial(4,5,6) };

Este tipo de inicialización del array, permite llamar a las funciones constructoras de cada uno de los objetos del array pasándoles los parámetros que sean necesarios.

Punteros a objetos


class Muestra{
     int id, valor;
public:
     Muestra(int _id, int _valor){ id = _id; valor = _valor; }
     int getId(){ return id; }
     int getValor(){ return valor; }
};

main(){
     int i;
     Muestra muestras[4] = {
          Muestra(0, 20),
          Muestra(1, 23),
          Muestra(2, 21),
          Muestra(3, 23)
     };
     Muestra *m;
     m = muestras;

     for(i=0; i<4; i++){
          cout << m->getValor() << " , ";
          m++; 
     }
}

Creamos un puntero m para apuntar a objetos de la clase Muestra, y cogemos la dirección del array.
Cuando se incrementa el puntero, este apunta al próximo objeto del array.
Para emplear las funciones miembro de los objetos se emplea el operador ->.

Uso de new y delete

En C la asignación de memoria dinámica se realizaba mediante malloc() y la memoria asignada se liberaba utilizando free(). Estas funciones estándar siguen siendo válidas en C++.
C++ proporciona un método más seguro para asignar memoria y liberar memoria, new y delete.

Muestra *m;
m = new Muestra(2, 40);
delete m;

Creamos un puntero para objetos de una clase en concreto.
new nos devuelve un puntero a la memoria asignada dinámicamente, que es lo suficientemente grande como para contener el objeto de la clase.
delete devuelve la memoria cuando ya no se necesita.

Si no hay memoria disponible para asignar, new devuelve un puntero nulo.
Solamente debe llamarse delete con punteros obtenidos previamente mediante new. En caso contrario, se bloquea el sistema de asignación, interrumpiéndose el programa.

new asigna automáticamente la memoria necesaria para albergar un objeto del tipo especificado, así que no es necesario emplear sizeof para calcular el número de bytes requeridos.
Tanto new como delete pueden sobrecargarse permitiendo que el programador cree a su medida su propio sistema de asignación de memoria.

m = new Muestra[10];

El puntero m apuntará a la primera posición de un array de 10 objetos de clase Muestra.
Para eliminar un array asignado dinámicamente, se emplea la siguiente instrucción:

delete []m;

Así el compilador llama a la función destructora para cada elemento del array, y m no se libera en múltiples ocasiones, se libera sólo una vez.

Referencias: pasar parámetros por referencia a una función

K = 10;
void sumarK(int *n);

void sumarK(int *n){
    *n = *n + K;
}

main(){
    int i = 5;
    sumarK(&i);
    cout << "Valor de i = " << i;
}

Por defecto, C++ pasa los parámetros por valor, lo que significa que realiza una copia de los mismos y que si alteramos su valor dentro de las funciones, la variable original que se ha pasado a la función no se ve afectada.
En este ejemplo la función sumarK(int *n) recibe como parámetro un puntero a un entero, lo que significa que cuando realizamos la llamada debemos pasar como parámetro la dirección de la variable entera, para coger dicha dirección o referencia, empleamos el operador &i.

Al hacer esto la función f trabaja directamente con la variable original, ya que altera el valor contenido en la dirección apuntada por el puntero.

En C la definición de paso por referencia sólo puede hacerse de la anterior forma, pero en C++ puede automatizarse para simplificarse el paso por referencia:


K = 10;
void sumarK(int &n);

void sumarK(int &n){
    n = n + K;
}

main(){
    int i = 5;
    sumarK(i);
    cout << "Valor de i = " << i;
}

Simplemente es necesario añadir el operador de dirección al declarar la función, &n.
Hecho esto el compilador ya sabe que realmente cuando se haga la llamada sumarK(i) lo que está pasando como parámetro es la dirección de la variable i.
Y del mismo modo tampoco hace falta indicar dentro de la implementación de la función, que n es un puntero.

Importante: no se pueden modificar las direcciones a las que apunta una referencia.

Pasar parámetros por referencia es la mejor forma de evitar los problemas asociados con la copia de argumentos, que al llamar a sus funciones destructoras pueden perjudicar partes del programa.

Una referencia no es un puntero. Cuando se pasa un objeto por referencia su operador de acceso a atributos y funciones miembro, es el punto "." y no la flecha "->".

Devolución de referencias

Una función puede devolver una referencia. Y podemos asignar directamente valores a la dirección de dicha referencia, es decir, una función puede estar en el lado izquierdo de una asignación.
El siguiente ejemplo es una locura, para los que nunca han trabajado con punteros o están muy acostumbrados a lenguajes como Java.
Vamos a crear una clase Array, para almacenar arrays de enteros.
Y en la función main vamos a hacer que las posiciones 2 y 3 del array contengan los valores 4 y 30:


class Array(){
     int size;
     int *set;
public:
     Array(int _size);
     int &put(int i);
     int get(int i);
};

Array::Array(int _size){
     set = new int[_size];
     size = _size;
}

int &Array::put(int i){
     if(!(i<0 || i>=size)) return set[i]; //devuelve una referencia a set[i]
}

int Array::get(int i){
     if(!(i<0 || i>=size)) return set[i]; //devuelve el entero contenido en set[i]
}

main(){
     Array a(20);
     a.put(2) = 4;
     a.put(3) = 30;
}     

Para que una función devuelva una referencia debemos poner el operador & delante del nombre de función.
Una vez hemos indicado esto automáticamente el compilador sabe que debe devolver la dirección de set[i] y no el valor que contiene.

Una referencia no puede ser referenciada.
No pueden crearse arrays de referencias.
Las referencias son similares a los punteros, pero no son punteros.

Hasta aquí la segunda parte de los apuntes de C++.
Espero que estos resúmenes os sirvan de "referencia" y os sean de utilidad!
Un saludo a todos!!

sábado, 6 de septiembre de 2014

Apuntes de C++

El siguiente post, no es un tutorial en sí. Son fragmentos a modo de síntesis de mis apuntes escritos durante la lectura del libro "C++ guia de autoenseñanza" de Herbert Schildt. Un libro lleno de ejemplos y perfecto para empezar de cero o volver a recordar C++.


Sobrecarga de funciones

En C++ varias funciones pueden tener el mismo nombre mientras el tipo o el número de sus argumentos sea distinto. Cuando ocurre esto, se dice que son funciones sobrecargadas.


Funciones constructoras en C++
Las funciones contructoras de una clase en C++ tienen el mismo nombre que la clase y no incluyen ningún tipo de retorno.

class Sprite{
public:
int x;
int y;
Sprite();
Sprite(int x, int y);
~Sprite();
};

Sprite::Sprite(){
x = 0;
y = 0;
}

Sprite::Sprite(int _x, int _y){
x = _x;
y = _y;
}

Sprite::~Sprite(){
cout << "Destruyendo el sprite \n";
}

int main(int argc, char *argv[]){
Sprite sp;
cout << "Position x " << sp.x;
}

Es importante ver que a diferencia de otros lenguajes como java o javascript, en C++ una sentencia de declaración de variable es una sentencia de acción, que desencadena que sea llamado el constructor sin parámetros.
También C++ permite que se ejecute una función cuando el objeto va a ser destruido, son las llamadas funciones destructoras, y tienen el mismo nombre que la clase pero vienen precedidas por el símbolo ~. Las funciones destructoras no pueden tener parámetros.
Los objetos locales se destruyen al salir de su ámbito y los objetos globales se destruyen al finalizar el programa.

No se puede obtener la dirección ni de las funciones constructoras ni de las destructoras.

Herencia en C++
En este ejemplo tenemos una clase B que hereda de una clase A:

class A{
int x;
public:
void setX(int _x);
int getX();
};

class B : public A{
int y;
public:
void setY(int _y);
int getY();
};

La declaración de la clase B indica al compilador que B heredará de la clase A, y tendrá públicos los mismos elementos que se hayan definido como públicos en la clase A, pero que todos los elementos de la clase base A permanecerán privados para B, de forma que no serán accesibles directamente.
El hecho de que una clase derivada no tenga acceso directo a las propiedades privadas de su clase base es para mantener la encapsulación.

La sintaxis parar realizar herencia es la siguiente:
class nombre_clase_derivada : tipo_acceso nombre_clase_base{...};

El tipo de acceso puede ser public, private o protected.

Punteros a objeto
A diferencia de lenguajes como Java en el que siempre que pasamos un objeto como parámetro de una función, este es pasado por referencia. Lo que significa que realmente estamos pasando la dirección de ese objeto y que todas las modificaciones que hagamos dentro de la función afectarán al objeto en sí. En C++ si queremos modificar un objeto dentro de una función, debemos especificar la dirección que hace referencia a dicho objeto, esto es lo que llamamos un puntero a un objeto.
Un puntero a objeto es una variable que contiene la dirección de memoria donde está contenido el objeto.

class Televisor{
int pulgadas;
public:
Televisor(int _pulgadas);
int getPulgadas();
};
Televisor::Televisor(int _pulgadas){
pulgadas = _pulgadas;                      
}
int Televisor::getPulgadas(){
return pulgadas;
}

int main(int argc, char *argv[]){
    Televisor tele(32);
    Televisor *t;
    t = &tele;
    cout << "Pulgadas " << t->getPulgadas();
    return 0;
}

"Televisor *t" define un puntero para apuntar a objetos de la clase Televisor.
"t = &tele" asigna la dirección del objeto tele al puntero t.
"t->getPulgadas()" ejecuta la función miembro del objeto tele.

Clases, estructuras y uniones

Las clases y las estructuras tienen prácticamente las mismas capacidades.
En C++ las estructuras pueden incluir funciones miembro, incluyendo funciones constructoras y destructoras.
La única diferencia es que por defecto los miembros de una clase son privados y los miembros de una estructura son públicos.

struct nombre_estructura{
...miembros públicos...
private:
...miembros privados...
}lista_objetos;

Tanto struct como class crean nuevos tipos de clase.
Al poseer las mismas capacidades, es una cuestión de preferencias decidir emplear struct o class. Pero lo más común es emplear struct para objetos que no tienen funciones miembro.

Las uniones en C++ también definen tipos de clase y también pueden contener funciones miembro.
Las uniones son como estructuras, por defecto tienen todos los miembros públicos hasta que se usa el especificador private. Su característica principal es que todos los miembros comparten la misma posición de memoria (como en C).
La capacidad de las uniones para enlazar código y datos permite crear tipos de clases en los que todos los datos usan una posición compartida. Esto no puede hacerse usando una clase.
Las uniones no pueden heredar de otra clase ni ser clases base. Tampoco pueden tener ningún miembro static, ni contener objetos que tengan un constructor o destructor. Pero las uniones sí pueden tener constructor y destructor.

Funciones insertadas

C++ permite definir funciones que realmente no son llamadas sinó que son insertadas en el código en el momento de la llamada. Como no tienen asociado el mecanismo de llamada y vuelta de la función, pueden ejecutarse más rápidamente que las funciones normales.
La desventaja es que si son demasiado largas y se las llama muy a menudo, el programa aumentará su longitud.
Una función inline se debe definir antes de ser llamada.

inline tipo_retorno nombre_funcion(parámetros){...}

inline es una solicitud no una obligación para el compilador. Todo depende de las restricciones del compilador en concreto, si no la hace insertada, la compilará como una función normal.

Inserción automática
En la declaración de una función miembro se puede incluir su definición si es suficientemente corta. Esto provoca que la función se convierta automáticamente en una función insertada, si es posible. La palabra clave inline, en este caso no es necesaria.

Asignación de objetos
Cuando asignamos un objeto a otro, se hace una copia a nivel de bits de todos los miembros. Los contenidos de los datos del objeto asignado se copian en los miembros equivalentes.
La asignación hace que los dos objetos sean idénticos, pero están realmente separados.

a = b;

Copia los valores de los datos miembros de b en el objeto a. Si posteriormente modificamos a o b, son completamente independientes.

Paso de objetos a funciones

Por omisión todos los objetos se pasan por valor a una función. Esto significa que se hace una copia idéntica del objeto y es la copia la que es usada por la función. Los cambios que se hagan dentro de la función en el objeto no afectan al objeto pasado como parámetro.
Cuando se hace una copia de un objeto para usarla en una llamada a función, no se llama a la función constructora. Sin embargo, cuando la función finaliza y la copia se destruye, se llama a la función destructora. Ya que los objetos pueden haber asignado memoria que debe ser liberada. Esto puede ser fuente de problemas. Ya que, si un objeto pasado como parámetro asigna memoria dinámica y al acabar la función se llama a su función destructora y esa memoria se libera, el objeto original quedará dañado y sin uso efectivo.
Hay que asegurarse de que las funciones destructoras de las copias de objetos pasados por valor, no provoquen efectos laterales que inutilicen los objetos originales.

Para evitar esto es mejor pasar la dirección de los objetos y no los objetos en sí. Al pasar una dirección, no se crea un nuevo objeto y no se llama a ningún destructor cuando se sale de la función.

Objetos devueltos por funciones

Cuando un objeto es devuelto por una función, se crea automáticamente un objeto temporal que guarda el valor devuelto. Después de devolver el valor, este objeto se destruye. La destrucción de este objeto temporal puede causar efectos laterales inesperados en algunas situaciones.
Si el objeto devuelto por una función tiene una función destructora que libera dinámicamente memoria asignada, esa memoria puede quedar liberada incluso aunque el objeto al que se asigna el valor devuelto lo siga utilizando.
Lo principal es entender que cuando un objeto es devuelto desde una función, el objeto temporal usado para hacer efectivo el retorno habrá llamado a su función destructora.

Introducción a las funciones amigas

Una función amiga no es un miembro de una clase pero tiene acceso a sus elementos privados.
Las funciones amigas principalmente son útiles para la sobrecarga de operadores y la creación de funciones de E/S o para que una función tenga acceso a los miembros privados de dos o más clases diferentes.

class A{
    int n;
public:
    friend int nombre_función ( A obj );
};

int nombre_función ( A obj ){
    obj.n++;
    return obj.n;
}

Las funciones amigas no son funciones miembro y no se puede acceder a ellas mediante un nombre de objeto. La llamada a la función se realiza como una llamada normal.
Las funciones amigas no se heredan. Si una clase base tiene una función amiga, esta función no es amiga de las clases derivadas de esta clase base.
Una función amiga puede ser amiga de más de una clase.

Referencias anticipadas
C++ permite hacer referencias anticipadas para poder emplear nombres de clases que todavía no han sido declarados. Para hacer esto, debemos advertir al compilador antes de realizar la referencia, que el nombre en concreto que vamos a usar pertenecerá a una clase. Lo hacemos de la siguiente forma:

class nombre_clase_A;

Función miembro de una clase y amiga de otra clase


class Cliente;

class Vendedor : public Persona{
    int id_vendedor;
    float salario;
public:
    int numero_ventas(Cliente c);
};

class Cliente : public Persona{
    int id_cliente;
public:
    friend int Vendedor::numero_ventas(Cliente c);
};

int Vendedor::numero_ventas(Cliente c){...}




Hasta aquí la primera parte de los apuntes sobre C++!
Un saludo a todos!!