ここでの使用法は、read() を C++ std:vector に直接使用する場合と同じですが、再割り当ての数があります。
入力ファイルのサイズが不明なため、ファイル サイズがバッファ サイズを超えると、サイズを 2 倍にしてバッファを再割り当てします。これが私のコードです:
#include <vector>
#include <fstream>
#include <iostream>
int main()
{
const size_t initSize = 1;
std::vector<char> buf(initSize); // sizes buf to initSize, so &buf[0] below is valid
std::ifstream ifile("D:\\Pictures\\input.jpg", std::ios_base::in|std::ios_base::binary);
if (ifile)
{
size_t bufLen = 0;
for (buf.reserve(1024); !ifile.eof(); buf.reserve(buf.capacity() << 1))
{
std::cout << buf.capacity() << std::endl;
ifile.read(&buf[0] + bufLen, buf.capacity() - bufLen);
bufLen += ifile.gcount();
}
std::ofstream ofile("rebuild.jpg", std::ios_base::out|std::ios_base::binary);
if (ofile)
{
ofile.write(&buf[0], bufLen);
}
}
}
プログラムは期待どおりにベクトル容量を出力し、出力ファイルを入力と同じサイズで書き込みますが、オフセットinitSize
の前は入力と同じバイトのみで、その後はすべてゼロになります...
&buf[bufLen]
inの使用read()
は間違いなく未定義の動作ですが&buf[0] + bufLen
、継続的な割り当てが保証されているため、適切な書き込み位置が得られますね。( が提供されます。サイズが になることにinitSize != 0
注意してください。また、はい、私の環境で の場合、ランタイムの致命的なエラーが発生します。) 何か見逃していますか? これもUBですか?標準は std::vector のこの使用法について何か言っていますか?std::vector<char> buf(initSize);
buf
initSize
initSize == 0
はい、最初にファイル サイズを計算し、まったく同じバッファー サイズを割り当てることができることはわかっていますが、私のプロジェクトでは、入力ファイルがほぼ常に特定の よりも小さいことが予想されるSIZE
ため、 に設定initSize
してSIZE
、オーバーヘッドはないと予想できます (ファイルサイズの計算など)、「例外処理」のためだけに再割り当てを使用します。そして、はい、私は と を置き換えることができることを知っています。そうすれば、reserve()
少しresize()
のcapacity()
オーバーヘッドsize()
(サイズ変更のたびにバッファーをゼロにする) で物事が機能しますが、冗長な操作、一種の偏執症を取り除きたいと思っています...
更新 1:
実際、適切な位置を取得する標準から論理的に推測できます。&buf[0] + bufLen
std::vector<char> buf(128);
buf.reserve(512);
char* bufPtr0 = &buf[0], *bufPtrOutofRange = &buf[0] + 200;
buf.resize(256); std::cout << "standard guarantees no reallocation" << std::endl;
char* bufPtr1 = &buf[0], *bufInRange = &buf[200];
if (bufPtr0 == bufPtr1)
std::cout << "so bufPtr0 == bufPtr1" << std::endl;
std::cout << "and 200 < buf.size(), standard guarantees bufInRange == bufPtr1 + 200" << std::endl;
if (bufInRange == bufPtrOutofRange)
std::cout << "finally we have: bufInRange == bufPtrOutofRange" << std::endl;
出力:
standard guarantees no reallocation
so bufPtr0 == bufPtr1
and 200 < buf.size(), standard guarantees bufInRange == bufPtr1 + 200
finally we have: bufInRange == bufPtrOutofRange
そして、ここで 200 は、every に置き換えることができbuf.size() <= i < buf.capacity()
、同様の演繹が成り立ちます。
更新された 2:
はい。今日、問題を調べる時間がありました。プログラムは正しいアドレスを取得し、正しいデータを予約済みメモリに書き込みましたが、次のreserve()
ではbuf
再割り当てされ、範囲内の要素のみが[0, buf.size())
新しいメモリにコピーされました。これがなぞなぞの答えです...
最後の注意:バッファーがデータでいっぱいになった後に再割り当てする必要がない場合は、reserve()/capatity()
の代わりに間違いなく使用できresize()/size()
ますが、必要な場合は後者を使用してください。また、ここで利用可能なすべての実装 (VC++、g++、ICC) で、例は期待どおりに動作します。
const size_t initSize = 1;
std::vector<char> buf(initSize);
buf.reserve(1024*100); // assume the reserved space is enough for file reading
std::ifstream ifile("D:\\Pictures\\input.jpg", std::ios_base::in|std::ios_base::binary);
if (ifile)
{
ifile.read(&buf[0], buf.capacity()); // ok. the whole file is read into buf
std::ofstream ofile("rebuld.jpg", std::ios_base::out|std::ios_base::binary);
if (ofile)
{
ofile.write(&buf[0], ifile.gcount()); // rebuld.jpg just identical to input.jpg
}
}
buf.reserve(1024*200); // horror! probably always lose all data in buf after offset initSize
'TC++PL, 4e' pp 1041 から引用した別の例を次に示します。関数の最初の行では ではreserve()
なくを使用していることに注意してresize()
ください。
void fill(istream& in, string& s, int max)
// use s as target for low-level input (simplified)
{
s.reserve(max); // make sure there is enough allocated space
in.read(&s[0],max);
const int n = in.gcount(); // number of characters read
s.resize(n);
s.shrink_to_fit(); // discard excess capacity
}
更新 3 (8 年後): この数年間で多くのことが起こりました。私は 6 年近く C++ を作業言語として使用していませんでしたが、現在は博士課程の学生です! また、多くの人が UB があると考えていますが、彼らが示した理由はかなり異なり (一部はすでに UB ではないことが示されています)、これは複雑なケースであることを示しています。そのため、投票して回答を書く前に、コメントを読んで参加することを強くお勧めします。
もう 1 つのことは、博士課程のトレーニングを受けて、今では比較的簡単に C++ 標準に飛び込むことができるようになったことです。標準に基づいて、上記の2つのコードブロックが機能するはずであることを私自身の回答で示したと思います。(このstring
例では C++11 が必要です。) 私の回答はまだ論争の的になっているため (ただし、改ざんされていないと私は信じています)、私はそれを受け入れず、むしろ批判的なレビューやその他の回答に対してオープンです。