C++ 复制一个流对象
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/7903903/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
C++ copy a stream object
提问by Matt Larsen
I've been experimenting with C++, and I've come across a problem that I don't know how to solve.
我一直在试验 C++,但遇到了一个我不知道如何解决的问题。
Basically, I've discovered that you can't copy streams (see Why copying stringstream is not allowed?), and that also applies for objects that 'wrap' them. For example:
基本上,我发现您不能复制流(请参阅为什么不允许复制 stringstream?),这也适用于“包装”它们的对象。例如:
- I create a class with a data member of type stringstream.
- I create an object of this class.
- I attempt to copy the object, eg "TestObj t1; TestObj t2; t1 = t2;"
- 我创建了一个带有 stringstream 类型数据成员的类。
- 我创建了这个类的一个对象。
- 我尝试复制对象,例如“TestObj t1; TestObj t2; t1 = t2;”
This causes the error C2249:
这会导致错误 C2249:
'std::basic_ios<_Elem,_Traits>::operator =' : no accessible path to private member declared in virtual base 'std::basic_ios<_Elem,_Traits>'
'std::basic_ios<_Elem,_Traits>::operator =' :在虚拟基类 'std::basic_ios<_Elem,_Traits>' 中声明的私有成员没有可访问的路径
So my question is: how can I (preferably easily) copy objects that have data members of type *stream?
所以我的问题是:我如何(最好是轻松地)复制具有 *stream 类型数据成员的对象?
Full example code:
完整示例代码:
#include <iostream>
#include <string>
#include <sstream>
class TestStream
{
public:
std::stringstream str;
};
int main()
{
TestStream test;
TestStream test2;
test = test2;
system("pause");
return 0;
}
Thanks in advance.
提前致谢。
UPDATE
更新
I've managed to solve this problem thanks the answers below. What I have done is declare the stream objects once and then simply reference them using pointers in the wrapper objects (eg, TestStream). The same goes for all other objects that have private copy constructors.
感谢下面的答案,我已经设法解决了这个问题。我所做的是声明一次流对象,然后使用包装对象(例如,TestStream)中的指针简单地引用它们。对于具有私有复制构造函数的所有其他对象也是如此。
回答by David Schwartz
The reason you are not allowed to copy a stream is that it doesn't make sense to copy a stream. If you explain what it is that you are trying to do, there's certainly a way to do it. If you want a chunk of data you can copy, use a string. But a stream is more like a connection than a string.
不允许复制流的原因是复制流没有意义。如果你解释你想要做什么,那么肯定有办法做到。如果您想要可以复制的数据块,请使用字符串。但是流更像是一个连接而不是一个字符串。
回答by JRL
This articleprovides ways to do it. Note however the interesting summary:
In summary, creating a copy of a stream is not trivial and should only be done if you really need a copy of a stream object. In many cases, it is more appropriate to use references or pointers to stream objects instead, or to share a stream buffer between two streams.
总之,创建流的副本并非易事,只有在您确实需要流对象的副本时才应该这样做。在许多情况下,更适合使用指向流对象的引用或指针,或者在两个流之间共享一个流缓冲区。
回答by Steve Jessop
Certainly you have to write the copy constructor and copy assignment operator yourself.
当然,您必须自己编写复制构造函数和复制赋值运算符。
Next, you have to decide what semantics you would like the copy to have. So:
接下来,您必须决定您希望副本具有什么语义。所以:
TestStream test;
TestStream test2;
test2 << "foo"
test = test2;
test << "bar";
test2.str.str(); // should this be "foo" or "foobar" ?
If you want a shallow copy, ("foobar"
) then you need to share the stringstream object between multiple instances of TestStream
, probably best to use a shared_ptr
for that.
如果你想要一个浅拷贝, ( "foobar"
) 那么你需要在 的多个实例之间共享 stringstream 对象TestStream
,可能最好使用 a shared_ptr
。
If you want a deep copy ("foo"
), then you could copy like this:
如果你想要一个深拷贝 ( "foo"
),那么你可以像这样复制:
TestStream(const TestStream &rhs) : str(rhs.str.str()) {}
Or use one of the variants in the question you link to.
或者使用您链接到的问题中的变体之一。
That covers a stringstream to which you're in the middle of writingwhen you take the copy. If you're in the middle of readingfrom it, or if you're writing but you might not be writing to the end because of use of seekp
, then you need to capture the current read/write positions as well as the data in the stringstream, which you do with tellg/tellp
.
这涵盖了您在获取副本时正在写入的字符串流。如果您正在读取它,或者如果您正在写入但由于使用 ,您可能不会写入到最后seekp
,那么您需要捕获当前的读/写位置以及其中的数据stringstream,你用tellg/tellp
.
You might also want to copy across the stream's format state, and so on, which is what copyfmt
does, and even the error flags (rdstate
-- copyfmt
leaves them alone).
您可能还想复制流的格式状态,等等,这是什么copyfmt
,甚至错误标志(rdstate
--copyfmt
不理会它们)。
回答by Gabriel
To test the performance of various write operations in c++ here is a code which compiles on your machine and tests write operations with and without buffering with several methods:
为了测试 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();
}
回答by rubenvb
There are two things you can do, both involve being careful about who owns the object:
您可以做两件事,都涉及小心谁拥有对象:
Store a reference to a stream, and make sure the object does not go out of scope as long as these classes of yours are around.
copy the pointers around, and be sure to delete only when the last of your classes is done with the stream object pointed to.
存储对流的引用,并确保只要您的这些类存在,对象就不会超出范围。
复制周围的指针,并确保只有在最后一个类完成并指向流对象时才删除。
Both are equivalent, although I personally prefer the reference approach.
两者都是等价的,尽管我个人更喜欢参考方法。