2011-09-18 194 views
2

在下面的代碼中,我遇到了問題。它表現爲bad_alloc異常,這是因爲傳遞給reader的參數CompressedBufferReader是一個錯誤的字符串。從構造函數調用構造函數

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(); } 

更具體地講,好像我創建匿名其數據內容可被傳遞到reader構造變得之前釋放的的FileReader,一個CompressedBufferReader

的問題是,我不能寫CompressedFileReader的構造函數的方式,讓我正確實例化一個FileReader,因爲我打算用CompressedBufferReader的構造,這意味着構造函數的身體之前,我必須調用它。一個catch-22的位。這個問題如何解決?

+1

我覺得很難說沒有看到'FileReader'的實現是什麼問題。但是作爲一般性評論,您的代碼非常「手動」,如果您使用標準容器和字符串,讀取和維護可能會更加簡單。 –

回答

2

我沒有看到你使用構造函數的方式,這應該會導致bad_alloc(儘管代碼看起來笨拙)的問題。讓我們看看這是導致問題的行執行 -

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

以下步驟進行:

  1. 一個時間FileReader創建。它的構造函數調用filename,這是一個const char*
  2. 構造函數做未知的事情,因爲我們沒有它的代碼。我認爲它應該將文件讀入分配的緩衝區,存儲在FileReaderdata成員中。
  3. getFileData()被稱爲臨時FileReader,返回data的值,我假設這是一個unsigned char *
  4. reader,這是一個CompressedBufferReader,使用unsigned char *構造。
  5. 時空FileReader被銷燬。

所以,問題不在於結構的順序或臨時的FileReader的使用壽命。有一些未知的,你應該看看:

  1. 是否的FileReader構造函數創建一個有效的緩衝,並將其存儲在data
  2. getFileData()是否返回創建的緩衝區?
  3. GetDecompressedBufferSize()是否爲有效緩衝區返回正確的值?
  4. 異常是否從WriteDecompressedBuffer引發,其代碼我們沒有?

最後,您可能想簡化您的代碼。這樣的構造不太可讀。而且,當然,使用像矢量這樣的標準容器會使它更安全。

+0

感謝您抽出寶貴時間來完成此步驟。對於1和2是,對於3:該函數從緩衝區本身讀出一個值。 4:發生異常是因爲GetDecompressedBufferSize返回了一個錯誤的(巨大的)值。這是因爲'CompressedBufferReader'得到了一個不正確的參數。我試圖找出爲什麼正確的緩衝區沒有發送到那裏。 –

+0

我發現了罪魁禍首。這實際上是糟糕的指針算術。它確實發生在GetDecompressedBufferSize函數中。 –

3

您的代碼違反了一個非常重要的規律,被稱爲「三巨頭」

如果你的類有析構函數,賦值運算符的任何或複製 構造函數,那麼它應該有所有三個其中

的原因是,如果一個自定義的析構函數存在,那麼最有可能的自動合成賦值操作符和拷貝構造函數(即只是成員由成員拷貝構造或賦值)都不會是正確的事做。

這條規則非常重要,以至於如果你碰巧發現一個案例,例如一個析構函數,但是默認的拷貝構造函數是有意義的,那麼你至少要在一個註釋中寫下你沒有忘記複製構造函數和/或分配,但自動提供的將是正確的。

如果您的類不能被複制或複製構造,那麼通過聲明它是私有的並且不寫入實現來禁止該操作。

在您的具體情況下,當您的兩個類中的任何一個的實例被複制或分配時,指針將被複制,但數據將被銷燬兩次。

+1

再次閱讀代碼。 'reader'不是用'FileReader'構造的 - 完整的代碼是:'reader(FileReader(filename).getFileData())',請注意'getFileData()'部分。當然,你錯過了這個事實告訴我們關於代碼的可讀性的一些事情,但是這裏沒有做任何拷貝構造。 'reader'用'unsigned char *'初始化。 – eran

+0

這是一個恥辱...你可以標記最喜歡的問題,但不是最喜歡的答案。 – bitmask

+0

@eran:true。我刪除了那部分。這段代碼仍然很糟糕(例如,爲什麼使用所有那些顯式管理不當的緩衝區而不是使用'std :: vector '?)。此外,通過這種代碼,您如何確定在典型的UB演進過程中沒有發生任何問題(即沒有任何明顯的情況發生,並且問題在以後只會在無辜的地方執行一百萬條指令)? – 6502