デストラクタパターン
CoddyのCジャーニー「Object Oriented Programming」セクションの一部 — レッスン 13/61。
malloc を使用して作成されたすべてのオブジェクトは、最終的に解放される必要があります。メモリ割り当てを処理するために create_ 関数を作成したのと同様に、クリーンアップを処理するために free_ 関数を作成します。これはデストラクタパターンです。
重要なポイントは、メモリの解放は確保した時と逆の順序で行う必要があるということです。もしコンストラクタが最初に内部メンバを確保し、その後に構造体自体を確保したのであれば、デストラクタは最初に構造体のメンバを解放し、その後に構造体自体を解放しなければなりません:
typedef struct {
char *name;
int age;
} Person;
void free_person(Person *p) {
if (p == NULL) return; // 安全チェック
free(p->name); // 最初に内部メンバを解放する
free(p); // 次に構造体自体を解放する
}
冒頭の NULL チェックは、誤って無効なポインタが渡された場合のクラッシュを防ぎます。内部の name 文字列を解放した後、構造体自体を解放します。もし先に p を解放してしまうと、p->name へのアクセス手段を失い、メモリリークを引き起こしてしまいます。
デストラクタの使用は簡単です:
Person *alice = create_person("Alice", 30);
// ... aliceを使用する ...
free_person(alice); // 終わったらクリーンアップする
このパターンは、すべてのメモリが適切に解放されることを保証します。これがないと、作成したすべてのオブジェクトが、プログラムのメモリが不足するまでメモリをリークし続けます。
チャレンジ
簡単オブジェクトのライフサイクル全体(作成と破棄の両方)を実証する Movie モジュールを構築しましょう。割り当てられたすべてのメモリを正しい順序で適切にクリーンアップするデストラクタ関数の作成を練習します。
3つのファイルを作成します:
movie.h: 2つのメンバを持つMovie構造体を宣言します:char *title(動的に割り当てられた文字列)とint year。タイトルと年を受け取るコンストラクタcreate_movieと、Movieポインタを受け取るデストラクタfree_movieの両方を宣言します。シンボルMOVIE_Hを使用してインクルードガードを使用してください。movie.c: 両方の関数を実装します。コンストラクタは、構造体とそのタイトル文字列のためのメモリを割り当てる必要があります(ヌル終端文字を忘れないでください)。デストラクタは、割り当ての逆の順序でメモリを解放する必要があります。まず内部のtitle文字列を解放し、次に構造体自体を解放します。安全のために、デストラクタの最初に NULL チェックを含めてください。main.c: 映画を作成し、その情報を表示してから、メモリを適切にクリーンアップします。
2つの入力を受け取ります:映画のタイトル(文字列)と公開年(整数)です。
メインファイルで、create_movie を使用して新しい映画を作成し、その情報を出力してから、free_movie を呼び出してすべてのメモリを解放します。解放後、確認メッセージを出力してください。
出力は次の形式で表示してください:
Movie: {title} ({year})
Memory freed例えば、入力が Inception と 2010 の場合、出力は次のようになります:
Movie: Inception (2010)
Memory freed覚えておいてください:適切なデストラクタの鍵は、逆の順序で解放することです。コンストラクタが最初に割り当てたものは最後に解放され、最後に割り当てたものは最初に解放されます。
チートシート
mallocで作成されたすべてのオブジェクトは、デストラクタ関数を使用して解放する必要があります。デストラクタは、割り当てとは逆の順序でメモリを解放するというパターンに従います。
基本的なデストラクタの構造:
typedef struct {
char *name;
int age;
} Person;
void free_person(Person *p) {
if (p == NULL) return; // 安全性のチェック
free(p->name); // 最初に内部メンバを解放する
free(p); // 次に構造体自体を解放する
}
重要な原則:
- クラッシュを防ぐために、常に最初に
NULLチェックを含めます - 構造体自体を解放する前に、内部メンバを解放します
- 最初に構造体を解放してしまうと、そのメンバへのアクセスができなくなり、メモリリークの原因となります
使用例:
Person *alice = create_person("Alice", 30);
// ... aliceを使用 ...
free_person(alice); // 終了時にクリーンアップする
自分で試してみよう
#include <stdio.h>
#include <stdlib.h>
#include "movie.h"
int main() {
char title[100];
int year;
// 入力を読み込む
scanf("%[^\n]", title);
scanf("%d", &year);
// TODO: create_movie を使用して映画を作成する
// TODO: 次の形式で映画情報を出力する:
// Movie: {title} ({year})
// TODO: free_movie を使用して映画を解放する
// TODO: "Memory freed" と出力する
return 0;
}
このレッスンには短いクイズがあります。レッスンを始めて解答し、進捗を記録しましょう。