2011-10-26 5 views
7

He estado experimentando con C++, y he encontrado un problema que no sé cómo resolver.C++ copie un objeto de secuencia

Básicamente, descubrí que no puede copiar flujos (consulte Why copying stringstream is not allowed?), y eso también se aplica a los objetos que los 'envuelven'. Por ejemplo:

  • Creo una clase con un miembro de datos del tipo stringstream.
  • Creo un objeto de esta clase.
  • Intento copiar el objeto, por ejemplo, "TestObj t1; TestObj t2; t1 = t2;"

Esto hace que el C2249 de error:

'std :: basic_ios < _Elem, _Traits> :: = operador': hay un camino accesible a los miembros privados declarada en base virtual 'std :: basic_ios < _Elem, _Traits>'

Así que mi pregunta es: ¿cómo puedo (preferiblemente fácilmente) copiar objetos que tienen los miembros de datos de este tipo * stream?

completo código de ejemplo:

#include <iostream> 
#include <string> 
#include <sstream> 

class TestStream 
{ 
public: 
    std::stringstream str; 
}; 

int main() 
{ 
    TestStream test; 
    TestStream test2; 
    test = test2; 

    system("pause"); 
    return 0; 
} 

Gracias de antemano.

ACTUALIZACIÓN

he logrado solucionar este problema gracias las respuestas a continuación. Lo que he hecho es declarar los objetos de transmisión una vez y luego simplemente hacer referencia a ellos utilizando punteros en los objetos de envoltura (por ejemplo, TestStream). Lo mismo ocurre con todos los demás objetos que tienen constructores de copia privada.

+0

Ya que parece has hecho tu tarea, la pregunta vinculada. ¿Por qué necesita este comportamiento? ¿leyendo escribiendo? –

Respuesta

0

Hay dos cosas que puede hacer, ambos implican tener cuidado con quién es el propietario del objeto:

  1. tienda una referencia a una corriente, y asegúrese de que el objeto no sale del alcance, siempre y cuando estas clases tuyas están alrededor.

  2. copie los punteros, y asegúrese de eliminar solo cuando la última de sus clases haya finalizado con el objeto de secuencia señalado.

Ambos son equivalentes, aunque personalmente prefiero el enfoque de referencia.

+0

Si elige el segundo y tiene un compilador capaz de C++ 11, podría usar punteros inteligentes. Si no utiliza dicho compilador, puede usar la biblioteca de impulso o escribir una clase de puntero inteligente usted mismo. – Darokthar

+2

@Darokthar estuvo de acuerdo. Este es un caso donde 'boost :: shared_ptr' es apropiado (y definitivamente preferible a cualquiera de las alternativas). –

1

This article proporciona formas de hacerlo. Tenga en cuenta sin embargo el interesante resumen:

En resumen, la creación de una copia de una corriente no es trivial y sólo se debe hacer si realmente necesita una copia de un objeto de secuencia. En muchos casos, es más apropiado utilizar referencias o punteros para transmitir objetos en su lugar, o para compartir un almacenamiento intermedio de flujo entre dos flujos.

4

La razón por la que no se le permite copiar una corriente es que it doesn't make sense to copy a stream. Si explica qué es lo que está tratando de hacer, ciertamente hay una manera de hacerlo. Si quieres una cantidad de datos que puedes copiar, usa una cadena. Pero una transmisión es más como una conexión que una cadena.

+0

¿Qué sucede si quiero copiar _la conexión_? Es decir, estoy leyendo un archivo y alcanzo una línea que sé que tendré que leer más tarde (o un grupo de líneas para almacenar). Sería conveniente copiar la secuencia, para que la copia se encuentre en esa ubicación en la transmisión y luego poder volver a ella más tarde. – VF1

+0

@ VF1 Eso no tiene sentido en general. Eso podría tener sentido para algunos tipos de flujos, pero como característica general de un flujo, no es sensato. –

+0

Creo que encontré lo que necesito (sí, solo para isotermas): almacenar la posición de la corriente actual con 'tellg' y usar' seekg' más tarde. – VF1

1

Ciertamente tiene que escribir el constructor de copias y el operador de asignación de copias usted mismo.

A continuación, debe decidir qué semántica desea que tenga la copia. Por lo tanto:

TestStream test; 
TestStream test2; 
test2 << "foo" 
test = test2; 
test << "bar"; 

test2.str.str(); // should this be "foo" or "foobar" ? 

Si desea una copia superficial, ("foobar"), entonces tienes que compartir el objeto stringstream entre varias instancias de TestStream, probablemente mejor usar un shared_ptr para eso.

Si desea una copia profunda ("foo"), entonces se podría copiar la siguiente manera:

TestStream(const TestStream &rhs) : str(rhs.str.str()) {} 

