2012-04-13 73 views
6

我寫過一個函數,它將字節加載到文件中,並返回包含字節緩衝區和緩衝區長度的FileData結構體。在一個結構體或類中使用智能指針

我想緩衝區一旦被消耗並被拋出作用域就被刪除。

我很難讓它編譯,由於各種鑄造錯誤。另外,我不確定緩衝區是否被正確移動而不是被複制。我不介意FileData結構本身被複制,因爲它最多可能是16個字節。

一般來說,你如何使用智能指針作爲類/結構域?這甚至是你會做的嗎?

這是一個含糊不清的問題,我知道,但由於我在智能指針方面有一些概念上的困難,我希望這個例子能幫助我走向正確的方向。

這裏是我到目前爲止有:

struct FileData 
{ 
    unique_ptr<char[]> buf; 
    unsigned int len; 
}; 

FileData LoadFile(string filename) 
{ 
    ifstream str; 
    str.open(filename, ios::binary); 

    str.seekg(0, ios::end); 
    auto len = str.tellg(); 
    str.seekg(0, ios::beg); 

    char* buf = new char[len]; 

    str.read(buf, len); 
    str.close(); 

    FileData d = { unique_ptr<char[]>(buf), len }; 

    return d; 
} 

編輯:由於一些人好奇的錯誤消息,我這個當前的代碼得到的,那就是:

error C2248: 'std::unique_ptr<_Ty>::unique_ptr' : cannot access private member declared in class 'std::unique_ptr<_Ty>' 
+0

您的問題是,您根本不會提供關於錯誤消息的任何具體細節。我們究竟可以怎樣識別他們呢? – Puppy 2012-04-13 00:18:48

+0

@DeadMG我認爲應該清楚代碼存在問題,因爲我確實指出我不確定這是使用智能指針並移動語義的正確方法。我希望代碼不僅僅是編譯;我希望它是正確和習慣的。儘管如此,我已經用錯誤信息更新了問題。 – 2012-04-13 00:23:37

+0

你得到的錯誤是因爲你試圖複製一個unique_ptr,你必須使用std :: move。您可以使用shared_ptr並聲明自己的釋放器,但矢量解決方案更清晰。 – pstrjds 2012-04-13 00:26:19

回答

5

你的代碼是正確的,除了一個小細節:

struct FileData 
{ 
    unique_ptr<char[]> buf; 
    <del>unsigned int</del> <ins>streamoff</ins> len; 
}; 

它不會編譯你的原因是你的編譯器尚未實現特別移動成員的自動生成。在完全C++ 11符合標準的編譯你的FileData會表現得好像:

struct FileData 
{ 
    unique_ptr<char[]> buf; 
    streamoff len; 

    FileData(FileData&&) = default; 
    FileData& operator=(FileData&&) = default; 
    FileData(const FileData&) = delete; 
    FileData& operator=(const FileData&) = delete; 
    ~FileData() = default; 
}; 

的違約轉移構造簡單的移動結構的每個成員(以及類似的違約舉動分配)。

當從LoadFile返回d時,會發生隱式移動,該移動將綁定到隱式默認移動構造函數。

使用vector<char>string正如其他人所建議的也將工作。但就C++ 11而言,您的代碼沒有任何問題。

哦,我可能會調整它,像這樣:我希望得到儘快擁有儘可能我的資源:

FileData LoadFile(string filename) 
{ 
    ifstream str; 
    str.open(filename, ios::binary); 

    str.seekg(0, ios::end); 
    auto len = str.tellg(); 
    str.seekg(0, ios::beg); 

    FileData d = {unique_ptr<char[]>(new char[len]), len}; 

    str.read(d.buf.get(), d.len); 
    str.close(); 

    return d; 
} 

如果您需要明確定義FileData招成員,它應該看起來像:

struct FileData 
{ 
    unique_ptr<char[]> buf; 
    streamoff len; 

    FileData(FileData&& f) 
     : buf(std::move(f.buf)), 
      len(f.len) 
     { 
      f.len = 0; 
     } 

