lunes, 1 de agosto de 2011

Instalar Java (JDK 6) en Debian/Ubuntu

Java Development Kit (o simplemente, JDK) es un software que nos provee de excepcionales herramientas de desarrollo para programar y crear aplicaciones y programas en Java, mismo software que peude instalarse en un equipo local o en una unidad de Red.

java 188x300 Instalar Java (JDK 6) en Debian/Ubuntu

JDK es multiplataforma, y puede instalarse en entornos Linux, Windows, Solaris y Mac OS. Hoy vamos a aprender a instalarlo en Linux, concretamente en Debian y distribuciones basadas en ésta, tal es el caso de Ubuntu. Recordemos que JDK es un complemento necesario para IDE’s como NetBeans, que lo necesita para poder programar en Java.

1. Antes que nada, debemos descargar el binario para Linux de JDK. Lo puedes descargar desde aquí, seleccionando la plataforma Linux y en la página posterior haciendo click en el segundo enlace de descarga (el primero es de arquitectura RPM). Una vez descargado, guardamos el archivo .bin en nuestra carpeta personal (/home/nombredeusuario).

2. Abrimos una terminal y nos logueamos como root (usando el comando su). Procedemos a crear el directorio de la instalación:

mkdir /usr/java

3. Le asignamos permisos de propietario a tu usuario (que es el que va a ejecutar JDK posteriormente), con el comando (ojo, cambiar tu_usuario por el de tu sesion en Debian o Ubuntu):

chown -R tu_usuario /usr/java

4. Copiamos el binario que descargamos anteriormente (JDK 6 update 11) al directorio que creamos (/usr/java) y le damos persmisos de ejecución:

cp /home/tu_usuario/jdk-6u11-linux-i586.bin  /usr/java
chmod a+x /usr/java/jdk-6u11-linux-i586.bin

5. Lanzamos la instalación del binario de JDK:

./jdk-6u11-linux-i586.bin

6. Aceptamos la licencia y procedemos con la instalación. Una vez concluída, debemos asignar permisos de propietario para nuestro usuario en la carpeta que se creó durante la instalación:

chown  -R tu_usuario  /usr/java/jdk1.6.0_11

7. Para ejecutar los comandos “java” y javac” desde cualquier ubicación hace falta editar el archivo .bashrc del superusuario (root) y de tu propio usuario. Para ello, debemos editar el archivo correspondiente para cada usuario con las siguientes lineas, mismas que se agregarán al final:

export JAVA_HOME=/usr/java/jdk1.6.0_05
export PATH=$JAVA_HOME/bin:$PATH

Para acceder a editar el archivo del usuario root:

gedit /root/.bashrc

Para tu usuario:

gedit /home/tu_usuario/.bashrc
8. Al terminar de añadir las líneas a ambos archivos, guardamos los cambio y cerramos la consola. JDK está instalado, con los permisos suficientes y listo para programar en Java.

Fuente: http://www.pcdigital.org

lunes, 15 de noviembre de 2010

Plantillas en C++

Las plantillas nos permiten especificar, con un solo segmento de código, un rango completo de funciones relacionadas (sobrecargadas), llamadas funciones de plantilla, o un rango completo de clases relacionadas, llamadas clases de plantilla.

Podríamos escribir una sola plantilla de función para una función de ordenamiento de arreglos y luego hacer que C++ generara automáticamente funciones de plantilla separadas que ordenaran un arreglo de int, un arreglo de float, un arreglo de string, etc.

Podríamos escribir una sola plantilla de clase de pila y luego hacer que C++ generara automáticamente clases de plantillas separadas, tales como una clase de pila de int, una clase de pila de float, una clase de pila de string, etc.

Hay que observar la diferencia entre las plantillas de función y las funciones de plantilla: las plantillas de función y las plantillas de clase son como plantillas con las cuales trazamos formas, y las funciones de plantilla y las clases de plantilla son como los trazos separados, que tienen la misma forma pero pueden trazarse en colores diferentes, por ejemplo.

Plantillas de función

Las funciones sobrecargadas se utilizan normalmente para realizar operaciones similares sobre diferentes tipos de datos. Si las operaciones son idénticas para cada tipo, esto puede realizarse en forma más compacta y conveniente mediante el uso de plantillas de función. Basándose en los tipos de argumento que se proporcionan en las llamadas a esa función, el compilador genera automáticamente funciones de código objeto separadas para manejar adecuadamente cada tipo de llamada. En C esta tarea se puede realizar mediante macros creadas con la directiva de preprocesador #define.

