コンストラクタパターン
CoddyのCジャーニー「Object Oriented Programming」セクションの一部 — レッスン 12/61。
JavaやC++のような言語では、メモリを自動的に割り当ててフィールドを初期化する特殊な関数であるコンストラクタを使用してオブジェクトを作成します。C言語にはそのような機能はありませんが、シンプルなパターンであるcreate_関数を使用してそれをシミュレートできます。
C言語のコンストラクタ関数は、通常3つのことを行います:mallocでメモリを割り当て、すべてのフィールドを適切な値に初期化し、新しいオブジェクトへのポインタを返します:
typedef struct {
char *name;
int age;
} Person;
Person *create_person(const char *name, int age) {
Person *p = malloc(sizeof(Person));
p->name = malloc(strlen(name) + 1);
strcpy(p->name, name);
p->age = age;
return p;
}
オブジェクトがスタックではなくヒープ上に存在するため、この関数はポインタを返します。これは、関数が終了した後もオブジェクトが保持され、自由に受け渡しができることを意味します。呼び出し側では次のように使用します:
Person *alice = create_person("Alice", 30);
printf("%s is %d years old\n", alice->name, alice->age);
コンストラクタが、構造体(struct)の割り当て、文字列(string)のための領域の割り当て、データのコピーといった面倒な詳細をすべてどのように処理しているかに注目してください。呼び出し側は初期値を提供するだけで、すぐに使用可能なオブジェクトを受け取ることができます。これにより、生成ロジックが1か所にカプセル化され、コードがよりクリーンになり、エラーが発生しにくくなります。
チャレンジ
簡単ヒープ上にbookオブジェクトを作成するために、コンストラクタパターンを使用する Book モジュールを構築しましょう。すべてのメモリ割り当てと初期化を処理し、すぐに使用できるポインタを返す create_ 関数の作成を練習します。
3つのファイルを作成します:
book.h: 2つのメンバを持つBook構造体を宣言します:char *title(動的に割り当てられた文字列へのポインタ)とint pagesです。また、タイトル文字列とページ数を受け取り、新しく作成されたBookへのポインタを返すコンストラクタ関数create_bookを宣言します。シンボルBOOK_Hを使用してインクルードガードを記述してください。book.c:create_book関数を実装します。コンストラクタは、Book構造体自体のメモリを割り当て、次にタイトル文字列用に別のメモリを割り当て(ヌル終端文字を考慮することを忘れないでください)、タイトルを新しいメモリにコピーし、pagesフィールドを設定して、新しいBookへのポインタを返す必要があります。main.c: コンストラクタを使用してbookを作成し、その情報を表示します。
2つの入力を受け取ります:book title(文字列)と number of pages(整数)です。
mainファイルで、create_book を使用して提供された値で新しいbookを作成し、次の形式でその情報を出力してください:
Title: {title}
Pages: {pages}例えば、入力が The C Programming Language と 272 の場合、出力は次のようになります:
Title: The C Programming Language
Pages: 272コンストラクタはすべての作成ロジックをカプセル化していることを忘れないでください。呼び出し側は初期値を提供するだけで、すぐに使用できる完全に初期化されたオブジェクトを受け取ります。
チートシート
C言語におけるコンストラクタ関数は、メモリを割り当ててフィールドを初期化することで、オブジェクトの作成をシミュレートします。通常、create_という命名パターンに従います。
コンストラクタ関数は、主に3つのことを行います:
mallocを使用してメモリを割り当てる- すべてのフィールドを適切な値に初期化する
- 新しいオブジェクトへのポインタを返す
コンストラクタパターンの例:
typedef struct {
char *name;
int age;
} Person;
Person *create_person(const char *name, int age) {
Person *p = malloc(sizeof(Person));
p->name = malloc(strlen(name) + 1);
strcpy(p->name, name);
p->age = age;
return p;
}
コンストラクタの使用:
Person *alice = create_person("Alice", 30);
printf("%s is %d years old\n", alice->name, alice->age);
オブジェクトはヒープ上に存在し、関数が終了した後も存続するため、関数はポインタを返します。これにより、作成ロジックが1か所にカプセル化され、コードがよりクリーンになり、エラーが発生しにくくなります。
自分で試してみよう
#include <stdio.h>
#include <stdlib.h>
#include "book.h"
int main() {
char title[256];
int pages;
// 入力を読み込む
fgets(title, sizeof(title), stdin);
// 改行があれば削除する
for (int i = 0; title[i]; i++) {
if (title[i] == '\n') {
title[i] = '\0';
break;
}
}
scanf("%d", &pages);
// TODO: 提供された値を使用して create_book で新しい本を作成する
// TODO: 必要な形式で本の情報を出力する:
// Title: {title}
// Pages: {pages}
// TODO: 完了時に割り当てられたメモリを解放するのを忘れないこと
return 0;
}
このレッスンには短いクイズがあります。レッスンを始めて解答し、進捗を記録しましょう。