【问题标题】:C++ copy a stream objectC++ 复制一个流对象
【发布时间】:2011-10-26 14:11:49
【问题描述】:

我一直在尝试使用 C++,但遇到了一个我不知道如何解决的问题。

基本上,我发现您无法复制流(请参阅Why copying stringstream is not allowed?),这也适用于“包装”它们的对象。例如:

  • 我创建了一个具有字符串流类型数据成员的类。
  • 我创建了这个类的一个对象。
  • 我尝试复制对象,例如“TestObj t1; TestObj t2; t1 = t2;”

这会导致错误 C2249:

'std::basic_ios<_elem>::operator =' : 在虚拟基'std::basic_ios<_elem>'中声明的私有成员没有可访问的路径

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

完整示例代码:

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

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

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

    system("pause");
    return 0;
}

提前致谢。

更新

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

【问题讨论】:

  • 既然您似乎已经完成了作业,那么链接的问题。为什么你需要这种行为?读/写?

标签: c++ stream compiler-errors


【解决方案1】:

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

【讨论】:

  • 如果我想复制连接怎么办?即,我正在读取一个文件,并且我到达了我知道以后必须读取的行(或要存储的大行组)。复制流会很方便,这样副本就在流中的那个位置,然后可以稍后再回来。
  • @VF1 这通常没有意义。这可能对某些类型的流有意义,但作为流的一般特征,它是不明智的。
  • 我想我找到了我需要的东西(是的,它仅适用于 istream) - 使用 tellg 存储当前流位置,稍后使用 seekg
  • @VF1 注意 seekg() 和 seekp() 有时会一起移动位置,有时不会,这取决于您使用的流类型。
【解决方案2】:

This article 提供了执行此操作的方法。但请注意有趣的总结:

总而言之,创建流的副本并非易事,而且应该只 如果您确实需要流对象的副本,则可以这样做。在很多情况下, 使用引用或指针来流对象更合适 相反,或者在两个流之间共享一个流缓冲区。

【讨论】:

    【解决方案3】:

    当然要自己写拷贝构造函数和拷贝赋值运算符。

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

    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,您可能没有写入到最后,那么您需要捕获当前读取/write 位置以及字符串流中的数据,您可以使用 tellg/tellp

    您可能还想复制流的格式状态,等等,这是 copyfmt 所做的,甚至是错误标志(rdstate -- copyfmt 不理会它们)。

    【讨论】:

      【解决方案4】:

      您可以做两件事,都需要小心谁拥有该对象:

      1. 存储对流的引用,并确保只要您的这些类还在,对象就不会超出范围。

      2. 复制指针,确保只有在最后一个类完成并指向流对象时才删除。

      两者是等价的,尽管我个人更喜欢参考方法。

      【讨论】:

      • 如果你选择第二个并且有一个能够支持 C++11 的编译器,你可以使用智能指针。如果你不使用这样的编译器,你可以使用 boost 库或自己编写一个智能指针类。
      • @Darokthar 同意了。这是boost::shared_ptr 合适的一种情况(并且绝对优于任何替代方案)。
      【解决方案5】:

      为了测试 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();
      }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2011-09-02
        • 1970-01-01
        • 2023-02-22
        • 2019-01-24
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多