    FileData& operator=(FileData&& f) 
    { 
     buf = std::move(f.buf); 
     len = f.len; 
     f.len = 0; 
     return *this; 
    } 
}; 

哦,這讓我想到另一點。默認移動成員不是,確切地說是正確的,因爲他們沒有在來源中將len設置爲0。如果這是一個錯誤,這取決於你的文檔。 ~FileData()不需要len來反映緩衝區的長度。但其他客戶可能會。如果您將移動的FileData定義爲沒有可靠的len,那麼默認的移動成員是好的,否則它們不是。

+0

'FileData(FileData &&)= default;' - 是一種適用於某些編譯器的簡寫語法嗎?當我嘗試自己創建移動構造函數時,它會抱怨'operator ='無法訪問。嗯......(VC++ 11,順便說一下) – 2012-04-13 00:49:20

+0

@ReiMiyasaka:是的,'= default'語法是一種明確請求C++ 98/03中隱含生成的特殊成員。適用於默認ctor,複製ctor,複製任務,移動ctor,移動任務和dtor。但它可能不會爲你實現(我不熟悉VC++ 11)。這些功能在遊戲的後期進行了標準化,因此您的編譯器可能還沒有這些功能是可以理解的。 「= delete」語法大致相當於聲明它是私有的,而不是定義它。我不確定你的明確行動是怎麼回事。 – 2012-04-13 01:01:08

2

我可能會使用std::vector而不是std:::unique_ptr<char[]>,如果您不介意std::vector正在複製當您返回FileData

struct FileData 
{ 
    vector<char> buf; 
}; 

FileData LoadFile(string filename) 
{ 
    ifstream str; 
    str.open(filename, ios::binary); 

    str.seekg(0, ios::end); 
    auto len = str.tellg(); 
    str.seekg(0, ios::beg); 

    FileData d; 
    d.buf.resize(len); 

    str.read(&(d.buf)[0], len); 
    str.close(); 

    return d; 
} 

另外,避免了複製,主叫方可以在FileData傳遞作爲函數參數,而不是返回值:

struct FileData 
{ 
    vector<char> buf; 
}; 

void LoadFile(string filename, FileData &data) 
{ 
    ifstream str; 
    str.open(filename, ios::binary); 

    str.seekg(0, ios::end); 
    auto len = str.tellg(); 
    str.seekg(0, ios::beg); 

    data.buf.resize(len); 

    str.read(&(data.buf)[0], len); 
    str.close(); 
} 
+0

這不會導致文件數據「數組」本身被複制,或者矢量是否實現了C++ 11移動語義?不要試圖爭論,只是好奇。 – pstrjds 2012-04-13 00:19:24

+0

在這種情況下,我甚至不需要FileData結構,所以答案是我的問題的一半。謝謝。我認爲在調整矢量大小的情況下性能不算太差? – 2012-04-13 00:19:54

+1

@Rei:它不會在給定的例子中調整大小。因爲它最初是空的,它更像「大小」:P,它不會比自己分配內存慢。 – Puppy 2012-04-13 00:21:04

-1

如何使用的std :: string作爲緩衝。它希望所有的行爲:

  • 引用計數,而不是複製
  • 消失,一旦超出範圍
  • 持有

人們會下來投票,是因爲任意字節任意數量的其不是弦的原始使用意圖;也許派生類(或把它包),並把它稱爲「緩衝」

+0

我認爲那會奏效,但是它又一次並沒有幫助我弄清楚在其他類似情況下如何使用智能指針。 – 2012-04-13 00:29:31

+0

我不知道unique_ptr(我使用boost),但unique_ptr 看起來不對我,通常數組ptrs有他們自己的類。我會有uniq_ptr 。基本上它應該工作 – pm100 2012-04-13 00:37:37

+0

什麼時候'std :: string'成爲引用計數?或者是在不同編譯器中實現的具體實現? – 2012-04-13 05:04:05