広く使用されている2つのメモリ割り当て技術があります。自動割り当てと動的割り当てです。通常、それぞれに対応するメモリ領域があります。スタックとヒープです。
スタック
スタックは常に順次方式でメモリを割り当てます。逆の順序でメモリを解放する必要があるため、これを行うことができます(先入れ先出し、FILO)。これは、多くのプログラミング言語におけるローカル変数のメモリ割り当て手法です。最小限の簿記が必要であり、割り当てる次のアドレスが暗黙的であるため、非常に高速です。
C ++では、スコープの最後でストレージが自動的に要求されるため、これは自動ストレージと呼ばれます。現在のコードブロック(を使用して区切られた{}
)の実行が完了するとすぐに、そのブロック内のすべての変数のメモリが自動的に収集されます。これは、デストラクタが呼び出されてリソースをクリーンアップする瞬間でもあります。
ヒープ
ヒープにより、より柔軟なメモリ割り当てモードが可能になります。簿記はより複雑で、割り当ては遅くなります。暗黙的な解放ポイントはないため、delete
or delete[]
(free
C)を使用して手動でメモリを解放する必要があります。ただし、暗黙的なリリースポイントがないことは、ヒープの柔軟性の鍵です。
ダイナミックアロケーションを使用する理由
ヒープの使用が遅く、メモリリークやメモリの断片化につながる可能性がある場合でも、動的割り当ての制限は少ないため、完全に適切な使用例があります。
ダイナミックアロケーションを使用する2つの主な理由:
コンパイル時に必要なメモリ量がわかりません。たとえば、テキストファイルを文字列に読み取る場合、通常、ファイルのサイズがわからないため、プログラムを実行するまで、割り当てるメモリ量を決定できません。
現在のブロックを終了した後も存続するメモリを割り当てたい。たとえばstring readfile(string path)
、ファイルの内容を返す関数を記述したい場合があります。この場合、スタックがファイルの内容全体を保持できたとしても、関数から戻り、割り当てられたメモリブロックを保持することはできません。
ダイナミックアロケーションが不要なことが多い理由
C ++には、デストラクターと呼ばれるきちんとした構成があります。このメカニズムでは、リソースの有効期間を変数の有効期間に合わせることで、リソースを管理できます。この手法はRAIIと呼ばれ、C ++の際立ったポイントです。リソースをオブジェクトに「ラップ」します。 std::string
完璧な例です。このスニペット:
int main ( int argc, char* argv[] )
{
std::string program(argv[0]);
}
実際には、可変量のメモリを割り当てます。std::string
オブジェクトはヒープとそのデストラクタで解放し、それを使用してメモリを割り当てます。この場合は、リソースを手動で管理する必要はなく、動的メモリ割り当てのメリットが得られます。
特に、このスニペットでは、
int main ( int argc, char* argv[] )
{
std::string * program = new std::string(argv[0]); // Bad!
delete program;
}
不要な動的メモリ割り当てがあります。このプログラムでは、さらにタイピング(!)が必要であり、メモリの割り当てを解除し忘れるリスクがあります。これには明らかな利点はありません。
自動ストレージをできるだけ頻繁に使用する理由
基本的に、最後の段落はそれを要約します。自動ストレージをできるだけ頻繁に使用すると、プログラムが次のようになります。
- 入力が速くなります。
- 実行すると速くなります。
- メモリ/リソースリークが発生しにくい。
ボーナスポイント
参照された質問には、追加の懸念があります。特に、次のクラス:
class Line {
public:
Line();
~Line();
std::string* mString;
};
Line::Line() {
mString = new std::string("foo_bar");
}
Line::~Line() {
delete mString;
}
実際には、次のものよりも使用するのがはるかに危険です。
class Line {
public:
Line();
std::string mString;
};
Line::Line() {
mString = "foo_bar";
// note: there is a cleaner way to write this.
}
その理由はstd::string
、コピーコンストラクタを適切に定義しているからです。次のプログラムを検討してください。
int main ()
{
Line l1;
Line l2 = l1;
}
元のバージョンを使用delete
すると、同じプログラムで2回使用されるため、このプログラムはおそらくクラッシュします。修飾されたバージョンを使用して、各Line
インスタンスが独自の文字列所有するインスタンスを、それ自体のメモリを有する各両方は、プログラムの終了時に解放されます。
その他の注意事項
上記のすべての理由により、RAIIの広範な使用はC ++でのベストプラクティスと見なされます。ただし、すぐには明らかではない追加の利点があります。基本的に、それはその部分の合計よりも優れています。メカニズム全体が構成しますます。それはスケーリングします。
Line
クラスをビルディングブロックとして使用する場合:
class Table
{
Line borders[4];
};
その後
int main ()
{
Table table;
}
4つのstd::string
インスタンス、4つのLine
インスタンス、1つのTable
インスタンス、およびすべての文字列の内容を割り当て、すべてが自動的に解放されます。