Reputation: 11
I have a c++ class that roughly looks like this (see code below). It has an input buffer and an output buffer using a std::stringbuf. Because I also want access to the raw buffer, I set the underlying buffer with my own user-defined byte array using std::stringbuf.pubsetbuf().
All things seem to work fine, until I no longer need the object but when the object is destructed, it causes my program to crash with an access violation.
I traced it down to the piece of code as illustrated below. It looks to me that the stringbuf is somehow cleaning up my user-defined byte array itself?? My program doesn't crash when I remove the code in the destrcutor of my class to free the byte array I previously allocated in the constructor.
Any advice on this, please? Am I wrongly using the std::stringbuf? I'm using the Borland 5.0 compiler (I know it's a very old and outdated compiler, but I have to stick to this compiler for a while).
class SomeClass {
private:
char *mIBuf;
char *mOBuf;
std::stringbuf mIBufStream;
std::stringbuf mOBufStream;
public:
SomeClass(int iBufSize, int oBufSize) :
mIBuf(), mOBuf(),
mIBufStream(), mOBufStream()
{
mIBuf = (char*)malloc(iBufSize);
mOBuf = (char*)malloc(oBufSize);
mIBufStream.pubsetbuf(mIBuf, iBufSize);
mIBufStream.pubseekpos(0);
mOBufStream.pubsetbuf(mOBuf, oBufSize);
mOBufStream.pubseekpos(0);
}
virtual ~SomeClass()
{
free(mIBuf);
free(mOBuf)
}
};
Upvotes: 0
Views: 290
Reputation: 11
Well, to answer my own question... (and thanks to Ben Voight for his useful advise). Until someone else would prove me wrong, I've come to the conclusion that my issue is implementation specific and that the Borland 5.0 STL library may not be conform the standard.
I tested my sample class. I extracted it and put it in a small test program as listed below (no other code that would possibly corrupt the memory). I compiled 1 version with Visual Studio 2013, the other with Borland 5.0. The one compiled with Visual Studio runs just fine. The one compiled with Borland crashes after the 1st iteration.
#include <iostream>
class SomeClass {
char *mIBuf;
char *mOBuf;
std::stringbuf mIBufStream;
std::stringbuf mOBufStream;
public:
SomeClass(int iBufSize, int oBufSize) : mIBuf(NULL), mOBuf(NULL), mIBufStream(), mOBufStream() {
mIBuf = (char*)malloc(iBufSize);
mOBuf = (char*)malloc(oBufSize);
mIBufStream.pubsetbuf(mIBuf, iBufSize);
mIBufStream.pubseekpos(0);
mOBufStream.pubsetbuf(mOBuf, oBufSize);
mOBufStream.pubseekpos(0);
}
virtual ~SomeClass() {
mIBufStream.pubsetbuf(0, 0);
mOBufStream.pubsetbuf(0, 0);
free(mIBuf);
free(mOBuf);
}
};
int main(int argc, char* argv[])
{
for (int x = 0; x < 1000; x++) {
SomeClass SomeClass(128 * 1024, 128 * 1024);
std::cout << "Pass " << x + 1 << std::endl;
}
return 0;
}
Upvotes: 0
Reputation: 283773
According to the Standard stringbuf
doesn't have a destructor of its own and streambuf
's destructor does nothing. Your old compiler and library may or may not be following that; the evidence is that it isn't.
Well, in principle you are doing something wrong. When you call pubsetbuf
you are giving that object permission to use that buffer for as long as the object lives, or until you change its buffer again.
Looking at your destructor, you aren't keeping your side of the bargain.
virtual ~SomeClass()
{
free(mIBuf);
free(mOBuf); // <- missing semicolon in your code
// the stringbuf objects are still alive here
} // they get automatically destroyed here
One option is to arrange for the stringbuf
objects to be destroyed before you free the buffers (char*
buffer deallocation will need to be done by a helper class, either a base class or a member declared before the stringbuf
-- std::vector<char>
would be a good choice).
Or, you can let the stringbuf
know that you're revoking its permission to use your memory:
virtual ~SomeClass()
{
mIBufStream.pubsetbuf(0, 0);
mOBufStream.pubsetbuf(0, 0);
// the stringbufs cannot use your memory any longer
free(mIBuf);
free(mOBuf);
}
Upvotes: 1