void escribe(int i)
{
printf( "0%5d", i );
}
void escribe(const string &s)
{
printf( "\"%s\"", s.c_str() );
}
class Precio {
private:
double precio;
public:
static const double ValorEuroPts = 166.386;
Precio(int precioPts)
{ precio = precioPts / ValorEuroPts; }
Precio(double precioEur)
{ precio = precioEur; }
double getPrecio() const
{ return precio; }
int getPrecioPts() const
{ return precio * ValorEuroPts; }
};
Cadena c1( "Hola" );
c1 += ", mundo"; // Equivale a c1.operator+=( ", mundo" );
class Cadena {Este es el caso de operadores que modifican al objeto que está a la izquierda de los mismos (=, +=). También hemos creado un constructor de copia para poder estar a salvo de los problemas de compartición de memoria vistos anteriormente. Estos operadores modifican al objeto, por lo que lo devuelven como una referencia (que es equivalente a un puntero, excepto en la sintaxis). Ya que el objeto va a sobrevivir a la ejecución del método (operator+=() y operator=()), se devuelve como una referencia para evitar la alternativa, la devolución por valor, que supondría la copia del objeto. Así, de esta manera es más eficiente.
private:
char * cad;
void copiar(const string &s)
{ delete cad;
cad = new char[ s.length() + 1 ];
strcpy( cad, s.c_str() );
}
void concatenar(const string &s)
{
// ... más cosas ...
}
// ... más cosas ...
public:
// ... más cosas ...
Cadena(const string &s = "")
{ cad = NULL; copiar( s ); }
Cadena(const Cadena &c)
{ cad = NULL; copiar( s ); }
Cadena &operator+=(const string &s)
{ concatenar( s ); return *this; }
Cadena &operator+=(const Cadena &s)
{ concatenar( s.getCadena() ); return *this; }
Cadena &operator=(const Cadena &s)
{ copiar( s.getCadena() ); return *this; }
Cadena &operator=(const string &s)
{ copiar( s ); return *this; }
const char * getCadena() const
{ return cad; }
};
int main()
{
Cadena c1( "Hola" );
Cadena c2( c1 );
c1 += ", mundo";
c2 = c1;
c2 = "LP";
cout << c1.getCadena() << endl;
}
cad1 = cad2 = cad3 += ".\n";
bool operator==(const Cadena &c)
{ return ( strcmp( cad, c.cad ) == 0 ); }
bool operator==(const string &s)
{ return ( strcmp( cad, s.c_str() ) == 0 ); }
Cadena operator+(const Cadena &c)
{ return Cadena( string( c.cad ).concat( cad ) ); }
Cadena operator+(const string &s)
{ return Cadena( s.concat( cad ) ); }
int main()
{
Cadena c1( "Hola, " );
Cadena c2( "mundo." );
Cadena c3;
// c1 y c2 no se modifican
c3 = c1 + c2; // c3.operator=(c1.operator+(c2));
cout << c3.getCadena() << endl;
}
Ahora se puede escribir:
friend ostream &operator<<(ostream &o, const Cadena &c)
{ o << c.getCadena(); return o; }
friend istream &operator>>(istream &i, const Cadena &c)
{ string s; getline( i, s );
copiar( s ); return i };
int main()
{
Cadena c1 = "Hola, "; // Constructor, no operador =
Cadena c2;
cout << "Dime tu nombre: ";
cin >> c2;
cout << c1 << c2 << endl;
}
class Producto {
public:
friend class Factura;
// ... más cosas ...
};
class Factura {
public:
// ... más cosas ...
Factura(const Proveedor &p, const Producto &p);
void generar()
{
cout << "Factura: " << producto.nombre
<< '\t' << producto.precio;
}
};
#include <memory>Normalmente, este programa implicaría una fuga de memoria (memory leak), pues no se está liberando el objeto reservado con el operador new. La ventaja del autopuntero está precisamente en que, cuando se elimina el mismo, hace un delete automáticamente del objeto apuntado (auto_ptr no es más que una clase, en este caso se utiliza su destructor). Una limitación menor de auto_ptr es que no se puede utilizar la sintaxis '=' para la construcción. Es decir, no se puede compilar "std::auto_ptr
#include <cstdio>
#include <cstdlib>
int main()
{
std::auto_ptri( new int );
*i = 5;
std::printf( "%d\n", *i );
return EXIT_SUCCESS;
}
Un ejemplo un poco más útil de auto_ptr cuando se emplea una clase:
#include <memory>Tal y como se puede apreciar en el ejemplo, es posible emplear el operador '->' y el operador '*' normalmente, tal y como se haría con cualquier puntero normal. De hecho, la única forma de acceder a los métodos de la propia clase auto_ptr es utilizar el operador '.', el único de los operadores de este nivel que no es sobrecargable.
#include <cstdio>
#include <cstdlib>
#include <cstring>
class Cadena {
public:
Cadena(char * str = NULL) : s( NULL )
{ guardar( str ); }
Cadena(const Cadena &c)
{ guardar( c.get() ); }
~Cadena()
{ delete s; }
Cadena &operator=(const Cadena &c)
{ guardar( c.get() ); return * this; }
char * get()
{ return s; }
const char * get() const
{ return s; }
private:
char * s;
void guardar(const char *str)
{ delete s;
if ( str != NULL ) {
s = new char[ std::strlen(str) +1 ];
std::strcpy( s, str );
}
}
};
int main()
{
std::auto_ptrstr( new Cadena( "Hola" ) );
std::auto_ptrstr2( new Cadena( *str ) );
std::printf( "%s\n", str->get() );
return EXIT_SUCCESS;
}
auto_ptr proporciona los siguientes métodos:
get | Devuelve el puntero almacenado. |
release | Libera al autopuntero de liberar el objeto, y se resetea. |
reset | Libera el objeto apuntado y pasa a apuntar a otro. |
operator= | Hace un reset en el objeto a la izquierda de =, y un release en el de la derecha |
Así, si es necesario comprobar que, por ejemplo, el puntero almacenado no es NULL, tendría que hacerse:
if ( str.get() != NULL ) {release libera al auto puntero de todas sus obligaciones:
// más cosas...
}
int main()Con reset, primero se hace un release del objeto actual, y se almacena otro nuevo:
{
std::auto_ptrstr( new Cadena( "Hola" ) );
Cadena * str2;
str2 = str.get();
str.release();
std::printf( "%s\n", str2->get() );
delete str2;
return EXIT_SUCCESS;
}
int main()Finalmente, el operador '=' aplicado sobre un autopuntero, hace que la responsabilidad de liberar el objeto apuntado por el objeto a la derecha del '=' pase al de su izquierda, y sobre el objeto de la derecha ya mencionado se efectúe un release.
{
std::auto_ptrstr( new Cadena( "Hola" ) );
str.reset( new Cadena( "adiós" ) );
std::printf( "%s\n", str->get() );
return EXIT_SUCCESS;
}
int main()
{
std::auto_ptrstr2( new Cadena( "Hola" ) );
std::auto_ptrstr;
str = str2;
std::printf( "%s\n", str->get() );
return EXIT_SUCCESS;
}
Con el operador de copia de auto_ptr sucede lo mismo; esto es útil para el caso del retorno de las funciones, en las que se efectúan varias copias.
std::auto_ptrLos autopunteros son una posibilidad muy cómoda en la librería de C++, pero es que no es menos cierto que constituyen, junto con el resto de punteros inteligentes (smart pointers), una de esas pocas situaciones en las que está justificado sobrecargar los operadores * y ->, así, auto_ptr es una clase relativamente simple que puede crearese así:crearCadena(const char *str)
{
return std::auto_ptr( new Cadena( str ) );
}
int main()
{
std::printf( "%s\n", crearCadena( "Hola" )->get() );
return EXIT_SUCCESS;
}
template <typename T>Otros posibles punteros inteligentes son aquellos que emplean medidas más sofisticadas, como el conteo de referencias, etc.
class AutoPtr {
public:
AutoPtr(T * p = NULL) : ptr( NULL )
{ reset( p ); }
AutoPtr(AutoPtr &a)
{ this->reset( a.get() ); a.release(); }
~AutoPtr()
{ release(); }
T * get()
{ return ptr; }
const T * get() const
{ return ptr; }
T * operator ->()
{ return ptr; }
T &operator *()
{ return *ptr; }
void reset(T * p)
{ release(); ptr = p; }
void release()
{ delete ptr; ptr = NULL; }
AutoPtr &operator=(AutoPtr &a)
{ if ( &a == this ) {
this->reset( a.get() ); a.release();
}
return * this;
}
private:
T * ptr;
};