Sin embargo las macros presentan la posibilidad de serios efectos secundarios y no permiten que el compilador realice revisión de tipo. Las plantillas de función proporcionan una solución compacta similar a la de las macros, pero permiten una revisión de tipo completa. Todas las definiciones de plantillas de función comienzan con la palabra clave témplate, seguida de una lista de parámetros formales para dicha plantilla encerrados entre paréntesis angulares (<>), cada parámetro formal que representa un tipo debe estar precedido por la palabra clave class, como en:

template
template
template


Los parámetros formales de una definición de plantilla se utilizan (como sucedería con los argumentos de tipos integrados o de tipos definidos por el usuario) para especificar los tipos de argumentos de la función, para especificar el tipo de devolución de la función y para declarar variables en el interior de la función.

Para definir una plantillas de clase, se puede usar el siguiente formato:

template
class Nombre { . . .uso del parámetro T en funciones o datos miembro.. }


Primer programa

El siguiente programa hace uso de plantillas para determinar el mínimo y máximo valor de un arreglo de elementos dado.
La primera función recibe tiene como parámetros un puntero al tipo de elemento dado y el número de elementos y retorna el menor de los elementos que se encuentran en el arreglo.
La segunda función recibe los mismos parámetros y retorna el mayor de los elementos presentes en el arreglo.
Finalmente en la función main, se hace una prueba de estas funciones, con arreglos de enteros y flotantes.

#include
#include

