miércoles, 15 de septiembre de 2010

Sobrecarga de operadores en C++

La sobrecarga de operadores, aunque puede ser una capacidad exótica, la mayoría de personas las usa implícita y regularmente se valen de los operadores sobrecargados.

Por ejemplo, el operador de suma (+) funciona de manera diferente sobre los enteros, puntos flotantes y dobles. No obstante dicho operador funciona muy bien con las variables int, float y double y varios otros tipos integrados han sido sobrecargados por el propio lenguaje C++.

Los operadores se sobrecargan escribiendo una definición de función (con su encabezado y cuerpo) de manera habitual, excepto que el nombre de la función ahora se vuelve la palabra clave operator, seguida por el símbolo del operador que se sobrecarga. Por ejemplo el nombre de la función operator+ sirve para sobrecargar el operador de suma (+).

Para utilizar un operador sobre objetos de una clase, dicho operador debe ser sobrecargado, con dos excepciones: el operador de asignación (=) puede utilizarse con cualquier clase, sin sobrecarga explícita.

El comportamiento predeterminado del operador (=) es una asignación a nivel de miembros de los datos miembro de la clase. El operador de dirección (&) también puede utilizarse sin sobrecarga con objetos de cualquier clase, simplemente devuelve la dirección de memoria del objeto.

La sobrecarga de operadores no es automática; el programador debe escribir funciones de sobrecarga de operadores que realicen las operaciones deseadas. A veces conviene que estas funciones se hagan funciones miembro, en otras ocasiones conviene que sean funciones friend, ocasionalmente puede hacerse funciones no miembro, no friend.

Es posible llegar a los extremos de la sobrecarga, como sobrecarga, como sobrecargar el operador + para que realice operaciones tipo resta. Tales empleos de la sobrecarga hace que sea muy difícil entender el programa.

Una lista de operadores que pueden o no sobrecargarse es la siguiente:

Operadores que pueden sobrecargarse

+-*/%^^&|
-!=< > +=-=*=
/=%=A=&=!=<< >> >>=
<<===!=<=>=&&||++
--->*->[ ]( )newdelete
new [ ]delete []







Operadores que NO pueden sobrecargarse

..*::?:sizeof


Los operadores &, *, + y - tiene versiones unarias y binarias, estas versiones unarias y binarias se pueden sobrecargar por separado.

No es posible crear nuevos operadores; sólo se pueden sobrecargar los operadores existentes, esto desgraciadamente, evita que el programador use notaciones como ** como en BASIC para la exponenciación.

La sobrecarga de un operador de asignación y de uno de suma para permitir instrucciones como:


obj eto2=obj eto2+obj eto1;


no implica que el operador += también este sobrecargado para permitir instrucciones como:


obj eto2 +=obj eto1;


tal comportamiento puede lograrse explícitamente sobrecargando el operador += de dicha clase.

La funciones de operador pueden ser funciones miembro o funciones no miembro, estas últimas con frecuencia se hacen friend por razones de desempeño. Las funciones miembro utilizan implícitamente el operador this para obtener uno de los argumentos de su objeto de clase.

Tal argumento de función puede debe listarse explícitamente en una llamada de función no miembro.

Cuando una función de operador se implemente como función miembro, el operador de la izquierda (o el único) debe ser un objeto de clase (o una referencia a un objeto de clase) de la clase del operador. Si el operador de la izquierda debe ser un objeto de una clase diferente o un tipo integrado, esta función operador debe implementarse como función no miembro.

Una función de operador no miembro debe ser friend si necesita acceder directamente a miembros prívate o protected la clase.
Las funciones miembro de los operadores de una clase específica se llaman sólo cuando el operando de la izquierda de un operador binario específicamente es un objeto de esa clase, o cuando el operando de un operador unario es un objeto de esa clase.

Ejemplo

Creación de una clase string y sobrecarga de la mayoría de sus operadores.


#include
#include
#include
#include
#include

enum bool { false, true };

class
string {
int size;
char *ptr;
public:
string (char []=""); //constructor predeterminado
string (string &); //constructor por copia
~string(); //destructor
string & operator= (string &); //asignaci¢n
bool operator== (string &); //prueba si s1=s2
bool operator!= (string &); //prueba si s1!=s2
bool operator! (); //prueba si string esta vacia
bool operator< (string &); //prueba si s1bool operator> (string &); //prueba si s1>s2
bool operator<= (string &); //prueba si s1<=s2 bool operator>= (string &); //prueba si s1>=s2
string & operator+= (string &); //concatenaci¢n
char operator[] (int); //operador de sub¡ndice
string operator() (int, int); //devuelve una subcadena
int longitud (void); //devuelve longitud de la cadena

friend ostream & operator<< (ostream &, string &);
friend istream & operator>> (istream &, string &);

};

