プロジェクトの概要
CoddyのCジャーニー「Object Oriented Programming」セクションの一部 — レッスン 44/61。
チャレンジ
簡単関数ポインタを通じて、あらゆる図形ができることを定義するインターフェースである、ベースの Shape 構造体を作成し、Shape Drawerプロジェクトの基礎を築きましょう。
このポリモーフィックなシステムを構築するために、2つのファイルを作成します:
shape.h: ここでコアとなるインターフェースを定義します。まず、Shape構造体の前方宣言を作成します。次に、2つの関数ポインタ型を定義します:DrawFunc—const Shape*パラメータを受け取り、何も返しませんAreaFunc—const Shape*パラメータを受け取り、doubleを返します
draw(DrawFunc型)とarea(AreaFunc型)を含むShape構造体自体を定義します。この構造体は純粋にインターフェースとして機能します。図形固有のデータは保持せず、振る舞いのみを保持します。main.c: シンプルなテスト用の図形を作成して、インターフェースが機能することを実証します。2つの関数を実装してください:test_draw—Drawing test shapeと出力しますtest_area—42.0を返します
Shapeインスタンスを作成し、これらのテスト関数に紐付け、インターフェースを介して両方を呼び出します。面積の結果を小数点以下1桁で出力してください。
出力は、関数ポインタが正しく機能していることを示す必要があります:
Drawing test shape
Area: 42.0なぜ前方宣言が必要なのですか? 構造体が完全に定義される前に typedef struct Shape Shape; が現れるのはなぜか不思議に思うかもしれません。これは、DrawFunc と AreaFunc がパラメータ型で const Shape* を参照しているため必要です。しかし、ファイルのその時点では、コンパイラはまだ完全な構造体定義を見ていません。前方宣言はコンパイラに「Shape という名前の構造体が存在する」ことを伝えます。これだけで、その構造体へのポインタを使用するには十分です。完全な定義は後で続きます。このパターンは、構造体が(直接、または関数ポインタを介して)自分自身を参照する必要がある場合に、C言語でよく使われます。
ここでの重要な洞察は、Shape は円や長方形については何も知らないということです。ただ、どんな図形も自分自身を draw(描画)でき、area(面積)を計算できなければならないということだけを知っています。今後のレッスンでは、このベース構造体を埋め込み、独自の実装を提供する具体的な図形を作成します。ヘッダーファイルにはインクルードガードを使用することを忘れないでください。
自分で試してみよう
#include <stdio.h>
#include "shape.h"
// TODO: test_draw 関数を実装する
// - const Shape* 型の引数を取る
// - "Drawing test shape" と出力する
void test_draw(const Shape* shape) {
// TODO: 必要なメッセージを出力する
}
// TODO: test_area 関数を実装する
// - const Shape* 型の引数を取る
// - 42.0 を返す
double test_area(const Shape* shape) {
// TODO: テスト用の面積の値を返す
}
int main() {
// TODO: Shape インスタンスを作成する
// TODO: shape を test_draw 関数と test_area 関数に紐付ける
// TODO: インターフェース経由で draw を呼び出す
// TODO: インターフェース経由で area を呼び出し、小数点第1位まで表示する
// フォーマット: "Area: %.1f\n"
return 0;
}