Sobrecarga de Operadores y E/S en...
Transcript of Sobrecarga de Operadores y E/S en...
Sobrecarga de Operadores y���E/S en C++
Antonio LaTorre de la Fuente
Índice • Sobrecarga de Funciones
• Sobrecarga de Operadores
• Entrada/Salida sobre streams básicos
• Entrada/Salida sobre streams de fichero
• Entrada/Salida sobre streams de cadena
Sobrecarga de Funciones
• Nuevo con respecto a C
• Funciones con el mismo nombre pero distinto prototipo: número y/o tipo de argumentos
• Útil si se quiere poder realizar una misma operación sobre distintos tipos de datos
Sobrecarga de Funciones void print (double number) {cout << number << endl;}
void print (int number) {cout << number << endl;}
void print (int number, string s = “hola”) {
cout << number << “, “ << s << endl;
}
Ambigüedad • ¿Qué pasa si incluimos en nuestro código…?
unsigned num = 3;
print (num);
• El compilador no sabe a qué función llamar: func_over.cc:18: error: call of overloaded ‘print(unsigned int&)’ is ambiguous
func_over.cc:6: note: candidates are: void print(int)
func_over.cc:7: note: void print(int, std::string)
func_over.cc:8: note: void print(double)
Ambigüedad • Se soluciona usando static_cast
unsigned num = 3;
print (static_cast<double>(num));
• ¿Qué pasaría si hiciéramos…?
int num = 3;
print (num);
Name Mangling • Estrategia que usa el compilador para poder
diferenciar dos funciones con el mismo nombre int f (void) {return 1;}
int f (int) {return 0;}
void g (void) {int i = f (), j = f (0);}
• Esto se traduciría en:
int __f_v (void) {return 1;}
int __f_i (int) {return 0;}
void __g_v (void) {int i = __f_v (), j = __f_i (0);}
Índice • Sobrecarga de Funciones
• Sobrecarga de Operadores
• Entrada/Salida sobre streams básicos
• Entrada/Salida sobre streams de fichero
• Entrada/Salida sobre streams de cadena
• Lista de operadores válidos en C++
• Se declaran de la siguiente forma:
tipo operator operador([argumentos])
Operadores en C++
+ - * / % ^ & |
~ ! , = < > <= >=
++ -- << >> == != && ||
+= -= *= /= %= ^= &= |=
<<= >>= [] () -> ->* new delete
Operadores en C++ • Hay algunos operadores especiales que no
pueden ser sobrecargados
• :: (resolución de ámbito)
• . (selección de un miembro)
• .* (selección de un miembro referenciado por un puntero)
• ?: (operador condicional)
• sizeof (tamaño de)
• typeid (identificación del tipo)
Precedencia de Operadores
• Los operadores en C++ tienen distinta precedencia, y a veces no es obvio
• Lo mejor es usar () en casa de no estar seguro
• Tabla de precedencias:
Inglés: http://www.cplusplus.com/doc/tutorial/operators/
Español: http://es.wikipedia.org/wiki/Operadores_en_C_y_C%2B%2B
Sobrecarga de operadores • Podemos sobrecargar tanto operadores unarios
como binarios
tipo_ret operator op_unario (tipo1 arg1);
tipo_ret operator op_binario (tipo1 arg1, tipo2 arg2);
• En este caso, ambos argumentos se muestran de manera explícita
Sobrecarga de operadores • Ambas notaciones son equivalentes
C operator- (C n) {…}
C operator- (C n, C m) {…}
int main (void) {
C a, b, c, d;���c = -b;���d = b – a;
}
int main (void) {
C a, b, c, d;���c = operator- (b);���d = operator- (b, a);
}
Sobrecarga de operadores • Si los operadores son métodos de una clase, uno
de los argumentos es implícito (aquél que invoca al operador)
class C {
public:
C operator- ();���C operator- (C);
}
Sobrecarga de operadores • Estas dos notaciones son equivalentes
int main (void) {
C a, b, c, d;
c = -b;
d = b – a;
}
int main (void) {
C a, b, c, d;
c = b.operator-();
d = b.operator-(a);
}
Sobrecarga de operadores • Ejemplo: Números Complejos
class Complejo {
public: ���Complejo (double r, double i) : real(r), imag(i) {}���…
private: ���double real;���double imag;
}
Operadores Aritméticos • Suma de números complejos:
Complejo Complejo::operator+ (const Complejo& z2) const {
Complejo res (0, 0);
res.real = this->real + z2.real;���res.img = this->img + z2.img;
return res;
}
Operadores Relacionales • Comparación de números complejos:
bool Complejo::operator== (const Complejo& z2) const {
return this->real == z2.real && this->imag == z2.imag;
}
Operadores de auto-incremento
• Son operadores unarios
• Permiten pre-incrementar y post-incrementar el valor de una variable
Pre-incremento: C C::operator++()
Post-incremento: C C::operator++(int x) Complejo Complejo::operator++() {
real += 1;���return *this;
}
Funciones “Amigas” • Son funciones ajenas a una clase que pueden
acceder a los elementos privados de esa clase
• Ejemplo:
• Tenemos dos clases: Vector y Matriz
• Queremos definir el producto vectorial entre ambas clases
• El contenido de los objetos es privado
Funciones “Amigas” class Vector {
friend operator* (const Matriz& matriz, const Vector& vector);
public:
…
private: ���int size;���int* data;
}
class Matriz {
friend operator* (const Matriz& matriz, const Vector& vector);
public:
…
private: ���int rows, columns;���int** data;
}
Vector operator* (const Matriz& matriz, const Vector& vector) {…}
Operadores de asignación • Se sobrecargan para evitar la copia binaria de
objetos class Vector {
public: ���Vector& operator= (const Vector& vector) {��� size = vector.size;��� data = new int[vector.size];��� for (unsigned i = 0; i < size; i++) ��� data[i] = vector.data[i];��� return *this;��� }
private: ���int size;��� int* data;
}
class Vector {
public: ���Vector& operator= (const Vector& vector) {��� size = vector.size;��� data = vector.data;��� return *this;��� }
private: ���int size;��� int* data;
}
Operadores de inserción y extracción
• Permiten escribir / leer en la salida / entrada estándar
• Hay que sobrecargar los operadores:
ostream& operator<< (ostream& os, const T& arg)
istream& operator>> (istream& is, T& arg)
• Podemos hacer que estos métodos sean amigos de nuestra clase o implementar un método público que imprima los datos
Operadores de inserción y extracción
class Complejo {
public: ��� void printComplejo(ostream & os);���…
private: ���double real;���double imag;
}
ostream& operator<< (ostream& os, const Complejo& c) {
c.printComplejo(os);���return os;
}
class Complejo {
public: ���friend ostream& operator<< (ostream& os, const Complejo& c);���…
private: ���double real;���double imag;
}
ostream& operator<< (ostream& os, const Complejo& c) {
os << c.real << …
}
Operador de Indexación • Es el operador []
• Es un operador binario y de clase: recibe el objeto donde se aplica y el índice facilitado
• Útil, p.ej., para acceso seguro a datos: int Vector::operator[] (int i) {
if (i >= 0 && i < this->size) ��� return data[i];���else {��� cout << “Posición incorrecta: “ << i << endl;��� return 0;���}
}
Conversión de tipos • Hay dos posibilidades para llevar a cabo
conversiones entre tipos definidos por el usuario:
• Los constructores de conversión
• Los operadores de conversión
• Son útiles para hacer, por ejemplo:
Complejo a (2, 3), b (0, 0);
b = a + 5;
Constructores de conversión
• Hay que añadir un constructor que nos permita crear el objeto a partir de un objeto del tipo que queramos convertir
class Complejo {
public: ���Complejo (double r, double i) : real(r), imag(i) {}���Complejo (int r) : real(r), imag(0) {}���Complejo operator+ (const Complejo& z2) const;���…
private: ���double real, imag;
}���
Complejo a (2, 3), b (0, 0); b = a + 5;
Operadores de conversión • Se usan para proporcionar una conversión de un
tipo de usuario a otro tipo (de usuario o no)
• Tiene la sintaxis:
C::operator T();
• Donde C es el tipo de usuario que queremos convertir y T es el tipo en el que queremos convertir C
Operadores de conversión class Complejo {
public: ���Complejo (double r, double i) : real(r), imag(i) {}���operator double() {return real;}���Complejo operator+ (const Complejo& z2) const;���…
private: ���double real, imag;
}
Complejo a (2, 3); double b = a + 5; double c = a.operator double() + 5; double d = static_cast<double>(a) + 5;
Ambigüedades en la conversión
class Complejo {
public: ���Complejo (double r = 0, double i = 0) ���: real(r), imag(i) {}������operator double() {return real;}���Complejo operator+ (const Complejo& z2) const;���…
private: ���double real, imag;
}���
Complejo a(2, 3), c(0,0); double b = 5; c = a + b;
El compilador no sabe si convertir “b” a “Complejo” y realizar la suma o convertir “a” a “double”, hacer la suma y convertir el resultado a Complejo.
Surgen si hay varias conversiones implícitas posibles
Operador de llamada a función
• Es el operador ()
• Siempre debe ser definido dentro de una clase
• x (y, z) se interpreta como x.operator (y, z)
• Útil para implementar callbacks (pasar código como argumento de una función). Los denominados Objetos Función
• También para el acceso a matrices multidimensionales
Operador de llamada a función class Inicializar {���
private: ��� double val;���public: ��� Inicializar (double x = 0) : val(x) {}��� void operator() (Vector& v);
}; void Inicializar::operator() (Vector& v) { for (unsigned i = 0; i < vector.size(); i++) v[i] = this->val;
} int main (void) { Vector v(5); Inicializar ini(1); ini(v);
}
Operador new • Al reservar memoria con new y new[] se invoca a las
funciones operator new y operator new[]:
void* operator new (size_t tamaño); void* operator new[] (size_t tamaño);
• Estas funciones reservan y memoria y luego llaman el constructor correspondiente
• Se pueden sobrecargar para una clase C:
void* C::operator new (size_t tamaño); void* C::operator new[] (size_t tamaño);
• Son funciones estáticas aunque no se declaren como tal (static), porque se invocan antes que el constructor
• Primero se busca en la clase (y clases base) y luego la global
Operador new void* Vector::operator new (size_t tam) { reservarMemoria (tam, 0);
} void* Vector::operator new[] (size_t tam) { reservarMemoria (tam, 0);
} void* Vector::reservarMemoria (size_t tam, char c) { void* p = malloc (tam);
if (p == 0) { cout << “Error reservando memoria.” << endl; exit(-1); }
memset (p, c, tam); return p;
}
Operador delete • Al liberar memoria con delete y delete[] se invoca a las
funciones operator delete y operator delete[]:
void operator delete (void*); void operator delete[] (void*);
• Estas funciones llaman al destructor y luego liberan la memoria del objeto
• Se puede sobrecargar para una clase C:
void C::operator delete (void*, [size_t]); void C::operator delete[] (void*, [size_t]);
• Son funciones estáticas aunque no se declaren como tal (static), porque se invocan después del destructor
• Primero se busca en la clase (y clases base) y luego la global
Operador delete void Vector::operator delete (void* p, size_t tam) { if (p) memset (p, 0, tam); free (p);
} void Vector::operator delete[] (void* p, size_t tam) { if (p) memset (p, 0, tam); free (p);
}
Índice • Sobrecarga de Funciones
• Sobrecarga de Operadores
• Entrada/Salida sobre streams básicos
• Entrada/Salida sobre streams de fichero
• Entrada/Salida sobre streams de cadena
Visión General de la E/S en C++
• Streams y Buffers • Especializaciones según el tipo de operación y el origen/destino de los datos
Clases istream y ostream • Derivan de la clase ios
• Deben ser conectadas a un buffer (del tipo que sea)
• cin y cout son de tipo istream y ostream
• Normalmente, para hacer E/S sobre ficheros o cadenas se usan las clases especializadas, pero se podrían usar estas clases básicas
• Para usarlas: #include <iostream>
Clases istream y ostream • Los Manipuladores nos permiten cambiar las
opciones de formato de los streams (#include <iomanip>)
• Son funciones especialmente diseñadas para trabajar junto con los operadores de inserción (<<) y extracción (>>)
• Se modifica el formato encadenándolos al objeto que representa el stream
cout << hex << showbase << 20 << endl;
Clases istream y ostream • Manipuladores sin parámetros: ���
(no)boolapha: Mostrar booleanos como true y false ���(no)showbase: Mostrar prefijo de tipo (0x, 0, etc.) ���(no)showpoint: Forzar la escritura del punto decimal ���(no)showpos: Forzar la escritura de un ‘+’ para valores positivos���(no)skipws: Permite ignorar caracteres de espaciado���(no)unitbuf: Forzar el vaciado del buffer tras cada operación���(no)uppercase: Mostrar caracteres alfabéticos en mayúsculas���dec, hex, oct: Cambiar base (decimal, hexadecimal y octal) ���fixed, scientific: Usar notación de coma fija o científica���internal, left, right: Alinear el texto���endl, ends, flush: Finalizar línea, cadena y vaciar buffer ���
Clases istream y ostream • Manipuladores con parámetros���
setprecision (int): Fija la precisión en coma flotante���setw (int): Fija el tamaño de un campo (que se rellena��� automáticamente en caso de ser necesario) ���setfill (char): Selecciona el carácter para el relleno���setbase (int): Fija la base numérica a usar (8, 10 ó 16) ���setiosflags (mask): Fija cualquiera de los anteriores flags���resetiosflags (mask): Reinicia los flags a los valores por defecto���
• Otra forma de hacer lo mismo • Manipuladores sin parámetros: métodos setf y unsetf • Manipuladores con parámetros: métodos propios (fill, width,
precision, etc.)
Clases istream y ostream • Ejemplos cout << fixed << setprecision(5) << setw(10) << setfill(‘*’) << 2.87 << endl;
cout.setf(ios::fixed); cout.precision(5); cout.width(10); cout.fill(‘*’); cout << 2.87 << endl;
• Ambas imprimen el mismo resultado���***2.87000
Clase ostream • Permite hacer operaciones de escritura con y sin
formato
• Sin formato: put y write
• Con formato: operator<<
• Permite manejar distintas situaciones de error
• También se puede posicionar el puntero de escritura en posiciones determinadas del flujo de salida
Clase ostream • La sobrecarga del operador de inserción permite
realizar escritura con formato
• Se puede sobrecargar para cualquier tipo
ostream& operator<< (ostream& os, const T& data)
• Y usar los manipuladores vistos anteriormente
Clase ostream • El método put permite escribir un carácter en el
stream
ostream& put (char ch)
• El método write permite escribir n caracteres de un array en el stream
ostream& write (const char* pch, int n)
• El método flush fuerza el vaciado del buffer asociado al stream
ostream& flush ()
Clase ostream • El método tellp permite obtener la posición en
el stream de salida (principalmente ficheros)
pos_type tellp ()
• Los métodos seekp permiten cambiar la posición de escritura en relación a la posición actual…
ostream& seekp (pos_type pos)
• …o en relación a una posición de referencia
ostream& seekp (off_type des, ios_base::seekdir pos)
Clase ostream • Ejemplos: const char* cadena = “Hola mundo”; cout << cadena << endl; cout.write (cadena, 10); cout.write (“\n”, 1); cout.flush (); for (unsigned i = 0; i < 10; i++) cout.put (cadena[i]);
cout.put (‘\n’); cout.flush ();
Clase istream • Permite realizar operaciones de lectura con y sin
formato:
• Sin formato: get, getline, read, ignore, peek, gcount, unget, putback
• Con formato: operator>>
• Al igual que la clase ostream, permite gestionar errores y reposicionar el puntero de lectura
Clase istream • La sobrecarga del operador de extracción
permite realizar lectura con formato
• Se puede sobrecargar para cualquier tipo
istream& operator>> (istream& is, T& data)
• Y usar los manipuladores vistos anteriormente (aunque algunos pueden tener sentido únicamente para la clase ostream)
Clase istream • El método get permite leer un carácter del
stream:
istream& get (char& ch)
• El método getline permite leer una línea, con una longitud máxima hasta encontrar un delimitador
istream& getline (char* ch, int n, char delim = “\n”)
• El método read permite leer n caracteres
istream& read (char* ch, int n)
Clase istream • El método ignore extrae y descarta una cadena
de hasta n caracteres, mientras no encuentre un delimitador dado
istream& ignore (int n = 1, int delim = eof ())
• El método peek devuelve el siguiente carácter del stream sin extraerlo
int peek () const
Clase istream • El método gcount devuelve el número de
caracteres leídos en la última extracción sin formato
int gcount () const
• El método unget devuelve el último carácter extraído al stream
istream& unget ()
• El método putback devuelve c al stream
istream& putback (char c)
Clase istream • El método tellg permite obtener la posición en el
stream de entrada (principalmente ficheros)
pos_type tellg ()
• Los métodos seekg permiten cambiar la posición de lectura en relación a la posición actual…
istream& seekg (pos_type pos)
• …o en relación a una posición de referencia
istream& seekg (off_type des, ios_base::seekdir pos)
Clase istream • Ejemplos char c, cad [100], cad2[10]; cin >> cad; // Introducimos por teclado “Hola mundo” cin.get(c); // cad = “Hola”, c=‘ ‘ cin.getline (cad, 100, ‘\n’); // cad=“mundo” cin.read (cad2, 10); // Introducimos por teclado // “Hola mundo cruel.Adios”
cout << cin.gcount() << endl; // cad2=“Hola mundo”. Imprime 10 cin.ignore (10, '.'); // Ignora “cruel.” cin.putback('!'); // Inserta ‘!’ en el stream cin >> cad; // cad=“!Adios!”
Clase iostream
• Clase derivada tanto de istream como de ostream
• Proporciona acceso de lectura/escritura a streams
• Presenta los mismos métodos que las clases de las que deriva
Gestión de errores • Los streams manejan 4 bits de error:
• eofbit: Se alcanzó el final del fichero en una operación de E/S
• failbit: El método de E/S falló
• badbit: Hubo un problema con el stream al realizar la operación
• goodbit: Ninguno de los anteriores bits está activado
• Los métodos eof, fail, bad y good devuelven el valor de estos bits
Índice • Sobrecarga de Funciones
• Sobrecarga de Operadores
• Entrada/Salida sobre streams básicos
• Entrada/Salida sobre streams de fichero
• Entrada/Salida sobre streams de cadena
E/S sobre streams de fichero ���
• Proporcionada por el fichero de cabecera: #include <fstream>
• Provee de las clases ofstream (salida), ifstream (entrada) y fstream (entrada/salida)
• Distintos modos para abrir un fichero:
ios::in, ios::out, ios::trunc, ios::app, ios::binary, ios::ate
• El fichero se puede abrir en el constructor o con el método open
Métodos de apoyo • El método eof indica si se alcanzó el final del
fichero: bool eof () const
• El método is_open indica si existe un fichero abierto asociado con el stream bool is_open () const
• El método close cierra el fichero abierto y lo desliga del stream bool close ()
Clase ofstream • Clase derivada de ostream especializada en
realizar escrituras sobre ficheros
• El fichero se puede abrir al construir un objeto de tipo ofstream o con el método open
ofstream (const char* nombre_fichero, ��� ios_base::openmode modo = ios::out | ios::trunc)
void open (const char* nombre_fichero, ��� ios_base::openmode modo = ios::out | ios::trunc)
Clase ofstream • Ejemplo: ofstream of (”fichero.txt"); of.write ("Hola mundo\n", 11); of << "Adios mundo" << endl; for (char c = 'a'; c <= 'z'; c++) { of.put(c); of << ", "; } of << endl; of.close();
Clase ifstream • Clase derivada de istream especializada en
realizar lecturas desde ficheros
• El fichero se puede abrir al construir un objeto de tipo ifstream o con el método open
ifstream (const char* nombre_fichero, ��� ios_base::openmode modo = ios::in)
void open (const char* nombre_fichero, ��� ios_base::openmode modo = ios::in)
Clase ifstream • Ejemplo: ifstream f (”fichero.txt"); char buffer[100]; f.getline (buffer, 100); cout << buffer << endl; f.ignore(100, '\n'); for (unsigned i = 1; i <= 26; i++) { char c; f >> c; cout << c << endl; f.ignore(2, ',');
}
Clase fstream • Combina las funcionalidades de ifstream y
ofstream
• El fichero se puede abrir al construir un objeto de tipo fstream o con el método open
fstream (const char* nombre_fichero, ��� ios_base::openmode modo = ios::in | ios::out)
void open (const char* nombre_fichero, ��� ios_base::openmode modo = ios::in | ios::out)
E/S usando Registros • Podemos escribir estructuras de datos complejas
• Los datos se guardan en formato binario, en lugar de escribirse carácter a carácter
• Podemos hacerlo con los métodos read y write
• Requiere una conversión de la estructura en un array de caracteres
• Uso de reinterpret_cast<char*>()
E/S usando Registros struct nodo_agenda {���
char nombre[30];���char direccion[40];���long telefono;
}; nodo_agenda persona; cin.getline (30, persona.nombre); cin.getline (40, persona.direccion); cin >> persona.telefono; ofstream ofs (“agenda”); ofs.write (reinterpret_cast<char*>(&persona), sizeof(nodo_agenda)); ofs.close ();
Índice • Sobrecarga de Funciones
• Sobrecarga de Operadores
• Entrada/Salida sobre streams básicos
• Entrada/Salida sobre streams de fichero
• Entrada/Salida sobre streams de cadena
La clase String • Clase que sirve para trabajar con cadenas de
caracteres
• También almacena el tamaño de la cadena
• Gestiona automáticamente la memoria dinámica asociada a la cadena
• Facilita las operaciones más habituales con cadenas de caracteres
• Hace falta incluir el siguiente fichero de cabecera: #include <string>
La clase String • Cómo construimos un objeto de tipo string:
string cad1; // Construye una cadena vacía string cad2 (cad1); // Constructor de copia char arr[] = “Hola mundo”); string cad3 (arr); // Constructor a partir de un array de char
• Podemos consultar el tamaño de una cadena: cout << “El tamano de cad1 es: “ << cad1.size() << endl;
• Podemos acceder carácter a carácter: for (unsigned i = 0; i < cad1.size(); i++) cout << cad1[i];
La clase String • Podemos asignar cadenas sin miedo (la memoria
la gestiona la propia clase): string cad1 = “Hola mundo”; string cad2 = “Adios mundo”; cad2 = cad1;
• Si nos interesa, podemos recuperar el array de caracteres asociado al objeto string
string cad1 = “abcdefgh”; const char* arr1 = cad1.c_str(); // Sí añade ‘\0’ const char* arr2 = cad1.data(); // No añade ‘\0’ char buffer[80]; cad1.copy (buffer, cad1.size(), 0);
La clase String • También podemos realizar las operaciones de
comparación habituales: ==, !=, <, >, <= y >=
• Se comparan letra por letra y se usa el orden alfabético
string str1 = “abcdefg”, str2 = “Abcdefg”; if (str1 < str2) cout << str1 << endl;
else cout << str2 << endl;
La clase String • Se pueden insertar caracteres en una cadena: string cad = “Hola”; cad += “ mundo”; // Añade “ mundo” al final cad.append (“ cruel”); // Añade “ cruel” al final cad.insert (11, “nada “); // Inserta “nada “ en la posición 11 cout << cad << endl; // Imprime “Hola mundo nada cruel”;
• Y concatenar dos cadenas: string cad1 = “Hola”; string cad2 = “ mundo”; string cad3 = cad1 + cad2; cout << cad3 << endl; // Imprime “Hola mundo”
La clase String • También podemos buscar subcadenas: int pos; string cad = “abcdefghijk”; pos = cad.find (“efg”); // pos = 4 pos = cad.find (“i”); // pos = 8
• Reemplazar subcadenas: cad.replace (cad.find (“efg”), 2, “ zyx ”); // cad = “abcd zyx ghijk”
• Y extraer subcadenas: string cad1 = “Esto es una cadena”; string cad2 = cad1.substr (12, 6); // cad2 = “cadena”
Entrada/Salida sobre streams de cadena
• Proporcionada por el fichero de cabecera: #include <sstream>
• Provee de las clases ostringstream (salida), istringstream (entrada) y stringstream (e/s)
• Permite realizar operaciones de E/S sobre objetos de tipo string (parecido al sprintf de C)
• Se pueden usar los métodos de las clases básicas istream, ostream y stream (respectivamente)
Entrada/Salida sobre streams de cadena
• Definen dos métodos adicionales:
string str ( ) const��� void str (const string & s)
• Permiten recuperar e inicializar la cadena asociada al stream
• También se les puede asociar una cadena en el constructor
istringstream (const string& str, ��� ios_base::openmode modo = ios_base::in)
Ejemplo con ostringstream ostringstream buffer; string nombre = "Antonio"; int edad = 27; char aficion[] = "leer"; buffer << "Hola. Me llamo " << nombre << ", tengo " << edad << " tacos y me gusta " << aficion << endl;
cout << buffer.str() << endl;
Ejemplo con istringstream string nombre2; int edad2 = 0; char aficion2[80]; istringstream buffer2 ("Antonio , 27 , leer"); buffer2 >> nombre2; // nombre2 = “Antonio” buffer2.ignore(100, ','); // Descartamos la , buffer2 >> edad2; // edad2 = 27 buffer2.ignore(100, ','); // Descartamos la , buffer2 >> aficion2; // aficion2 = “leer”