template
T minimo (T * Array, int num_elemen)
{
T min= Array [0] ;

for (int i=1; i< min =" Array" t="">
T maximo (T * Array, int num_elemen)
{
T max= Array [0];

for (int i=1; i<> max)
max = Array [ i ];

return max;
};

void main(void)
{
int ArrayInt [3] = { 2, 8, 6};
float ArrayFloat [3] = { 12.1, 8.7, 5.6 };
int i;

cout<<"Arreglo de enteros: "; for (i=0; i<3; i="0;">
#include

template
void Ordenar ( T * a, int st, int fn)
{
int i, j , k;
T item_a_ordenar;

k = 1 ;

do
{
k= 3 * k + 1;
} while (k < i=" st+k;" item_a_ordenar =" a" j =" i"> 1);
}

void main(void)
{
int Arraylnt[3] = { 2, 3, 1 } ;
float ArrayFloat[3] = {25.0, 3.0, 45.2 } ;
int i ;

cout << "Enteros: " << i="0;" i="0;" i="0;" i="0;">
#include

enum estado_pila { OK, LLENA, VACIA };

template
class NuevaPila
{
int tamanyo;
T *tabla;
int cima;
estado_pila estado;

public:
NuevaPila(int =10);
~NuevaPila() { delete [] tabla; }
void meter (T);
T sacar ();
void visualizar ();
int num_elementos ();
int leer_tamanyo () { return tamanyo; }
}

template
NuevaPila :: NuevaPila (int tam)
{
tamanyo=tam;
tabla= new T [tamanyo] ;
cima=0;
estado=VACIA;
}

template
void NuevaPila :: meter (T elemento)
{
if( estado!=LLENA)
tabla [cima++]=elemento;
else cout << "*** Pila llena ***"; if(cima>=tamanyo)
estado=LLENA;
else
estado=OK;
}

template
T NuevaPila :: sacar ()
{
T elemento=0;

if(estado!=VACIA)
elemento=tabla[--cima];
else cout<<"*** Pila vac¡a ***"; if(cima<=0) estado=VACIA; else estado=OK; return elemento; } template
void NuevaPila :: visualizar ()
{
for (int i=cima-1; i >=0; i--)
cout << t="">
int NuevaPila :: num_elementos ()
{
return cima;
}

void main()
{
cout << "Probando pila de enteros"; NuevaPila s1 (5);
s1.meter(5);
s1.meter(10);
s1.meter(15);

cout <<> s2 (5);
s2.meter(7.5);
s2.meter(10.2);
s2.meter(15.3);

cout <<> s3 (5);
s3.meter('m');
s3.meter('p');
s3.meter('s');

cout <<>
#include
#include

template
class Cola;

///////////////////////////////////
// definicion clase NodoCola /////
//////////////////////////////////

template
class NodoCola {
TIPONODO dato; // dato
NodoCola * sig; // puntero siguiente nodo
public:
NodoCola (const TIPONODO &); // constructor
TIPONODO getDato() const; // devuelve dato del nodo

friend class Cola ; // hace que Cola sea friend
};

// constructor
template
NodoCola :: NodoCola (const TIPONODO & info)
: dato(info), sig (0) { }

// devuelve una copia del dato que esta en el nodo
template
TIPONODO NodoCola :: getDato() const {
return dato;
}

// definicion enumeracion //
enum bool { false, true };

//////////////////////////////////
// definicion clase Cola /////////
//////////////////////////////////

template
class Cola {
NodoCola * primero; // puntero al primer nodo
NodoCola * ultimo; // puntero al ultimo nodo
public:
Cola (); // constructor
~Cola(); // destructor
void encolar (const TIPONODO &); // permite insertar nodo
bool decolar (TIPONODO &); // permite eliminar nodo
bool estaVacia() const; // verifica si la cola esta vacia
void imprimir() const; // imprime datos de la cola

// funci¢n de utileria para asignar un nuevo nodo
NodoCola * getNuevoNodo (const TIPONODO &);
};

// constructor predeterminado
template
Cola :: Cola (): primero(0), ultimo(0) { }

// destructor
template
Cola :: ~Cola () {
if( !estaVacia() ) { // ingresa si la cola no esta vacia
cout<<"Eliminando nodos ..." <<> * actual = primero, *temporal;

while (actual != 0) { // borra los nodos restantes
temporal = actual;
cout<<>dato << actual =" actual-">sig;
delete temporal;
}
}
cout << "Todos los nodos han sido eliminados" << tiponodo="">
void Cola :: encolar (const TIPONODO & valor) {
NodoCola * nuevo = getNuevoNodo (valor);

if ( estaVacia() ) // si la cola esta vacia
primero=ultimo=nuevo;

else { // si la cola no esta vacia
ultimo->sig=nuevo;
ultimo=nuevo;
}
}

// elimina un nodo
template
bool Cola :: decolar (TIPONODO & valor) {
if( estaVacia() ) // la cola esta vacia
return false; // eliminacion no satisfactoria

NodoCola *temporal = primero;

if(primero==ultimo)
primero=ultimo=0;

else
primero=temporal->sig;

valor=temporal->dato; // dato que se esta eliminando
delete temporal;
return true; // eliminacion satisfactoria
}

// verifica si la cola esta vacia
template
bool Cola :: estaVacia () const {
if (primero==0) return true;
return false;
}

// imprime el contenido de la cola
template
void Cola :: imprimir() const {
if ( estaVacia() ) {
cout<<"La lista esta vacia" <<> *actual = primero;

cout<<"La cola es: "; while (actual!=0) { cout<<>dato <<" "; actual=actual->sig;
}

cout << tiponodo="">
NodoCola * Cola :: getNuevoNodo (const TIPONODO & valor) {
NodoCola * nuevo = new NodoCola (valor);
assert(nuevo!=0);
return nuevo;
}

/////////////////////////////////////////////////////////////////////////////

// funcion que prueba una cola
template
void probarCola ( Cola & ObjetoCola) {

T valor;
int opcion;

for (;;){
cout<<<"(1) Insertar, (2) Eliminar, (3) Imprimir, (4) Salir"<<<"Seleccion: "; cin>>opcion;

switch( opcion ){
case 1:
cout<<"Ingrese dato: "; cin>> valor;
ObjetoCola.encolar(valor);
break;

case 2:
if(ObjetoCola.decolar(valor))
cout<<"Dato " << opcion="="> ColaEnteros;
probarCola (ColaEnteros);

}


FUENTE: www.programacionenc.net


lunes, 1 de noviembre de 2010

Polimorfismo

Con las funciones virtuales y el polimorfismo es posible diseñar e implementar sistemas que con extensibles con mayor facilidad. Los programas se pueden escribir para que procesen objetos en forma genérica, como objetos de clase base de todas las clases existentes en una jerarquía. Las clases que no existen durante el desarrollo del programa se pueden agregar con poca o sin modificaciones a la parte genérica del programa, siempre y cuando esas clases sean parte de la jerarquía que está siendo procesada en forma genérica. Las únicas partes del programa que necesitarán modificaciones son aquellas que requieren un conocimiento directo de la clase particular que se está agregando a la jerarquía.

Funciones virtual

Si tenemos un conjunto de clases con las formas: Circle, Triangle, Rectangle, Square, que están derivadas de la clase base Shape. En la programación orientada a objetos a cada una de estas figuras se le podría dar la habilidad de dibujarse a sí misma. Aunque cada clase tiene su propia función draw, esta es bastante diferente en cada forma. Cuando se dibuja una forma sin importar el tipo, sería agradable tratar a todas esta formas genéricamente como objetos de la clase base Shape.
Luego, para dibujar cualquier forma podríamos llamar a la función draw de la clase base Shape y dejar que el programa determine dinámicamente (en tiempo de ejecución) cual función draw de la clase derivada se debe utilizar.

Para permitir este tipo de comportamiento declaramos a draw de la clase base como función virtual, y sobreponemos a draw en cada una de las clases derivadas para dibujar la forma adecuada.

Una función de este tipo se declara precediendo la palabra clave virtual, al prototipo de la función, en la definición de la clase, por ejemplo:


virtual void draw();


Clases base abstractas y clases concretas.

Hay casos en los que es útil definir clases, para las cuales el programador nunca pretende instanciar ningún objeto. Tales clases se denomina clases abstractas. Debido a que estas se utilizan como clases base en situaciones de herencia, normalmente nos referimos a ellas como clases base abstractas. No es posible instanciar un objeto de una clase base abstracta.

El único propósito de una clase abstracta es proporcionar una clase base adecuada a partir de la cual las clases puedan heredar interfaces y/o implementaciones. Las clases de las que se pueden instanciar objetos se denomina clases concretas.

Por ejemplo, se puede tener una clase base abstracta TwoDimwensionalShape y derivar clases concretas tales como Cube, Sphere, Cylinder. Las clases base abstractas son demasiado genéricas como para definir objetos reales, y necesitamos ser mucho más específicos antes de que podamos pensar en instanciar objetos.

Para que una clase sea abstracta se debe declarar como "pura" una o más de sus funciones virtual. Una función virtual pura es aquella que tiene un inicializador = 0 en su declaración, por ejemplo:


virtual float earningsO const = 0;


Polimorfismo

C++ permite el polimorfismo, que es la habilidad de los objetos de diferentes clases que están relacionados mediante la herencia para responder en forma diferente al mismo mensaje (es decir, a la llamada de función miembro). El mismo mensaje que se envía a muchos tipos de objetos diferentes toma "muchas formas", y de ahí viene el término polimorfismo.

Por ejemplo, si la clase Rectangle se deriva de la clase Quadrilateral, un objeto Rectangle es una versión más específica de un objeto Quadrilateral. Una operación (como el cálculo del perímetro o el área) que puede realizarse en un objeto Quadrilateral también puede realizarse en un objeto Rectangle.

El polimorfismo se implementa por medio de funciones virtual.

Cuando se hace una petición por medio de un apuntador de clase base (o referencia) , para utilizar una función virtual, C++ elige la función sobrepuesta correcta en la clase derivada adecuada que está asociada con ese objeto. Hay muchas veces en que una función miembro no virtual está definida en la clase base y sobrepuesta en una clase derivada. Si a una función de estas se le llama mediante un apuntador de clase base al objeto de la clase derivada, se utiliza la versión de la clase base. Si la función miembro se llama mediante un apuntador de la clase derivada, se utiliza la versión de dicha clase derivada. Este comportamiento no es polimórfico.

Mediante el uso de funciones virtual y el polimorfismo, una llamada de función miembro puede causar que sucedan diferentes acciones, dependiendo del tipo de objeto que recibe la llamada. Esto le da una capacidad expresiva tremenda al programador.

El polimorfismo promueve la extensibilidad: el software que está escrito para llamar al comportamiento polimórfico se escribe en forma independiente de los tipos de objetos a los cuales se envían los mensajes. Por lo tanto los nuevos tipos de objetos que pueden responder a los mensajes existentes se pueden agregar a un sistemas, sin modificar el sistema base.

Un ejemplo concreto

El enunciado del programa sería el siguiente:

Definir una clase Shape que sea una clase base abstracta que contenga la interfaz hacia la jerarquía. Derive a TwoDimensionalShape y ThreeDimensionalShape de la clase Shape, que también serán abstractas. Utilice una función print virtual para enviar a la salida el tipo y dimensiones de cada figura. También incluye funciones virtual area y volume para que estos cálculos puedan realizarse para los objetos de cada clase concreta de la jerarquía.
Escriba un programa controlador que pruebe la jerarquía de la clase Shape.

Y la solución...


#include
#include
#include

// clase Shape

class Shape {
public:
virtual double area() const { return 0.0; }
virtual double volume() const { return 0.0; }

// funcion virtual pura sobrepuesta en las clases derivadas
virtual void print() const=0;
};

////////////////////////////////
// clase TwoDimensionalShape ///
////////////////////////////////

class TwoDimensionalShape : public Shape {
public:
virtual void print () const=0;
};

// clase triangulo
class triangulo : public TwoDimensionalShape {
double lado1, lado2, lado3;
public:
triangulo (double=0.0, double=0.0, double=0.0);
void fijar_triangulo(double, double, double);
virtual double area() const;
virtual void print() const;
};

triangulo :: triangulo (double l1, double l2, double l3){
fijar_triangulo(l1,l2,l3);
}

void triangulo :: fijar_triangulo (double l1, double l2, double l3){
lado1= l1 > 0 ? l1 : 0;
lado2= l2 > 0 ? l2 : 0;
lado3= l3 > 0 ? l3 : 0;
}

double triangulo :: area () const {
double s;
s=(lado1+lado2+lado3)/2;

return
sqrt(s*(s-lado1)*(s-lado2)*(s-lado3));
}

void triangulo :: print () const {
cout << endl << "Triangulo" << endl
<< "Lado 1= " << lado1 << endl
<< "Lado 2= " << lado2 << endl
<< "Lado 3= " << lado3;
}

// clase cuadrado
class cuadrado : public TwoDimensionalShape {
double lado;
public:
cuadrado (double=0.0);
void fijar_cuadrado(double);
virtual double area() const;
virtual void print() const;
};

cuadrado :: cuadrado (double l) {
fijar_cuadrado(l);
}

void cuadrado :: fijar_cuadrado (double l){
lado= l>0 ? l : 0;
}

double cuadrado :: area () const {
return
lado*lado;
}

void cuadrado :: print() const {
cout << endl << "Cuadrado" << endl
<<"Lado= " << lado;
}

////////////////////////////////
// clase ThreeDimensionalShape /
////////////////////////////////

class ThreeDimensionalShape : public Shape {
public:
virtual void print () const=0;
};

// clase cubo
class cubo : public ThreeDimensionalShape {
double lado;
public:
cubo(double=0.0);
void fijar_cubo(double);
virtual double area() const;
virtual double volume() const;
virtual void print() const;
};

cubo :: cubo (double l){
fijar_cubo(l);
}

void cubo :: fijar_cubo (double l){
lado= l>0 ? l : 0;
}

double cubo :: area () const {
return
6*lado*lado;
}

double cubo :: volume () const {
return
lado*lado*lado;
}

void cubo :: print() const{
cout << endl << "Cubo" << endl
<<"Lado= " << lado;
};

// clase paralelepipedo
class paralelepipedo : public ThreeDimensionalShape {
double largo, ancho, altura;
public:
paralelepipedo(double=0.0, double=0.0, double=0.0);
void fijar_paralelepipedo(double, double, double);
virtual double area() const;
virtual double volume() const;
virtual void print() const;
};

paralelepipedo :: paralelepipedo (double l, double a, double h){
fijar_paralelepipedo(l,a,h);
}

void paralelepipedo :: fijar_paralelepipedo (double l, double a, double h){
largo= l>0 ? l : 0;
ancho= a>0 ? a : 0;
altura= h>0 ? h : 0;
}

double paralelepipedo :: area () const {
return
2*largo*ancho + 4*ancho*altura;
}

double paralelepipedo :: volume () const {
return
largo*ancho*altura;
}

void paralelepipedo :: print() const{
cout << endl <<"Paralelep¡pedo" << endl
<<"Largo= " << largo << endl
<<"Ancho= " << ancho << endl
<<"Altura= " << altura;
}

// llama a funcion virtual a partir del apuntador de clase base
// utilizando enlace dinamico
void virtualViaPointer (const Shape* baseClassPtr){
baseClassPtr->print();
cout << endl <<"Area= " << baseClassPtr->area() << endl
<<"Volumen= " << baseClassPtr->volume() << endl;
}

// llama a funcion virtual a partir de referencia a clase base
// utilizando enlace dinamico
void virtualViaReference (const Shape& baseClassRef){
baseClassRef.print();
cout << endl <<"Area= " << baseClassRef.area() << endl
<<"Volumen= " << baseClassRef.volume() << endl;
}

// funcion principal
void main(){
clrscr();
triangulo t(5.2,6.5,7.1);
virtualViaPointer (& t);
virtualViaReference (t);

cuadrado c(8.7);
virtualViaPointer (&c);
virtualViaReference (c);

getch();
clrscr();

cubo cub(8.3);
virtualViaPointer (&cub);
virtualViaReference (cub);

paralelepipedo p(4.5,6.7,9.2);
virtualViaPointer (&p);
virtualViaReference (p);

getch();
}
FUENTE: www.programacionenc.net


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