Miércoles, 24 de mayo de 2006
Para continuar con el anterior artículo explicaremos:
Antes de continuar con la segunda parte de esta serie. Comentaré que perfectamente conozco manuales como el de C con clase, y lo recomiendo a todo el mundo que esté empezando en esto de la programación (yo ya lo leí). Estos trabajos simplemente son un apoyo a aspectos difíciles del lenguaje, un material de apoyo, y un intento de documentar temas superficialmente tocados en Internet.
1.- SOBRECARGA DE OPERADORES:
Se explicará a continuación la sobrecarga de los operadores más difíciles de encontrar en otros tutoriales o libros.
1- Operador new:
Antes de nada, comentar que estos prototipos son inamovibles, lo he comprobado. Su prototipo para memoria dinámica de tipos simples:
void* operator new(size_t tamano);
void* operator new[](size_t tamano)
void* operator new(size_t tamano)
{
void *puntero = NULL;
puntero = new "tipo";
return puntero;
}
void* operator new(size_t tamano)
{
void *puntero = NULL;
puntero = new("tipo"); //No entra en recursivo
return puntero;
}
void* operator new[](size_t tamano)
{
void *puntero = NULL;
puntero = new "tipo"[tamano];
return puntero;
}
objeto.operator new(parámetros);
void* operator new(size_t tamano)
{
void *puntero = NULL;
puntero = malloc(tamano);
return puntero;
}
void operator delete(void *p);
void operator delete(void *puntero)
{
delete puntero;
return;
}
void operator delete[](void *p);
{
delete []puntero;
return;
}
tipo& nombre_clase::operator [](int indice )
class Arrayseguro
{
public:
int array[5];
Arrayseguro();
int & operator [](int indice);
};
Arrayseguro::Arrayseguro()
{
for(int i=0;i<5;i++)
array[i]=i;
}
int & Arrayseguro::operator [](int indice)
{
if(i<0 || i > 4)
exit(1);
return array[i];
}
int main()
{
Arrayseguro objeto;
objeto.array[3] = 8; //FUNCIONA
//objeto.array[5] = 9; ABORTA
return 0;
}
class Personalidad
{
protected:
int sociabilidad;
int gracioso;
public:
virtual void mostrar() = 0;
virtual int get_gracioso() = 0;
};
class Persona : public Personalidad
{
protected:
char *nombre;
int edad;
public:
void mostrar();
int get_gracioso();
bool mayor_de_edad();
};
void Persona::mostrar()
{
cout << "La persona es: " << nombre << endl;
return;
}
int Persona::get_gracioso()
{
return gracioso;
}
bool mayor_de_edad()
{
if (edad >= 18)
return true;
return false;
}
int main()
{
Persona Miguel;
return 0;
}
Por: Miguel Araujo | Programación | Comentarios (6) | Referencias (0)
Para usar la gestión de memoria "tipo C++" hay que llamar al new no sobreescrito hay que usar el operador :: (resolución de ámbito o "scope") como quieres llamar al new "global" no tienes que especificar nombre de clase ni namespace (ver el ejemplo)
Por otro lado está bien saber como se sobreescribir operadores, pero hay que tener una buena razón para ello, porque a alguein que no conozca el código puede ser ilegible (en particular esos operadores new, delete que tienen un comportamiento bien definido si no están sobreescritos... )
Un saludo y gracias por el esfurzo en tu artículo
ejemplo:
#include <stdio.h>
#include <iostream>
using namespace std;
class A
{
public:
void* operator new(size_t)
{
cout<<"constryendo uno (sobreescrito)"<
}
void* operator new[] (size_t siz)
{
cout<<"constryendo vector (sobreescrito), tamaño "<< siz <
}
};
int main ()
{
A* a= new A[25];
A* b= new A;
}
mig21 | 25-05-2006 12:51:03
me temo que la info de mi comentario anterior no es del todo válida, voy a investigar un poquito... :)
mig21 | 25-05-2006 13:21:59
En el ejemplo anterior el problema es que se llamaba al operador global dentro del operador sobreescrito, con lo que se pasaba dos veces por el constructor (y no estoy seguro que no se pierda memoria, habría que comprobarlo...)
eso si se puede evitar usar el gestor de memoria sobreescrito si se llama a ::new y ::delete (para más dificultad de uso aún :) )
En general sobrecargar new y delete sirve para hacer gestion distinta a la normal del heap, por ejemplo hacer un gestor que coja mucha memoria al pricipio y vaya repartiéndola a los que la vayan pidiendo. Le veo poca utilidad en la mayoría de los casos...
(por aquí hay alguien que sabe de que va y no le gusta mucho...: http://www.scs.stanford.edu/~dm/home/papers/c++-ne...)
hacer un gestor de memoria de una clase con new y delete (sencillo y muy poco útil) sería:
#include <stdio.h>
#include <iostream>
using namespace std;
class A
{
public:
static int i;
int ii;
A()
{
ii=i++;
cout<<"construyendo... "<< i<<" "<
~A()
{
ii=i--;
cout<<"destruyendo... "<< i<<" "<
static void* operator new(size_t t)
{
cout<<"constryendo uno (sobreescrito)"<
}
static void* operator new[] (size_t siz)
{
cout<<"constryendo vector (sobreescrito), tamaño "<< siz <
}
static void operator delete(void *point)
{
cout<<"destryendo uno (sobreescrito)"<
}
void operator delete[] (void * point)
{
cout<<"destryendo vector (sobreescrito)"<
}
};
int A::i=0;
int main ()
{
A* a= new A[10];
A* b= new A;
A* c= ::new A;
for(int i=0;i<10;++i)
cout<
delete []a;
delete b;
::delete c;
return 0;
}
mig21 | 25-05-2006 14:02:21
Carlos | 25-05-2006 16:04:47
Por último, y como curiosidad
en la primera versión hacía -> return ::new char[siz];
algo que yo no sabía es que en el parámetro tipo size_t del new te viene el tamaño en bytes de lo que se va a alojar, con lo que
A* a= new A[25];
le pasaba un siz al new que es el número de bytes. Total que debería poner (para que el número sea el mismo)
return ::new char[siz/sizeof(A)];
de todos modos pasaría dos veces por el constructor, porque (no lo sabía) el cada new significa pasar por el constructor. Si se tienen datos de la clase (estáticos) y se cambian en las construcción (una cuenta de referencias, como en el ii del segundo ejemplo) puede dar comportamientos inesperados. Pero no perdía memoria...
Como viene a decir el artículo que menciono en el tercer comentario, una cosa es la gestion de memoria y otra la inialización (o destrucción) cosa que en C++ está un poco ligada...
Creo que he liado más que ayudado, lo siento O:)
mig21 | 25-05-2006 18:02:40
Hola:
Yo he usado la sobrecarga de new y delete globales para hacer contabilidad de memoria reservada y liberada, con la intención de ver en qué partes del ćodigo se olvida hacer los delete.
Simplemente se hace una pequeña librería compilada con estas dos funciones. Si enlazas tu ejecutable con ella, tienes contabilidad de punteros. Si no la enlazas, no la tienes. Es una forma cómoda y rápida de sin tener que tocar en absoluto código -sólo hay que linkar-, ver dónde se pierde esa memoria.
En cuanto a usar malloc(), lo hago porque estoy redefiniendo new. Si uso new dentro de new, tengo recursión infinita. No he probado si llamando a ::new en vez de new se evita esa recursión, pero ... ¿no es lo mismo ::new que new a secas?.
Cada clase puede sobreescribir su propio new y delete, pero eso no me sirve para contabilidad global de punteros, además de que si quieres poner o quitar, tienes que tocar el código. La contabilidad de punteros es una cosa temporal y mientras se está desarrollando.
Se bueno.
chuidiang | 07-11-2007 23:09:36

Tu punto de encuentro GNU/Linux: Manuales, anécdotas, curiosidades, consejos y trucos, ¡sácale partido a tu ordenador!
Diseñado por Studio.st
Online gracias a Bitacoras.com