string :: string (char * cadena){
size=strlen(cadena);
ptr=new char[size+1];
if(
ptr==NULL){ cout<<"No hay memoria"; exit(0); }
strcpy(ptr, cadena);
}

string :: string (string & copia){
size=strlen(copia.ptr);
ptr=new char[size+1];
if(
ptr==NULL){ cout<<"No hay memoria"; exit(0); }
strcpy(ptr, copia.ptr);
}

string :: ~string(){
delete [] ptr;
}

string & string :: operator= (string & s){
if(&
s != this){ //evita la autoasignaci¢n
delete [] ptr; //evita fugas de memoria
size=s.size;
ptr=new char[size+1];
strcpy(ptr, s.ptr);
}
else
cout<<"Intento de asignar un objeto a si mismo";

return (*
this); //habilita asignaciones en cascada
}

bool string :: operator== (string & s){
if(!
strcmp(ptr, s.ptr)) return (true);
return (
false);
}

bool string :: operator!= (string & s){
if(!(*
this==s)) return(true); //usa sobrecarga de ==
return(false);
}

bool string :: operator! (){
if (
size==0) return true;
return
false;
}

bool string :: operator< (string & s){
if (
strcmp(ptr, s.ptr)< 0) return true;
return
false;
}

bool string :: operator> (string & s){
if (
s < *this) return true;
return
false;
}

bool string :: operator<= (string & s){
if( !(
s < *this) ) return true;
return
false;
}

bool string :: operator>= (string & s){
if( !(
s > *this) ) return true;
return
false;
}

string & string :: operator+= (string & s){
char *temps=ptr;
size+=s.size;
ptr=new char[size+1];
if(
ptr==NULL){ cout<<"No hay memoria"; exit(0); }
strcpy(ptr, temps);
strcat(ptr, s.ptr);
delete [] temps;
return (*
this); //habilita llamadas en cascada
}

char string :: operator[] (int num){
assert(num>=0 && num<size); //prueba si num est en el rango
return (ptr[num]);
}

//Devuelve subcadena que comienza en: inicio y de longitud: subsize
string string :: operator() (int inicio, int subsize){
//asegura que inicio este en el rango y que subsize sea >=0
assert(inicio>=0 && inicio<size && subsize >=0);

string *subptr= new string; //string vac¡a
if(subptr==0){ cout<<"No hay memoria"; exit(0); }

//determina la longitud de la subcadena
if((subsize==0) || (inicio+ subsize> size))
subptr->size= size- inicio+ 1;
else
subptr->size= subsize+1;

//asigna memoria para la subcadena
delete subptr->ptr; //borra el arreglo de caract‚res
subptr->ptr= new char[subsize];
if(
subptr->ptr==NULL){ cout<<"No hay memoria"; exit(0); }

//copia la subcadena a la nueva string
strncpy(subptr->ptr, & ptr[inicio], subptr->size);
subptr->ptr[subptr->size]='\0'; //termina string

return (*subptr); //devuelve la nueva string
}

int string :: longitud (void){
return (
size);
}

ostream & operator<< (ostream & salida, string & s){
salida<< s.ptr;
return (
salida); //habilita el proceso en cascada
}

istream & operator>> (istream & entrada, string & s){
entrada>> s.ptr;
return (
entrada); //habilita proceso en cascada
}

