简体   繁体   中英

Calling constructors from constructors

In this code I present below I am having a problem. It manifests as a bad_alloc exception, and this is because the argument passed to reader , the CompressedBufferReader , is a bad string.

class FileReader { 
    FILE *file; 
protected:
    unsigned char *data; // local copy
    long size;
public:
    FileReader(const char *filename);
    ~FileReader();
    unsigned long getSize();
    unsigned char *getFileData();
};

class CompressedBufferReader {
    unsigned char *buffer;
    unsigned long len;
public:
    CompressedBufferReader(unsigned char *);
    ~CompressedBufferReader();
    unsigned char *getBuffer();
    unsigned long getLength();
};
CompressedBufferReader::CompressedBufferReader(unsigned char *srcCompressed) {
    len = 0; buffer = 0;
    len = GetDecompressedBufferSize(srcCompressed);
    buffer = new unsigned char[len]; if (!buffer) throw std::runtime_error("Cannot allocate!");
    WriteDecompressedBuffer(buffer, len, srcCompressed);
}
CompressedBufferReader::~CompressedBufferReader() {
    delete[] buffer;
}
unsigned char *CompressedBufferReader::getBuffer() {return buffer;}
unsigned long CompressedBufferReader::getLength() {return len;}

// similar interface to FileReader. Does not inherit because it does not benefit from doing so.
class CompressedFileReader {
    CompressedBufferReader reader;
public:
    CompressedFileReader(const char *filename);
    unsigned char *getFileData();
    unsigned long getSize();
};

CompressedFileReader::CompressedFileReader(const char *filename) : reader(FileReader(filename).getFileData()){} // this line is causing the problem
unsigned char *CompressedFileReader::getFileData() { return reader.getBuffer(); }
unsigned long CompressedFileReader::getSize() { return reader.getLength(); }

To be more specific, it seems like the FileReader which I create anonymously becomes deallocated before its data contents can be passed to the constructor of reader , a CompressedBufferReader .

the problem is that I cannot write CompressedFileReader 's constructor in a way that allows me to properly instantiate a FileReader , because I intend to use CompressedBufferReader 's constructor and that means I must call it before the body of the constructor. Bit of a catch-22. How is this issue resolved?

Your code is violating a very important rule, known as the "big three"

If your class has any of a destructor, assignment operator or copy constructor then it should have all three of them .

The reason is that if a custom destructor is present then most probably the automatically synthesized assignment operator and copy constructor (that are simply member-by-member copy constructions or assignments) are not going to be the right thing to do.

This rule is so important that if you happen to find a case in which it's meaningful to have for example a destructor but the default copy constructor then just write at least in a comment that you didn't forget about copy constructor and/or assignment but that the automatically provided one are going to be correct.

If instead your classes must not be copied or copy constructed then forbid the operation by declaring it private and by not writing the implementation.

In your specific case when an instance of either of your two classes gets copied or assigned the pointer will be copied but then the data will be destroyed twice.

I don't see a problem in the way you use the constructors, which should cause the bad_alloc (though the code does seem clumsy). Let us look at the execution of the line that is causing the problem -

CompressedFileReader::CompressedFileReader(const char *filename) : 
   reader(FileReader(filename).getFileData()){}

The following steps take place:

  1. A temporal FileReader is created. Its constructor is called with filename , which is a const char* .
  2. The constructor does unknown things, because we don't have its code. It should, I assume, read the file into an allocated buffer, stored at the data member of the FileReader .
  3. getFileData() is called on the temp FileReader , returning the value of data , I assume, which is an unsigned char * .
  4. reader , which is a CompressedBufferReader , is constructed using the unsigned char * .
  5. The temporal FileReader is destructed.

So, the problem is not with the order of the constructions, or the lifespan of the temporary FileReader . There are a few unknown which you should look into:

  1. Does the constructor of FileReader create a valid buffer, and stores it in data ?
  2. Does getFileData() return the buffer created?
  3. Does GetDecompressedBufferSize() return the correct value for a valid buffer?
  4. Does the exception happen to be thrown from WriteDecompressedBuffer , whose code we don't have?

Lastly, you might want to simplify your code. Constructions like that aren't very readable. And, of course, the use of standard container like vectors would make it safer.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM