メモリ領域とnew演算子の有無について

  • 0

メモリ領域とnew演算子の有無について

Category : C++

どうも、ろっさむです。

この記事はC++ Advent Calendar 2016の23日目の記事です。

昨日は@pink_bangbiさんのC++で enable_if を使うコードのベストプラクティスでした。

今回はメモリ領域とnew演算子の有無に関して書いていきます。

 

メモリ領域について

5種類のメモリ領域

C++では異なる特性を持つ5種類のメモリ領域が存在しています。

(※ただしc++の規格にはスタックもヒープも書いていません。フリーストアは書いてます)

  • Const Data

コンパイル時に値がわかるデータ(例えばstatic指定されたクラス上の変数やグローバル変数、文字列リテラル等)がここに格納されます。ただしクラスオブジェクトは格納することができません。Const Dataに格納されている値はプログラムが終了しない限り利用可能です。また、値は読み取り専用で変更をすることはできません。規格として決められています。ポインタを用いれば書き換え可能になってしまう環境もありますが、結果がどうなるかは保証されません。自分の環境ではうまくいっても他の環境では例えばOSによりプログラムが強制終了させられることもあります。

  • Stack

自動変数が格納されます。変数は宣言と同時に作成・割り当てが行われ、そのオブジェクトが作られたスコープから出たら破棄されて、割り当てが解除されます。なので初期化が行われていない領域を割り当てられることはありません。確保済みの連続したメモリとスタックポインタの増減で実装されており、メモリの割り当て速度は動的ストレージであるヒープやフリーストアよりもはるかに高速となります。しかし、これらの動的ストレージに比べると非常にサイズが小さいです。なのでプログラムを書くときはスタックメモリの使用は必要最小限にしなければなりません。使用しすぎるとスタックオーバーフローが発生しプログラムが強制終了することになります。

  • Free Store

new・deleteによって割り当て・解放される、二つある動的メモリ領域の内の一つです。ただしここで言うヒープとフリーストアは概念的なものとなります。ヒープとフリーストアは同じ領域ではないと仮定した場合に、どちらかで確保したメモリをもう一方で安全に解放することはできないのは容易に想像がつきます。また、変数の存在期間を、メモリが割り当てられて居る時間よりも短くすることが可能です。つまり、フリーストアにある変数は即座に初期化されることなくメモリが割り当てられる可能性があり、メモリがすぐに割り当て解除がされずに破壊される可能性があります。

  • Heap

malloc・freeによって割り当て・解放される、二つある動的メモリ領域の内の一つです。ヒープから割り当てられたメモリはクラス型のオブジェクトに使用できます。なのでフリーストアオブジェクトに関する注意事項もここで同様に適用されます。

  • Global/Static

グローバル変数または静的変数及びオブジェクトはプログラムの起動時に割り当てられた記憶域を持ちますが、プログラムの実行が開始されるまでは初期化されません。例えば関数内の静的変数はプログラムの実行がその定義を最初に通る時に飲み初期化されます。翻訳単位全体のグローバル変数の初期化の順序は定義されていません。グローバルオブジェクト間の依存関係を管理するには注意が必要です。初期化されていないプロトタイプの記憶域は常にvoid *を使ってアクセスしたり操作したりすることができますが、非静的メンバーやメンバ関数はオブジェクトの実際の寿命の外で使用または参照することはできません。

 

new演算子の有無による違い

まずは以下のコードを眺めて下さい。

ローカルスコープ内でnewをつけず宣言だけをした場合、Javaでは変数にはインスタンスそのものではなくインスタンスのポインタが格納されるため、この場合は中身が空っぽだと怒られてしまいますが、C++では暗黙的にコンストラクタが呼びだされます。今回の場合は引数付きコンストラクタを呼び出しているので自動変数rosaには文字列”rossam”が問題なく格納できています。この時rosaは先ほど説明をしたスタック領域に実態が確保されます。

 

引数付きコンストラクタが暗黙的に呼ばれる機能(変換コンストラクタ)について知りたい方は以下の記事参照
C++の変換コンストラクタについて

 

Hoge()は参照型関数なのでローカルスコープ内のrosaを返そうとします。が、ローカルスコープ内で宣言されたオブジェクトは関数が戻る時に破棄、つまりメモリが解放されてしまいます。こうなると、たまたま動作することもありますが、最悪Segmentation Faultという、見てはいけないメモリ領域を参照した時のエラーが起こります。

スクリーンショット 2016-05-11 16.00.45

ここで先ほどのstring変数をnewをつけて宣言することでヒープ領域に確保されるようになり、スコープを抜けた際にメモリが解放されることはありません。(ただし自分でdeleteを行って解放する必要があります。ただし人的要因によるバグ(deleteし忘れ等で予期せぬ事態)が発生する可能があるので、できればスマートポインタを使用しましょう)

 

今回の記事は以上となります。

1時間ちょいオーバーしてしまい、間に合いませんでした…すいませんすいません。

でもとりあえず書けるだけ書いたので満足です。(マサカリが怖いですが)

みなさん良いクリスマスをお過ごし下さい。

僕は嫁とFF15と龍が如く6を楽しみながらケンタッキーを頬張ります。それでは。

 

参考資料

GotW #9 「Memory Management – Part I」

大熊猫のペーじ 「メモリ領域の種類」

メモリスタック

Gobble up pudding 「関数内で宣言した配列変数をreturnしてはいけない」



Leave a Reply