o utilizar una de las variantes de la pregunta que hace referencia.

Eso cubre una secuencia de cadenas a la que se encuentra en medio de escribiendo cuando toma la copia. Si está en el medio de leyendo de él, o si está escribiendo pero puede que no esté escribiendo hasta el final debido al uso de seekp, entonces necesita capturar las posiciones de lectura/escritura actuales, así como datos en el flujo de cadenas, que se hace con tellg/tellp.

También puede ser que desee copiar todo el estado de formato de la corriente, y así sucesivamente, que es lo que hace copyfmt, e incluso los indicadores de error (rdstate - copyfmt los deja solos).

0

Para probar el rendimiento de varias operaciones de escritura en C++ aquí es un código que recoge en su máquina y las pruebas de las operaciones de escritura con y sin búfer con varios métodos:

Link

#include <stdio.h> 
#include <cstring> 
#include <iostream> 
#include <fstream> 
#include <chrono> 

#define TOCOUT(output) \ 
    if(!outputToCout) { \ 
     buf = output##_t.rdbuf(); \ 
    } else { \ 
     buf = std::cout.rdbuf(); \ 
    } \ 
    std::ostream output(buf); 

void fstreamBufferTest(){ 

    const bool outputToCout = true; 

    const unsigned int multiplyStep = 1<<2; 
    const unsigned int startLength = 1<<2; 
    const unsigned int stopLength = 1<<24; 

    const unsigned int writeNTimes = 1; // Averaging over some many times! 
    const unsigned int fileLength = 1<< 30; //104857600=100mb, 314572800=300mb , 1<< 30 =1GB 
    std::string add = "1000.txt"; 
    unsigned int loops, restBytes; 


    std::streambuf * buf; 

    std::ofstream output1_t("FStreamTest-FstreamBuffering-OwnBufferSet-"+add); 
    TOCOUT(output1); 
    output1 << "#Buffer Length \tTimeToWrite \tWriteSpeed [mb/s]" << std::endl; 

    std::ofstream output2_t("FStreamTest-ManualBuffering-StdStreamBuffer-"+add); 
    TOCOUT(output2); 
    output2 << "#Buffer Length \tTimeToWrite \tWriteSpeed [mb/s]" << std::endl; 

    std::ofstream output3_t("FStreamTest-ManualBuffering-NoInternalStreamBuffer-"+add); 
    TOCOUT(output3); 
    output3 << "#Buffer Length \tTimeToWrite \tWriteSpeed [mb/s]" << std::endl; 

    std::ofstream output4_t("FStreamTest-NoManualBuffering-NoInternalStreamBuffer-"+add); 
    TOCOUT(output4); 
    output4 << "#Buffer Length \tTimeToWrite\tWriteSpeed [mb/s]" << std::endl; 

    std::ofstream output5_t("FStreamTest-NoManualBuffering-StdStreamBuffer-"+add); 
    TOCOUT(output5); 
    output5 << "#Buffer Length \tTimeToWrite \tWriteSpeed [mb/s]" << std::endl; 

    // To Cout 


    typedef std::chrono::duration<double> fsec; 
    typedef std::chrono::high_resolution_clock Clock; 



    // Test Data for the Buffer 
    bool removeFile = true; 
    char value = 1; 
    char *testData = new char[fileLength]; // Just Garbage 1GB!! 
    std::memset(testData,value,fileLength); 

    // Preallocate file; 
    if(!removeFile){ 
     std::fstream stream; 
     stream.open("test.dat", std::ios::binary | std::ios::trunc | std::ios::out); 
     for(int i = 0; i < writeNTimes; i++){ 
       stream.write(testData, fileLength); 
     } 
     stream.close(); 
    }else{ 
     if(remove("test.dat") == 0){ 
      std::cout << "File deleted at start!" << std::endl; 
     } 
    } 

    for(unsigned int bufL = startLength; bufL <= stopLength; bufL = bufL * multiplyStep){ 

     // First Test with Fstream Buffering! 
     { 
      std::cout << "Doing test: FStream Buffering: " << bufL <<std::endl; 
      char * buffer = new char[bufL]; 
      //open Stream 
      std::fstream stream; 
      stream.rdbuf()->pubsetbuf(buffer, bufL); 
      stream.open("test.dat", std::ios::binary | std::ios::trunc | std::ios::out); 

      // Write whole 1gb file! we have fstream buffering the stuff 
      auto t1 = Clock::now(); 
      for(int i = 0; i < writeNTimes; i++){ 
       stream.write(testData, fileLength); 
      } 
      stream.close(); 
      auto t2 = Clock::now(); 

      //Calculate timing 
      fsec time = (t2 - t1)/writeNTimes; 
      output1 << bufL << "\t" << time.count() <<"\t" << (fileLength/time.count())/(1024*1024) << std::endl; 

      delete buffer; 
      if(removeFile){ 
       if(remove("test.dat") != 0){ 
        std::cerr << "File not deleted" << std::endl; 
       }; 
      } 
     } 

     // Second Test with Manual Buffering! 
     { 
      std::cout << "Doing test: Manual Buffering: " << bufL <<std::endl; 
      // Calculate the loops to write fileLength 
      loops = fileLength/bufL; 
      restBytes = fileLength % bufL; 

      //open Stream 
      std::fstream stream; 
      stream.open("test.dat", std::ios::binary | std::ios::trunc | std::ios::out); 
      // TODO stream buf -> 0 

      // Write 1GB File in loops of bufL 
      auto t1 = Clock::now(); 
      for(int i = 0; i < writeNTimes; i++){ 
       for(int i = 0; i < loops; i++){ 
        stream.write(testData, bufL); 
       } 
       stream.write(testData, restBytes); 
      } 
      stream.close(); 
      auto t2 = Clock::now(); 

      //Calculate timing 
      fsec time = (t2 - t1)/writeNTimes; 
      output2 << bufL << "\t" << time.count() <<"\t" << (fileLength/time.count())/(1024*1024) << std::endl; 
      if(removeFile){ 
       if(remove("test.dat") != 0){ 
        std::cerr << "File not deleted" << std::endl; 
       }; 
      } 
     } 

     // Second Test with Manual Buffering! 
     { 
      std::cout << "Doing test: Manual Buffering (no internal stream buffer): " << bufL <<std::endl; 
      // Calculate the loops to write fileLength 
      loops = fileLength/bufL; 
      restBytes = fileLength % bufL; 

      //open Stream 
      std::fstream stream; 
      stream.open("test.dat", std::ios::binary | std::ios::trunc | std::ios::out); 
      stream.rdbuf()->pubsetbuf(0, 0); 

      // Write 1GB File in loops of bufL 
      auto t1 = Clock::now(); 
      for(int i = 0; i < writeNTimes; i++){ 
       for(int i = 0; i < loops; i++){ 
        stream.write(testData, bufL); 
       } 
       stream.write(testData, restBytes); 
      } 
      stream.close(); 
      auto t2 = Clock::now(); 

      //Calculate timing 
      fsec time = (t2 - t1)/writeNTimes; 
      output3 << bufL << "\t" << time.count() <<"\t" << (fileLength/time.count())/(1024*1024) << std::endl; 
      if(removeFile){ 
       if(remove("test.dat") != 0){ 
        std::cerr << "File not deleted" << std::endl; 
       }; 
      } 
     } 


     { 
      std::cout << "Doing test: No manual Buffering (no internal stream buffer): " << bufL <<std::endl; 
      // Calculate the loops to write fileLength 
      loops = fileLength/bufL; 
      restBytes = fileLength % bufL; 

      //open Stream 
      std::fstream stream; 
      stream.open("test.dat", std::ios::binary | std::ios::trunc | std::ios::out); 
      stream.rdbuf()->pubsetbuf(0, 0); 

      // Write 1GB File in loops of bufL 
      auto t1 = Clock::now(); 
      for(int i = 0; i < writeNTimes; i++){ 
       stream.write(testData, fileLength); 
      } 
      stream.close(); 
      auto t2 = Clock::now(); 

      //Calculate timing 
      fsec time = (t2 - t1)/writeNTimes; 
      output4 << bufL << "\t" << time.count() <<"\t" << (fileLength/time.count())/(1024*1024) << std::endl; 
      if(removeFile){ 
       if(remove("test.dat") != 0){ 
        std::cerr << "File not deleted" << std::endl; 
       }; 
      } 
     } 

     { 
      std::cout << "Doing test: No manual Buffering (std stream buffer): " << bufL <<std::endl; 
      //Calculate the loops to write fileLength 
      loops = fileLength/bufL; 
      restBytes = fileLength % bufL; 

      //open Stream 
      std::fstream stream; 
      stream.open("test.dat", std::ios::binary | std::ios::trunc | std::ios::out); 

      // Write 1GB File in loops of bufL 
      auto t1 = Clock::now(); 
      for(int i = 0; i < writeNTimes; i++){ 
       stream.write(testData, fileLength); 
      } 
      stream.close(); 
      auto t2 = Clock::now(); 

      //Calculate timing 
      fsec time = (t2 - t1)/ writeNTimes; 
      output5 << bufL << "\t" << time.count() <<"\t" << (fileLength/time.count())/(1024*1024) << std::endl; 
      if(removeFile){ 
       if(remove("test.dat") != 0){ 
        std::cerr << "File not deleted" << std::endl; 
       }; 
      } 
     } 



    } 



} 

int main() { 
fstreamBufferTest(); 
}