回転:C++ダイナミックメモリ作成とメモリ管理学習ノート[3]



3 C
++
内ダイナミックメモリの作成
3.1 new operatorとdelete operator
C++のダイナミックメモリnew operatorを作成するには、スタック内でオブジェクトにメモリを割り当てる(C++のoperator newには長さ計算、タイプ変換、セキュリティチェックが組み込まれている)2つのステップがあります.メモリの割り当てに成功した場合、メモリに適切な構造関数を呼び出して初期化します.
New operatorは実際には常に標準Cのmalloc()で完了し、delete operatorも標準Cのfree()で完了する.[Inside the C++Object Modelを参照]
delete operatorもそれに応じて2つのステップに分けられます:対応するクラスの構造関数を呼び出します;メモリを解放します.
注意:deleteはvoidポインタで、唯一発生したのはメモリを解放することです.タイプ情報がなくてもコンパイラに呼び出す構造関数を知らせることができません.deleteはNULLポインタで、何もしていないので安全です.
Delete後のポインタは自動的に0にクリアされず、ポインタが指すオブジェクトのライフタイムはdeleteによって終了する.deleteポインタの後、アドレス上のオブジェクトは合法ではないが、アドレス自体は合法的なプログラム空間を表しており、それを操作するには定義されていないため、deleteポインタを構築した後、ポインタを0に割り当て、複数回削除したり誤用したりすることを避ける.
例を示します:(疑似コードによって2つのステップが明確に表示されます)
Point3d *origin = new Point3d;
delete origin;
疑似コードは次のようになります.
Point3d *origin;
if ( origin = __new( sizeof( Point3d )))
//スペースの割り当て
     origin = Point3d::Point3d( origin );//コンストラクタ初期化の呼び出し
if ( origin != 0 )
{
    Point3d::~Point3d( origin );//解析関数の呼び出し
     __delete( origin );//スペースの解放
}
3.2 operator newとoperator delete
3.2.1 new operatorとoperator newの違い
String* ps = new string(
“string”);その中のnewはnew operatorを表し、言語で構築され、その意味を変えることができず、いつも同じことをしています.あるタイプのオブジェクトを格納するのに十分なメモリを割り当てます.コンストラクション関数を呼び出し、割り当てられたメモリ内のオブジェクトに初期値を設定します.特定のタイプのポインタを返し、オブジェクトの作成を完了します.プログラマはその動作を変更することはできません.オブジェクトを収容するためのメモリの割り当て動作しか変更できません.New operatorはある関数を呼び出し、必要なメモリ割り当て動作を実行し、その関数を書き換えたり再ロードしたりして、その動作を変えることができます.この関数はoperator newです.
operator new
の通常宣言はvoid*operator new(size_t size);voidポインタを返し、生鮮で初期値のないメモリを指します.Operator newの唯一のタスクは、メモリを割り当て、operator newが返すメモリを取得してオブジェクトに変換することです.new operatorの責任です.例:
new operator:string *ps = new string("Memory Management");
operator newに変換:
void *memory = operator new(sizeof(string));//未処理メモリをStringオブジェクトとして取得
  call string::string("Memory Management") on *memory;//
