miércoles, 18 de abril de 2012

INICIANDO CON CLASES DE C++

En escencia, una clase en C++ es una estructura en el estilo de C con algunas ventajas sencillas pero muy potentes.

Declaración de clases

Para declarar una clase, todo lo que se necesita es escribir una definición de estructura y sustituir la palabra reservada struct por class. Por ejemplo, una clase empleado con campos como el nombre, el departamento, la posición, el una función que nos imprima la información de este quedaría así:

class Empleado {
char* m_nombre;
char* m_departamento;
char* m_posicion;
long m_salario;
void Imprimir( Empleado infoEmpleado);
}

Cuando usted declara una clase en C++, no se reserva memoria para la clase hasta que usted crea un objeto de la clase. Crear un objeto de una clase se llama instanciar un objeto. Un objeto creado de una clase de denomina instancia de una clase. Por ejemplo, yo puedo tener una instancia de empleado con el valor en m_nombre=Jose, m_departamento=Sistemas, m_posicion=programador y m_salario=3000000 por ejemplo.


Especificadores de acceso

C++ utiliza especificadores de acceso para permitir controlar a una clase el acceso a las variables de datos de esa clase. Los especificadores de acceso permiten acceder a algunos miembros de la clase y restringir el acceso a otros.

Hay tres especificadores de acceso en C++: public, private y protected. Cuando usted declara público ( public) un miembro de una clase, usted permite el acceso a tal miembro desde dentro y fuera de la clase. Los miembros de datos que son declarados protegidos ( protected ) son únicamente accesibles por funciones miembro de la clase, pero no se pueden acceder a ellos desde otras clases. Cuando un miembro de una clase es declarado privado ( private ) es ináccesible no sólo desde otras clases y otras partes del programa, sino también desde sus clases derivadas. Las clases derivadas se explicara posteriormente.

Miremos el siguiente programa de ejemplo. Se compone de tres partes: la primera una declaración de una clase llamada Empleado:

class Empleado {
private:
char* m_nombre;
char* m_departamento;
char* m_posicion;
long m_salario;

public:
void ImprimirInfo();
void SetNombre( char* nombre ) { m_nombre = nombre }
void SetDepartamento( char * departamento) { m_departamento = departamento }
void SetPosicion ( char* posicion ) { m_posicion = posicion }
void SetSalario ( long salario ) { m_salario = salario }
const char* GetNombre( ){ return m_nombre }
const char* GetDepartamento( ){ return m_departamento }
const char* GetPosicion( ){ return m_posicion }
const char* GetSalario( ){ return m_salario }
};

Las funciones SetNombre, SetDepartamento, setPosicion, setSalario, GetNombre, GetDepartamento, GetPosicion y GetSalario se denominan funciones intercaladas, que son funciones que se declaran en una sola línea.

Las variables de miembro son declaradas privadas para que funciones de miembro de otras funciones no tengan acceso a ellas sino a travez de la correspondiente funcion Get o Set. Las funciones de miembro si son declaradas públicas de tal modo que se pueda acceder a ellas desde otras funciones.

La definición de la función PrintInfo puede quedar así:

void Empleado::ImprimirInfo( )
{
cout << "Nombre: " << m_nombre << '\n';
cout << "Departamento: " << m_departamento << '\n';
cout << "Puesto: " << m_posicion << '\n';
cout << "Salario: " << m_salario << '\n';
}

Los dos dos puntos ( :: ) se denomina operador de resolución de ambito. Nos indica que la función que estamos definiendo que en este caso es ImprimirInfo, pertenece a la clase Empleado.

La tercera parte es la función main. Veamos como podría ser:

void main()
{
//creacion de un objeto de la clase Empleado
Empleado empleado12;

//asignacion de valores a las variables miembro
empleado12.SetNombre("Jose");
empleado12.SetDepartamento("Sistemas");
empleado12.SetPosicion("Programador");
empleado12.SetSalario(3000000);

//impresion de los datos
empleado12.ImprimirInfo();
}

Entonces, primero en :

Empleado empleado12;

Se instancia un objeto de la clase Empleado con nombre empleado12. Entonces empleado12 tiene la estructura de la clase Empleado.

Luego, en las líneas siguientes a la instanciación del objeto, se le asignan los valores iniciales a sus variables:

//asignacion de valores a las variables miembro
empleado12.SetNombre("Jose");
empleado12.SetDepartamento("Sistemas");
empleado12.SetPosicion("Programador");
empleado12.SetSalario(3000000);

Finalmente se llama ImprimirInfo para imprimir el contenido de las variables:

