Cでは、それは多かれ少なかれ合法でした。
C++ では、通常、関数はそれを行うべきではありません。メモリがリークしないことを保証するために、 RAIIを使用するようにしてください。
そして今、「どうやってメモリ リークが発生するのでしょう。ここで呼び出しますdelete[]!」と言うかもしれませんが、行で例外がスローされた場合はどうなる// ...でしょうか?
関数の目的に応じて、考慮すべきオプションがいくつかあります。明らかな 1 つは、配列をベクトルに置き換えることです。
std::vector<char> f();
std::vector<char> data = f();
int data_length = data.size();
// ...
//delete[] data;
ベクトルがスタックに割り当てられ、スコープ外になるとそのデストラクタが呼び出されるため、明示的に削除する必要がなくなりました。
コメントに応じて、上記はベクトルのコピーを意味することを言及する必要があります。これは潜在的に高価になる可能性があります。ほとんどのコンパイラは、f関数が複雑すぎない場合、そのコピーを最適化するので問題ありません。(関数があまり頻繁に呼び出されない場合、オーバーヘッドは問題になりません)。しかし、それが起こらない場合は、代わりに空の配列をf参照によって関数に渡しf、新しいベクトルを返す代わりにそのデータを格納することができます。
コピーを返すパフォーマンスが受け入れられない場合、別の方法として、コンテナーの選択を完全に分離し、代わりにイテレーターを使用することもできます。
// definition of f
template <typename iter>
void f(iter out);
// use of f
std::vector<char> vec;
f(std::back_inserter(vec));
これで、通常のイテレータ操作 (*out現在の要素を参照または書き込むため、および++outイテレータを次の要素に進めるため) を使用できるようになりました。さらに重要なことに、すべての標準アルゴリズムが機能するようになりました。std::copyたとえば、データをイテレータにコピーするために使用できます。これは、関数が一連のデータを返す必要がある場合に、標準ライブラリによって通常選択されるアプローチです (つまり、これは良い考えです;))。
別のオプションは、割り当て/解放を担当する独自のオブジェクトを作成することです。
struct f { // simplified for the sake of example. In the real world, it should be given a proper copy constructor + assignment operator, or they should be made inaccessible to avoid copying the object
f(){
// do whatever the f function was originally meant to do here
size = ???
data = new char[size];
}
~f() { delete[] data; }
int size;
char* data;
};
f data;
int data_length = data.size;
// ...
//delete[] data;
また、割り当てはスタック上のオブジェクトによって管理されるため、明示的に削除する必要はなくなりました。後者は明らかに手間がかかり、エラーが発生する余地もあります。そのため、標準のベクター クラス (またはその他の標準ライブラリ コンポーネント) が機能する場合は、そちらを優先してください。この例は、状況に合わせてカスタマイズする必要がある場合のみです。
C++ の一般的な経験則は、「 RAII オブジェクトの外側にdeleteorを書いdelete[]ている場合、それは間違っています。RAII オブジェクトの外側に anewまたは `new[]を書いている場合、それは間違っています。結果がすぐにスマート ポインターに渡されない限り、間違っています。"