メモリ内のオブジェクトstring*ps=static_を初期化cast(memory);
//psポインタが新しいオブジェクトを指す
3.2.2標準でサポートされている3つのnew形式
A plain new
(単純new):一般的に使用されるnewでは、追加のパラメータは受け入れられません.plain newは異常なタイプstd::bad_allocを放出します.これは標準的な適応状態です.初期のC++の舞台では、この状態は現在とは非常に異なります.newは0を返して失敗を指摘し、malloc()と非常に似ています.
B nothrow new
:追加のパラメータを受け入れることができます.一定の環境では、NULLポインタを返して失敗を表すのは依然として良い選択です.C++標準委員会はこの問題に気づいたので、特別なnewオペレータバージョンnothrow newを定義し、0を返して失敗を示すことにした.nothrow new文は、通常のnew文と似ています.変数がstd::nothrow_に関連する以外は、t.
C placement new
:メモリの指定された場所にオブジェクトを構築します.これにより、placement newを使用すると、明示的なコンストラクション関数呼び出しに相当する新しいスペースは割り当てられません.既存のオブジェクトでコンストラクション関数を呼び出すのは意味がありません.コンストラクション関数はオブジェクトを初期化するために使用され、1つのオブジェクトは初期値を与えるときに1回しか初期化できないからです.Placement newは、割り当てられているが未処理のrawメモリの中にオブジェクトを構築するために使用されます.
注意:placement newを使用する場合は、t->T:~T();
3つのnewのプロトタイプ宣言は以下の通りです.
Plain new: void*::operator new(std::size_t sz) throw (std::bad_alloc);
Nothrow new: void*::operator new(std::size_t sz, const std::nothrow_t &nt) throw();
Placement new: void*::operator new(std::size_t sz, void* ptr) throw();
使用法のリストは次のように比較されます.
Operator new
追加のパラメータ
メモリ割り当てを行うかどうか
失敗するかどうか
異常を投げ出す
置換可能かどうか
Plain
なし
はい
はい(投げ出す)
Std::bad_alloc
はい
Nothrow
Std::nothrow
はい
はい(戻り)
なし
はい
Placement
Void*
いいえ
いいえ
なし
いいえ
Operator new
一般にマルチステートはサポートされません.
デモの例の必要性
3.3 operator newとoperator deleteの再ロード
operator newを再ロードする問題:new operatorの場合、変更できるのはoperator new部分だけです.すなわち、既存のメモリ割り当て方式しか変更できません.このメモリを初期化するためにコンストラクション関数部分を呼び出すことはできません.
operator newのリロード:メモリの割り当て方法;割り当てに失敗した場合に必要なこと:戻りや異常の投げ出しなど.
3.3.1グローバルnew/deleteの再ロード
グローバルnewをリロードすると、デフォルトのバージョンは完全にアクセスできず、この再定義でも呼び出すことができないため、使用時に慎重に考慮する必要があります.
リロードされたoperator newにはsize_が含まれている必要があります.tパラメータは、メモリを割り当てるオブジェクトの長さを表し、その長さに等しいオブジェクトを指すポインタを返さなければならない.要件を満たすストレージユニットが見つからない場合、コンストラクション関数は呼び出されず、失敗を示す戻り値以外に例外を投げ出す必要があります(カスタマイズ可能).
Operator deleteパラメータは、operator newによって割り当てられたメモリを指すvoid*(構造関数が呼び出されたポインタ)であり、その戻りタイプはvoidである.
デモの例
3.3.2リロードクラスのnew/delete
リロードクラスのnewは、実際にはstaticメンバー関数を作成します.このnewはクラスオブジェクトの作成にのみ機能し、デフォルトのグローバルバージョンnewには影響しませんが、名前の非表示の問題に注意する必要があります.
注意:任意のクラスに独自のoperator newまたはoperator new[]が提供されている場合、対応するクラス関連バージョンのplain new、placement new、nothrow newを同時に提供する必要があります.そうしないと、名前によって隠されたルール(グローバルnewを隠した)に基づいて、一致するエラーが発生します.
クラスにoperator newとoperator deleteがリロードされると、このクラスのオブジェクトがいつ作成されるかにかかわらず、これらのリロード演算子が呼び出されますが、クラスのオブジェクト配列を作成すると、グローバルoperator newがすぐに呼び出され、この配列に十分なメモリを割り当てるために使用されるため、このような状況を回避するには、operator newの配列バージョン:operator new[]とoperator delete[]を再ロードする必要があります.
デモ例:(item 36 in exceptional c++)
1.             //  1B delete       D
2.             class B 
3.             {
4.             public:
5.               virtual ~B();
6.               void operator delete ( void*, size_t ) throw();
7.               void operator delete[]( void*, size_t ) throw();
8.               void f( void*, size_t ) throw();
9.             };
10.          class D : public B
11.          {
12.          public:
13.            void operator delete ( void* ) throw();
14.            void operator delete[]( void* ) throw();
15.          };
16.          void f()
17.          {
18.            //  2:       ,       delete        ,   ?
19.       D* pd1 = new D; 
20.       delete pd1;
21.       B* pb1 = new D;
22.       delete pb1;
23.       D* pd2 = new D[10];
24.       delete[] pd2;
25.       B* pb2 = new D[10];
26.       delete[] pb2;
27.     
28.       //  3:           ?
29.       typedef void (B::*PMF)(void*, size_t); 
30.       PMF p1 = &B::f;
31.       PMF p2 = &B::operator delete;
32.     }

3.4メモリの割り当てに失敗した問題
Aメモリの割り当てに失敗したレポート方式:ほとんどのnewはbad_を投げ出すことによってalloc異常は割り当て失敗を報告する.nothrow newでは、Cのmalloc方式で失敗を報告します.すなわち、空のポインタだけを返し、異常は決して放出されません.
Bメモリの割り当てに失敗した処理手順:set_new_handler呼び出しエラー処理関数new_handlerは、関数を指すポインタをチェックし、ポインタが0でない場合、指向する関数が呼び出されます.
new_handlerとset_new_handlerのプロトタイプは以下の通りです.
typedef void (*new_handler)();
new_handler set_new_handler(new_handler p) throw();
new_handlerはtypedefであり、パラメータも戻り値もない関数ポインタを示し、set_new_handlerは関数でnew_が必要ですhandlerパラメータを1つのnew_に返しますhandler.
Set_new_handlerのパラメータポインタが指す関数はoperator newが十分なメモリを構成できない場合に呼び出すべき関数であり、その戻り値は関数ポインタであり、以前にログインしたnew_を指す.handler.使用例は次のとおりです.
void nomorememory()
{
       cerr << "unable to satisfy request for memory/n";
       abort();
}
int main()
{
       set_new_handler(nomorememory);
       int *pbigdataarray = new int[100000000];
       ...
}