2011-10-26 75 views
7

Tôi đã thử nghiệm với C++ và tôi đã gặp một vấn đề mà tôi không biết cách giải quyết.C++ sao chép đối tượng luồng

Về cơ bản, tôi đã phát hiện ra rằng bạn không thể sao chép luồng (xem Why copying stringstream is not allowed?) và điều đó cũng áp dụng cho các đối tượng 'quấn' chúng. Ví dụ:

  • Tôi tạo một lớp học với thành viên dữ liệu thuộc loại chuỗi.
  • Tôi tạo một đối tượng của lớp này.
  • Tôi cố sao chép đối tượng, ví dụ: "TestObj t1; TestObj t2; t1 = t2;"

Điều này làm cho C2249 lỗi:

'std :: basic_ios < _Elem, _Traits> :: operator =': không có con đường tiếp cận với thành viên tin khai báo trong cơ sở ảo 'std :: basic_ios < _Elem, _Traits>'

Vì vậy, câu hỏi của tôi là: làm thế nào tôi (tốt nhất dễ dàng) có thể sao chép các đối tượng có thành viên dữ liệu kiểu * dòng?

Full mã ví dụ:

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

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

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

    system("pause"); 
    return 0; 
} 

Cảm ơn trước.

CẬP NHẬT

tôi đã quản lý để giải quyết vấn đề này nhờ các câu trả lời dưới đây. Những gì tôi đã làm là khai báo các đối tượng stream một lần và sau đó chỉ đơn giản là tham khảo chúng bằng cách sử dụng con trỏ trong các đối tượng bao bọc (ví dụ, TestStream). Cũng vậy đối với tất cả các đối tượng khác có các nhà xây dựng bản sao riêng.

+0

Vì bạn dường như đã làm bài tập về nhà của bạn, câu hỏi được liên kết. Tại sao bạn yêu cầu hành vi này? đọc viết? –

Trả lời

0

Có hai điều bạn có thể làm, đều liên quan đến việc cẩn thận về những người sở hữu đối tượng:

  1. Lưu trữ một tham chiếu đến một dòng suối, và chắc chắn rằng đối tượng không đi ra khỏi phạm vi chừng những lớp học này của bạn xung quanh.

  2. sao chép các con trỏ xung quanh và đảm bảo chỉ xóa khi lớp cuối cùng của bạn được thực hiện với đối tượng luồng được trỏ tới.

Cả hai đều tương đương, mặc dù cá nhân tôi thích phương pháp tham chiếu.

+0

Nếu bạn đi cho lần thứ 2 và có một trình biên dịch có khả năng C++ 11 bạn có thể sử dụng con trỏ thông minh. Nếu bạn không sử dụng trình biên dịch như vậy, bạn có thể sử dụng thư viện tăng hoặc viết một lớp con trỏ thông minh. – Darokthar

+2

@Darokthar đồng ý. Đây là một trường hợp 'tăng :: shared_ptr' là thích hợp (và chắc chắn là thích hợp hơn cho bất kỳ lựa chọn thay thế nào). –

1

This article cung cấp các cách để thực hiện. Tuy nhiên lưu ý tóm tắt thú vị:

Nói tóm lại, việc tạo ra một bản sao của một dòng là không nhỏ và chỉ nên được thực hiện nếu bạn thực sự cần một bản sao của một đối tượng dòng. Trong nhiều trường hợp, thích hợp hơn khi sử dụng tham chiếu hoặc con trỏ để phát trực tiếp các đối tượng hoặc để chia sẻ bộ đệm luồng giữa hai luồng.

4

Lý do bạn không được phép sao chép luồng là it doesn't make sense to copy a stream. Nếu bạn giải thích những gì bạn đang cố gắng làm, chắc chắn có một cách để làm điều đó. Nếu bạn muốn một đoạn dữ liệu bạn có thể sao chép, hãy sử dụng một chuỗi. Nhưng một luồng giống như một kết nối hơn là một chuỗi.

+0

Nếu tôi muốn sao chép _ kết nối_ thì sao? Tức là, tôi đang đọc một tập tin, và tôi đạt được một dòng tôi biết tôi sẽ phải đọc sau (hoặc nhóm các dòng lớn để lưu trữ). Sẽ thuận tiện khi sao chép luồng, sao cho bản sao ở vị trí đó trong luồng và sau đó có thể quay lại sau. – VF1

+0

@ VF1 Điều đó không có ý nghĩa nói chung. Điều đó có thể có ý nghĩa đối với một số loại luồng, nhưng là một tính năng chung của một luồng, nó không hợp lý. –

+0

Tôi nghĩ rằng tôi đã tìm thấy những gì tôi cần (có, nó chỉ dành cho istreams) - lưu trữ vị trí dòng hiện tại bằng 'tellg' và sử dụng' seekg' sau. – VF1

1

Chắc chắn bạn phải viết hàm tạo bản sao và tự sao chép toán tử gán.

Tiếp theo, bạn phải quyết định ngữ nghĩa nào bạn muốn có bản sao. Vì vậy:

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

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

Nếu bạn muốn có một bản sao cạn, ("foobar") thì bạn cần phải chia sẻ đối tượng stringstream giữa nhiều trường hợp của TestStream, có lẽ tốt nhất để sử dụng một shared_ptr cho điều đó.

Nếu bạn muốn có một bản sao sâu ("foo"), sau đó bạn có thể sao chép như thế này:

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

Hoặc sử dụng một trong những biến thể trong câu hỏi bạn liên kết đến.

Điều đó bao gồm một chuỗi mà bạn đang ở giữa viết khi bạn lấy bản sao. Nếu bạn đang ở giữa số đọc từ đó hoặc nếu bạn đang viết nhưng có thể bạn chưa viết thư đến cuối vì sử dụng seekp, thì bạn cần nắm bắt các vị trí đọc/ghi hiện tại cũng như dữ liệu trong chuỗi, bạn làm với tellg/tellp.

Bạn cũng có thể muốn sao chép qua trạng thái định dạng của luồng, v.v., đó là những gì copyfmt làm và thậm chí là cờ lỗi (rdstate - copyfmt để riêng chúng).

0

Để kiểm tra việc thực hiện các hoạt động ghi khác nhau trong C++ đây là một mã mà biên dịch trên máy tính của bạn và kiểm tra ghi các hoạt động có và không có đệm với một số phương pháp:

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(); 
} 
Các vấn đề liên quan