//impresion de los datos
empleado12.ImprimirInfo();

que lo que hará es imprimir el valor de las varibles en la pantalla.

Permitir el acceso a las variables solo a travez de funciones, que en la mayoría de los casos se llaman SetXxx y GetXxx, se llama encapsulación de datos. Las funciones que necesitan valores de otra clase, llaman a las funciones que les dan acceso y obtienen estos datos sin conocimiento de detalles específicos de como se manipulan los datos.

Operador de resolución de ambito

El operador de ambito permíte acceder de otra manera funciones de miembro y variables de miembro de una clase. Cuando aparece el operador de resolución de ámbito entre el nombre de la clase y el nombre de la función en un programa significa que la función especificada es un miembro de la clase especificada:
Empleado::ImprimirInfo();
El operador de resolución de ambito se suele utilizar para llamar funciones que se encuentran fuera del ambito de la función de llamada. Entonces, para llamar la función ImprimirInfo() de la clase Empleado se fuera de su ambito se debe utilizar este operador.
La principal diferencia entre este operador y los operadores punto y flecha es que el operador de resolución de ambito se utiliza para acceder a miembros de clases, y el operador punto y flecha para acceder a miembros de objetos específicos.
Veamos el siguiente código:
::MessageBox("Prueba del operador de resolucion");
Si el operador de resolución de ambito aparece sin un nombre de clase delante, significa que la función que esta llamando ( MessageBox ) no es miembro de ninguna clase.

El apuntador this

Este apuntador lo tiene todo objeto en C++, apuntando a sí mismo. Se puede utilizar este apuntador en cualquier lado para acceder a todos los miembros del objeto al cual esta apuntando este apuntador this. Veamos el siguiente código:
#include
class Miclase {
public:
Miclase() {} //constructor por defecto
~Miclase() {} //destructor
void yoMismo() { return this }
};
int main()
{
void* pClase;
Miclase unObjeto;
pClase = unObjeto.yoMismo();
cout<< "El puntero pClase es "
<< pClase <<'\n.';
return 0;
}
En este ejemplo la clase yoMismo() devuelve un apuntador al objeto que lo posee de la clase Miclase. El main() crea un objeto de la clase Miclase y luego llama a yoMismo(). Lo almacena en pClase y luego enseña el contenido, que en este caso es el valor de la referencia. Entonces este apuntador nos permitira realizar muchas cosas sobre los propios objetos con esta referencia.

Jerarquía de clases

Cuando una clase se deriva a partir de una clase base, la clase derivada hereda todas las variables de miembro y funciones de miembro de su clase base.
El siguiente código muestra como se puede derivar una clase a partir de una clase base en un programa en c++ y define una jerarquía sencilla.
#include
//clase base
class EmpInfo {
public:
//constructor y destructor
EmpInfo() {}
~EmpInfo(){}
private:
char* m_name;
char* m_dept;
char* m_position;
long m_salary;
public:
void SetName(char* name) { m_name=name; }
void SetDept( char* dept) { m_dept = dept; }
void SetPosition( char* position ) { m_position = position; }
void SetSalary( long salary ) { m_salary = salary; }
void PrintInfo();
};
//clase derivada
class OffshoreEmpInfo : public EmpInfo {
public
//constructor y destructor
OffShoreEmpInfo() {}
~OffshoreEmpInfo() {}
private:
char* m_country;
public:
void SetCountry( char* country) { m_country = country; }
void PrintInfo();
};
void EmpInfo::PrintInfo()
{
cout << "Nombre: " << m_name << "\n";
cout <<"Departamento: " << m_dept << "\n";
cout <<"Puesto: " << m_position << "\n";
cout << "Sueldo: " << m_salary << "\n";

}
void OffshoreEmpInfo::PrintInfo()
{
EmpInfo::PrintInfo();
cout << "País: " << m_country << "\n";

}
int main()
{
//Declaración de un objeto de la clase
EmpInfo* empInfo1 = new OffshoreEmpInfo;
OffshoreEmpInfo* empInfo2 = new OffshoreEmpInfo;

//relleno del objeto con datos
empInfo1.SetName( "Gabriel Lozano" );
empInfo1.SetDept( "Entrenamientos" );
empInfo1.SetPosicion("Vocalista");
empInfo1.SetSalary(240000);
empInfo1.PrintInfo();

//relleno del objeto con datos
empInfo2.SetName( "Alvaro Tovar" );
empInfo2.SetDept( "Relaciones humanas" );
empInfo2.SetPosicion("Entrevistador");
empInfo2.SetSalary(1800000);
empInfo2.SetCountry( "Colombia" );
empInfo2.PrintInfo();

return 0;

}

