C ++ скопировать объект потока - PullRequest
9 голосов
/ 26 октября 2011

Я экспериментировал с C ++ и столкнулся с проблемой, которую не знаю, как решить.

По сути, я обнаружил, что нельзя копировать потоки (см. Почему копирование потока строк не разрешено? ), и это также относится к объектам, которые их «обертывают».Например:

  • Я создаю класс с членом данных типа stringstream.
  • Я создаю объект этого класса.
  • Я пытаюсь скопировать объектНапример, «TestObj t1; TestObj t2; t1 = t2;»

Это приводит к ошибке C2249:

'std :: basic_ios <_Elem, _Traits> ::operator = ': нет доступного пути к приватному члену, объявленному в виртуальной базе' std :: basic_ios <_Elem, _Traits> '

Поэтому мой вопрос: как я могу (желательно легко ) копировать объекты, имеющие элементы данных типа * 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).То же самое касается всех других объектов, которые имеют частные конструкторы копирования.

Ответы [ 5 ]

5 голосов
/ 26 октября 2011

Причина, по которой вам не разрешено копировать поток, заключается в том, что не имеет смысла копировать поток . Если вы объясните, что вы пытаетесь сделать, безусловно, есть способ сделать это. Если вам нужен фрагмент данных, который вы можете скопировать, используйте строку. Но поток больше похож на соединение, чем на строку.

2 голосов
/ 26 октября 2011

Эта статья предоставляет способы сделать это. Однако обратите внимание на интересное резюме:

Таким образом, создание копии потока не тривиально и должно только сделать, если вам действительно нужна копия объекта потока . Во многих случаях, более целесообразно использовать ссылки или указатели для потоковых объектов вместо этого или для совместного использования потокового буфера между двумя потоками.

1 голос
/ 26 октября 2011

Конечно, вы должны сами написать конструктор копирования и оператор присваивания.

Затем вы должны решить, какую семантику вы хотите иметь в копии. Итак:

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

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

Если вам нужна мелкая копия, ("foobar"), вам нужно разделить объект stringstream между несколькими экземплярами TestStream, вероятно, лучше всего использовать shared_ptr для этого.

Если вам нужна глубокая копия ("foo"), то вы можете скопировать так:

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

Или используйте один из вариантов в вопросе, на который вы ссылаетесь.

Это относится к потоку строк, которому вы занимаетесь , пишущему , когда вы берете копию. Если вы находитесь в середине чтения из него , или если вы пишете, но не можете писать до конца из-за использования seekp, то вам нужно захватить текущее чтение / запишите позиции, а также данные в потоке строки, которые вы делаете с tellg/tellp.

Возможно, вы также захотите скопировать состояние формата потока и т. Д., Что делает copyfmt, и даже флаги ошибок (rdstate - copyfmt оставляет их в покое).

0 голосов
/ 21 октября 2014

Чтобы проверить производительность различных операций записи в c ++, вот код, который компилируется на вашем компьютере и тестирует операции записи с буферизацией и без нее несколькими способами:

Ссылка

#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();
}
0 голосов
/ 26 октября 2011

Вы можете сделать две вещи, обе из которых включают осторожность в отношении того, кто владеет объектом:

  1. Сохраните ссылку на поток и убедитесь, что объект не выходит изобласть действия, пока эти ваши классы существуют.

  2. копируйте указатели вокруг и обязательно удаляйте только тогда, когда последний из ваших классов сделан с указанным объектом потока.

Оба эквивалентны, хотя я лично предпочитаю эталонный подход.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...