2011-10-26 40 views
7

我一直在试验C++,并且遇到了一个我不知道如何解决的问题。C++复制一个流对象

基本上,我发现你不能复制流(见Why copying stringstream is not allowed?),这也适用于'包装'它们的对象。例如:

  • 我创建了一个类型为stringstream的数据成员的类。
  • 我创建了这个类的一个对象。
  • 我试图复制对象,例如“TestObj t1; TestObj t2; t1 = t2;”

这会导致错误C2249:

'的std :: basic_ios < _Elem,_Traits> ::运算符=':没有可访问的路径在虚拟基“的std :: basic_ios <声明为专用构件_Elem,_Traits>”

所以我的问题是:我(最好容易)如何可以复制具有类型的数据成员*流对象?

完整的示例代码:

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

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

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

    system("pause"); 
    return 0; 
} 

在此先感谢。

UPDATE

我已经成功地解决了这个问题下面感谢的答案。我所做的就是声明流对象一次,然后使用包装对象中的指针(例如TestStream)简单地引用它们。所有其他具有私人拷贝构造函数的对象也是如此。

+0

因为你似乎已经完成了你的功课,这个相关的问题。你为什么需要这种行为?读/写? –

回答

0

有两件事情可以做,都涉及小心关于谁拥有对象:

  1. 存储到流的引用,并确保对象不出去的范围,只要你们的这些班都在附近。

  2. 复制指针周围,并确保只有当您的最后一个类与指向的流对象完成删除。

虽然我个人比较喜欢参考方法,但它们都是等价的。

+0

如果你选择第二个并且拥有一个支持C++ 11的编译器,那么你可以使用智能指针。如果你不使用这样的编译器,你可以使用boost库或者自己编写一个智能指针类。 – Darokthar

+2

@Darokthar同意。这是'boost :: shared_ptr'是合适的(并且绝对比任何其他选择更好)。 –

1

This article提供了这样做的方法。不过要注意的有趣的总结:

总的来说,创建一个流是不平凡的,如果你真的需要一个流对象的副本应该只 做的副本。在很多情况下,使用引用或指针来流式传输对象 更合适,或者在两个流之间共享流缓冲区。

4

您不允许复制流的原因是it doesn't make sense to copy a stream。如果你解释你想要做的是什么,肯定有办法做到这一点。如果您需要可复制的大量数据,请使用字符串。但是流更像是一个连接而不是一个字符串。

+0

如果我想复制_连接_怎么办?也就是说,我正在阅读一个文件,并且达到了我知道我将在稍后阅读的一行内容(或者一组行以大量存储)。复制流将会很方便,以便复制位于流中的该位置,然后能够返回到该位置。 – VF1

+0

@ VF1这一般没有意义。这对于某些类型的流可能是有意义的,但作为流的一般特征,这是不明智的。 –

+0

我想我找到了我需要的东西(是的,它仅适用于istreams) - 将当前流位置存储为'tellg'并稍后使用'seekg'。 – VF1

1

当然,您必须自己编写复制构造函数和复制赋值运算符。

接下来,您必须决定您希望副本具有哪些语义。所以:

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

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

如果你想有一个浅拷贝,("foobar"),那么你需要共享的TestStream多个实例之间的stringstream的对象,可能最好使用一个shared_ptr

如果你想有一个深拷贝("foo"),那么你可以复制这样的:

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

或使用您链接到问题的变种之一。

这涵盖了你在中间写入的一个字符串流,当你拿这个副本的时候写入。如果你在中读取,或者如果你正在编写,但由于使用了seekp,你可能没有写完,那么你需要捕获当前的读/写位置以及字符串流中的数据,与​​一起使用。

您还可能要在整个流的格式状态复制,等等,这是什么copyfmt做,甚至错误标志(rdstate - copyfmt留给他们单独)。

0

为了测试C++这里各种写操作的性能是编译你的机器上测试,写操作有和没有缓冲的几个方法代码:

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(); 
}