Declaración de una clase derivada

La siguiente es la declaración de la clase OffShoreEmpInfo:
//clase derivada
class OffshoreEmpInfo : public EmpInfo {
public
//constructor y destructor
OffShoreEmpInfo() {}
~OffshoreEmpInfo() {}
private:
char* m_country;
public:
void SetCountry( char* country) { m_country = country; }
void PrintInfo();
};
Advierta que la cabecera de la declaración de una clase derivada contiene los nombres de ambas clases: la clase derivada y la clase base. El nombre de la clase derivada se separa del nombre de la clase base por dos elementos: dos puntos y un especificador de acceso.

Redefinición de funciones miembro

Las clases derivadas pueden reemplazar o redefinir, funciones de miembro que heredan de sus clases bases. En el código anterior se redefinio la funcion PrintInfo().

Polimorfismo y funciones de miembro virtuales

El polimorfismo es un concepto clave de la programación orientada a objetos. El polimorfismo proporciona una forma de dar un nimbre a una acción que es realizada por objetos similares, implementando cada objeto la acción de una manera adecuada al objeto en concreto.
Para la implementación de polimorfismo en C++ se utiliza un tipo especial de función denominado función virtual. Una función virtual es el mecanismo por el que las clases derivadas pueden redefinir funciones de las clases base. Para crear una función virtual en un programa en C++ puede declarar la función utilizando la palabra clave virtual, como en la secuencia siguiente:
virtual void Display();

Ejemplo: utilización de una función virtual

En el siguiente código se demuestra la utilización de funciones virtuales de miembro. Se declara una clase base llamada BaseClass y una función derivada, DerivedClass, cada una de las cuales define una versión diferente de una función de miembro llamada Display. La función BaseClass::Display es una función de miembro virtual y DerivedClass::Display es una función que redefine BaseClass::Display.
#include
class BaseClass
{
//miembros de la clase base
public:
virtual void Display() { cout << 100 << "\n"; }

};
class DerivedClass: public BaseClass
{
//miembros de la clase derivada
public:
void Display() { cout << 200 << "\n"; }
};
void Print( BaseClass* bc)
{
bc->Display();
}
int main()
{
BaseClass* pMyBaseClass = new BaseClass;
DerivedClass* pMyDerivedClass = new DerivedClass;
Print(pMyBaseClass);
Print(pMyDerivedClass);
return 0;
}

Llamada a la función Print

En la función main del programa anterior, se instancian un objeto de una clase bae y un objeto de una clase derivada. La función main después llama a la funcioón Print.
int main()
{
BaseClass* pMyBaseClass = new BaseClass;
DerivedClass* pMyDerivedClass = new DerivedClass;
Print(pMyBaseClass);
Print(pMyDerivedClass);
return 0;
}
Como resultado, la función Print llama a la función de miembro BaseClass::Display y a la función de miembro DerivedClass::Display. Advierta, sin embargo, quela funciónPrint no llama a estas dos funciones utilizando dos punteros diferentes. En su lugar, utiliza el mismo puntero, concretamente bc, que,comopuede ver examinando la cabecera de la función Print, a continuación, es un puntero a BaseClass.

void Print( BaseClass* bc)
{
bc->Display();
}

Taller c++

Realize una agenda en donde se registre la información de contacto tanto de personas como de empresas. La información a almacenar es la siguiente:
1. Apellidos de la persona.
2. Nombre completo de la persona.
3. Dirección de la persona.
4. Telefono de la persona.
Esta información debe estar disponible tanto para personas naturales como para organizaciones. Para organizaciones se debe sumar el nombre de la empresa y la ciudad en donde esta se encuentra y el sitio web si lo hay, aparte de la información que ya se tiene que corresponde a lainformación de contacto de el gerente. En resumen, para las compañias se requiere la siguiente información aparte:
5. Nombre de la empresa.
6. Ciudad en donde se haya la empresa.
7. Dirección en internet.
Todos los registros se deben almacenar ordenados alfabeticamente utilizando el apellido y luego el nombre. Se debe poder: ver registros, adicionar registros, eliminar registros, modificar los registros y ver el contenido de un registro. Además no debe permitir el ingreso de un registro con apellidos y nombres que ya existian en la agenda. De nuevo se debe utilizar la arquitectura MVC. Con esto separamos el modelo de la vista y del controlador y podríamos, por ejemplo, en un futuro utilizar una base de datos como mecanismo de persistencia para los registros( aclaro, no en el taller).