void main(void){
textcolor(BLACK);
textbackground(WHITE);
clrscr();
string s1("hola"), s2(" amigos"), s3;


//probando operadores de igualdad y relacionales
cout<<"s1: " <<s1 <<" , s2: " <<s2 <<" , s3: " <<s3;

cout<<endl<<endl<<"Resultados al comparar s1 y s2: "
<<endl<<"Resultado de s1==s2: " << (s1==s2 ? "verdadero" : "falso")
<<
endl<<"Resultado de s1!=s2: " << (s1!=s2 ? "verdadero" : "falso")
<<
endl<<"Resultado de s1> s2: " << (s1> s2 ? "verdadero" : "falso")
<<
endl<<"Resultado de s1<><< (s1< s2 ? "verdadero" : "falso")
<<
endl<<"Resultado de s1>=s2: " << (s1>=s2 ? "verdadero" : "falso")
<<
endl<<"Resultado de s1<=s2: " << (s1<=s2 ? "verdadero" : "falso");

//prueba operador sobrecargado (!)
cout<<endl<<endl<<"Probando !s3: ";
if(!
s3){
cout<<"s3 esta vacio, asignando s1 a s3";
s3=s1;
cout<<"ns3: " << s3;
}

//probando operador sobrecargado de concatenacion
cout<<endl<<endl<<"Resultado de s1+=s2: "<<endl;
s1+=s2;
cout<<"s1: " <<s1;
//probando operador sobrecargado []
cout<<", s1[8]= " <<s1[8];

//prueba del operador sobrecargado ()
cout<<endl<<endl<<"Cadena resultante de s1(5,6): " <<s1(5,6) ;

//prueba el constructor de copiado
string *s4= new string(s1);
cout<<endl<<endl<<"Constructor copia para *s4: " <<*s4;

//prueba del operador de asignacion =
cout<<endl<<endl<<"Asignando s3 a *s4, *s4: ";
*
s4=s3;
cout<< *s4
<<endl<<"Longitud de *s4: " <<s4->longitud();

getch();
}

FUENTE: www.programacionenc.net

miércoles, 1 de septiembre de 2010

Optimizaciones en C++

Estas optimizaciones son fácilmente aplicables a código existente y en algunos casos aceleran notablemente la velocidad con que nuestros programas se ejecutan. Recuerden sin embargo lo siguiente: el código más rápido es aquel que no es llamado.


Usa listas de inicialización

Siempre usa listas de inicialización en constructores, por ejemplo usa:


TMyClass::TMyClass(const TData &data) : m_Data(data)
{
}


en lugar de


TMyClass::TMyClass(const TData &data)
{
m_Data = data;
}


Sin listas de inicialización, el contructor por defecto es llamado detrás de la escena en lugar del contructor de la clase.


Optimización para ciclos

Cuando sea posible, usa ciclos que vayan contando hacia abajo, por ejemplo usa:


for (i = n-1; i >= 0; --i)


en lugar de:


for (i = 0; i < n; ++i)


Es más rápido cuando se compara contra cero que contra cualquier otro valor. Nota también que:


++i


es más rápido que:


i++



Usa 'int'

Siempre usa el tipo de dato int en lugar de char o short cuando sea posible. int es siempre el tipo de dato nativo de la máquina


Haz las funciones locales estáticas (static)

Siempre declara las funciones locales como estáticas, usa static, por ejemplo:


static void foo()


Esto quiere decir que la función no será visible a funciones fuera del archivo .cpp donde se usa, y algunos compiladores C++ pueden tomar ventaja de esta optimización.


Optimiza las sentencias if

Factor del salto. Por ejemplo usa:


bar();
if (
condition)
{
undoBar();
foo();
}


en lugar de:


if (condition)
{
foo();
}
else
{
bar();
}


Usa tu buen juicio para ver si la operación undo es más rápida!


Optimiza las sentencias switch

Pon los casos más comunes al inicio


Evita las operaciones excesivas

La suma es más rápida que la multiplicación y la multiplicación es más rápida que la división.


Inicializar en la declaración

Cuando sea posible inicializa las variables cuando las declaras, por ejemplo:


TMyClass myClass = data;


es más rápido que:


TMyClass myClass;
myClass = data;


Declarar y luego inicializar invoca el contructor por defecto y luego hacer una signación son 2 operaciones. Inicializar en la declaración invoca sólo el contructor copia.


Paso por referencia

Siempre trata de pasar clases por referencia en lugar de hacerlo por valor, por ejemplo usa:


void foo(TMyClass &myClass)


en lugar de:


void foo(TMyClass myClass)



Uso de operadores

Usa:


myClass += value;


en lugar de:


myClass = myClass + value;


La primera versión es mejor que la segunda porque se evita la necesidad de crear un objeto temporal.


Has las funciones pequeñas de tipo inline

Las funciones pequeñas deberían ser declaradas inline para mejorar el rendimiento, por ejemplo:


inline void foo()


Cuando se trata de funciones largas, no se debe declararlas "inline"


Usa clases sin nombre

Cuando sea posible usa clases sin nombre, por ejemplo:


foo(TMyClass("abc"));


es más rápido que:


TMyClass myClass("abc");
foo(myClass);


porque en el primer caso, el parámetro y la clase comparten memoria
FUENTE: www.programacionenc.net