C言語でオブジェクト指向プログラミング 第1回

皆さん、こんにちは。技術開発グループのn-ozawanです。
夏ですね。クーラーの涼しさになれると、外出たときの反動がすごいです。

本題です。
たまにはマニアックなネタをしれっと投稿したい今日この頃。C言語でオブジェクト指向プログラミングと言えばC++ですよね。でもちょっと待ってください。実はC言語だけでオブジェクト指向プログラミングがそれっぽく出来るんです。今日はそんなお話です。

C言語でクラスっぽいものを作ってみよう

C言語は手続き型プログラミングの1つであり、オブジェクト指向プログラミングではありません。なので、C言語に「クラス」というものはありません。

クラスの定義

オブジェクト指向におけるオブジェクトとは、プログラミング視点においてはデータと処理の集まりです。データは「メンバ変数」や「フィールド」、「プロパティ」とも呼ばれており、要は変数です。処理は「メソッド」のことですね。

JavaやC++、C#などのオブジェクト指向言語では、データと処理の集まりをclassによりコーディング出来るようになっています。例えばC++では以下のようなコードになります。

#include <stdio.h>
#include <string.h>

class Button {
private:
  // メンバ変数
  char label[256];

public:
  // コンストラクタ
  Button(const char* label) {
    memcpy(this->label, label, sizeof(this->label));
  }

  // メソッド
  void push() {
    printf("push `%s` button!\n", this->label);
  }
};

// メイン処理
int main() {
  Button* b = new Button("hoge");

  b->push();

  delete b;
  return 0;
}

class Buttonは、ボタンを表すクラスになります。ボタンにはラベルがあり、ボタンを押下したときの処理があります。上記コードではchar label[256]がラベルを、void push()がボタン押下処理になります。また、ラベルを初期化するためにコンストラクタが定義されています。

上記は、hogeというボタンを押下すると「push `hoge` button!」とコンソールに表示されるコードになります。これをC言語でコーディングすると以下になります。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

// メンバ変数
typedef struct {
  char label[256];
} Button;

// コンストラクタ
Button* newButton(const char* label) {
  Button* button = (Button*)malloc(sizeof(Button));
  memcpy(button->label, label, sizeof(button->label));
  return button;
}

// メソッド
void pushButton(Button* button) {
  printf("push `%s` button!\n", button->label);
}

// メイン処理
int main() {
  Button* b = newButton("hoge");

  pushButton(b);

  free(b);
  return 0;
}

1つずつ見ていきましょう。

メンバ変数

// メンバ変数
typedef struct {
  char label[256];
} Button;

繰り返しになりますが、C言語は手続き型言語であり、オブジェクト指向言語でありません。ですので、C++のようなデータと処理の集まりを定義するclassというものはありません。よって、C言語ではデータと処理を別々に定義する必要があります。

ご存知の通り、C言語でデータの集まりを定義するのはstructになります。なので、メンバ変数はstructで定義しています。

コンストラクタ

// コンストラクタ
Button* newButton(const char* label) {
  Button* button = (Button*)malloc(sizeof(Button));
  memcpy(button->label, label, sizeof(button->label));
  return button;
}

コンストラクタを定義します。と言っても特別なことは何もなく、単にstruct Buttonのメモリ領域を確保して、メンバ変数を初期化して、その結果を返却しているだけになります。

メソッド

// メソッド
void pushButton(Button* button) {
  printf("push `%s` button!\n", button->label);
}

C言語では処理は関数でコーディングします。しかし、C++のthisようにメソッドからメンバ変数を参照することは出来ません。なので第1引数にメンバ変数となるstruct Buttonの参照を渡してあげる必要があります。

メイン処理

// メイン処理 (C)
int main() {
  Button* b = newButton("hoge");

  pushButton(b);

  free(b);
  return 0;
}

newButton()関数でButtonのインスタンスを生成します。そして、pushButton()関数により、Buttonの処理が実行されます。以下のC++のメイン処理を比べてみると、インスタンスの生成~処理~インスタンスの解放が同じように並んでいることから、オブジェクト指向プログラミングと似たようなことがC言語でも出来ることに気付くかと思います。

// メイン処理 (C++)
int main() {
  Button* b = new Button("hoge");

  b->push();

  delete b;
  return 0;
}

おわりに

今回の投稿は正直なところ、C言語を熟知されている方から見ると困惑されることかと思います。何か特別なことをやっているかのようなタイトルですが、やっていることは関数に構造体の参照を渡すだけの、C言語ではごく普通の当たり前のことをしているだけです。

C言語は手続き型言語です。なのでオブジェクト指向を意識してコーディングすることはなく、普段、当たり前のようにコーディングしていたことが、実はオブジェクト指向に近いコードでもあると気付く人は少ないのではないでしょうか。そこに気付くと、もしかしたらC言語の新たな一面が見れるのかもしれませんね。

とはいえ、オブジェクト指向でコーディングするのであれば、JavaやC++、C#をお勧めします。

次回(いつになるか分かりませんが)は、これを継承、委譲させてみたいと思います。
ではまた。

Recommendおすすめブログ