Cでの使いやすいオブジェクト指向"的"表現について考える。～その1 クラスの表現、継承

0/2 [はじめに void *](https://qiita.com/developer-kikikaikai/items/02f8c2b5739079f33fb7)
1/2 [クラス表現と継承](https://qiita.com/developer-kikikaikai/items/ee782a4865d9186c5d8c) ←ここ
1/2 2018/07/01 再考しました [再考:クラス表現と継承](https://qiita.com/developer-kikikaikai/items/98d568c442cfbea20cb0)
2/2 [インターフェイス, オーバーライド](https://qiita.com/developer-kikikaikai/items/47b55456ae6e9c287718)

C言語でのオブジェクト指向表現、本題に入ります。
今回はクラス表現と継承。

今回言っているクラスはメンバーを持つ普通のクラスの事と思ってください。
インターフェイスクラスだと表現、継承の印象が大分変わるので、次回改めて書きます。

## **クラスをどう表現するか**

例えばJavaのこんなクラスを考えます。

```Java:HumanClass.java
public class HumanClass{
	public String name;
	private int age;

	//この人が年齢、(もしくは好みとして)ビールが飲めるのか？
	public bool can_drinking_beer();
	//この人がアラサー/アラフォーかの確認
	public bool is_alound(int generation);
}
```
Cで表現してみましょう。

### **Classメンバーの表現**

基本ヘッダーにpublicな構造を定義し、Cファイルにprivateメンバーを書く感じですね。
public methodの前にまずは基本的な表現から。

```C:HumanClass.h
typedef struct human_class{
	char * name;
} human_class;

human_class * human_class_new(コンストラクタパラメータ);
void human_class_init(human_class *, コンストラクタパラメータ);
void human_class_delete(human_class *);
```

```C:HumanClass.c
#define HUMAN_CLASS_PUBLIC \
	char * name;

struct human_class_implement{
//先頭にpublic
HUMAN_CLASS_PUBLIC
	int age;
} human_class_implement;

//プライベートメソッドはCファイル内のstaticな関数で定義
static void is_you_want_to_use_private_metod(XXX);

human_class * human_class_new(コンストラクタパラメータ) {
	human_class_implement * instance = malloc(sizeof(human_class_implement));
	instance->name = malloc(名前の長さ);
	//初期処理
	return (human_class *)instance;
}

void human_class_init(human_class *this, コンストラクタパラメータ) {
	if(!this->name) {
		this->name = malloc(名前の長さ);
	}
	//初期処理

}

void human_class_delete(human_class *this) {
	human_class_implement * instance = (human_class_implement *)this;
	//ここではinstance->ageが使えます。
	...
	free(instance->name);
	free(instance);
}
```

**先頭にpublicメンバーを定義し、インスタンスを渡す際に`human_class`にcastしてあげます。**
後はinitメソッドがあると実体の初期化も出来ますね。

human_classクラスと同じ定義でpublicメンバーを書くいいと思います。

使い方については1点注意が。
*human_class_newを使わないと、privateメンバーのメモリが確保されない！*

というわけで、**privateメンバーが必要なら必ずリソースはnew APIで確保**しましょう。

Object指向関係なく、大抵の場合はnew, free用のAPIはを用意しておくのが好きです。
理由はリソース管理がhuman_classの実装内で完結していることがわかるからです。
はい、好みです。

4/29追記 コメントでいただきましたが、このクラスをどう表現するか？にも様々な手段、趣向があるんですね。別途時間を見つけて考えて纏めてみたいと思います。

さて、メンバーはいいけどpublicメソッドはどうしましょうか。
手段は2つあるかな。
1. 公開APIで表現
2. 関数ポインタで表現

どちらも好きですが、私としては1派です。

### **Public methodの表現1. 公開APIを使う**

こんな感じ

```C:HumanClass.h
typedef struct human_class{
	char * name;
} human_class;

human_class * human_class_new(コンストラクタパラメータ);
bool human_class_can_drinking_beer(human_class *);
bool human_class_is_alound(human_class *, int);
void human_class_delete(human_class *);
```

ヘッダーにそのままpublicメソッドを定義します。実体がないので実体をもらいます。
"human_classのpublicメソッドである"ことがわかる名前の付け方をしておけば、
「human_classはこれで扱うのね。触っていいメンバーはnameなのね」ってのが直感的にわかりやすいと思います。

### **Public methodの表現2. 関数ポインタで表現**

こんな感じ。実装側で定義した関数を`can_drinking_beer`, `is_alound`に代入します。

```C:HumanClass.h
typedef struct human_class{
	char * name;
	bool (*can_drinking_beer)(human_class *);
	bool (*is_alound)(human_class *);
} human_class;

human_class * human_class_new(コンストラクタパラメータ);
void human_class_delete(human_class *, int);
```

使う側はこんな感じ。

```C:izakaya.c
#include "HumanClass.h"

human_class * customer;

int come_customer(void) {
	customer = human_class_new(XXX);

	if(!customer->can_drinking_beer(customer)) {
		printf("お客様、最初の飲み物のご注文はお決まりですか？");
	} else {
		printf("お客様、最初の飲み物のご注文はお決まりですか？生にいたしますか？");
        }

	return;
}

int gohome_customer(void) {
	//お会計で年齢に悩む
	if(customer->is_alound(customer, 40)) {
		printf("...そっと40代のボタンを押す店員");
	}
	human_class_delete(customer);
}

```

よりオブジェクト指向言語の記載に近いですが、`this`ポインタは明示的に渡してあげないといけないんですよね。
好みがありそうですが、
私としては、`is_alound`の定義がいらない分`human_class_is_alound`を直接呼んだ方が見やすい気がします。

オブジェクト指向言語でも、thisを渡すメソッドなら、staticメソッドで実現しますよね？きっと。

### **Cでのクラス表現 まとめ**

- ヘッダー定義にpublicなクラス構造を定義し、new, delete(, init)を用意する。
- Cファイル内にprivateメンバーを(publicメンバーの)下に追加して拡張。
    - newで返す実体は拡張したものをキャストOK
- privateメンバーを利用したい際は、必ずnew, deleteでリソース確保

正直クラスの意識をしなくても、これだけ守って実装するだけでCの実装が一気に見やすくなると思います。

## **クラス継承をどう表現するか**

HumanClassを継承したクラスについて考えましょう

```Java:HumanClass.java
public class FamilyClass extends HumanClass{
	public String fatherName;
	public String motherName;
	public String[] childrenName;
}
```

家族構成が追加されました。さあどうしましょかね。

privateメンバーがいるなら必ずリソースはそのクラスのnewで確保という前提があるので、
前提として**親クラスにprivateメンバーがいない場合の継承**について書いていきます。

こちらは2つ紹介
1. 親クラスのメンバーを持つ
2. 親クラスと同じ定義を先頭に書いてあげる。

1か2で選ぶなら2です。

### **クラス継承の表現 1: 親クラスのメンバーを持つ**

[C 言語によるオブジェクト記述法 COOL ver.2](http://www.sage-p.com/process/cool.htm#6-2:'C 言語によるオブジェクト記述法')がこのイメージです。
human_classは公開APIとしてpublicメソッドを使ってクラスを表現した場合でサンプルを書きます。

```C:FamilyClass.h
typedef struct family_class_{
	human_class super;
	char * fatherName;
	char * motherName;
	int children_num;
	char ** children;
} family_class;

family_class * family_class_new(XXX);
```

```C:FamilyClass.c
#define  super_init(this, XXX) human_class_init(human_class*)(this), XXXX )
#define  super_can_drinking_beer(this) human_class_can_drinking_beer(human_class*)(this))

family_class * family_class_new(XXX) {
	family_class * family = (family_class*) malloc(sizeof(family_class));

	super_init(family, XXX)
	///初期処理色々

	//メンバーはsuperをつけてアクセス
	printf("name:%s", family->super.name)
	
	//親メソッド呼び出しはマクロでキャストして使う。
	if(super_can_drinking_beer(family)) {
		printf("ビールが飲めます！");
	}
	
	return family;
}
```

メソッドはいいけど、メンバーアクセスにsuperが付くのが付いちゃうんですよね。

### **クラス継承の表現 2: 親クラスと同じ定義を先頭に書いてあげる。**

HumanClass側のヘッダーに少し工夫を施します。
関数ポインタで表現した方が良さがイメージしやすいのでこちらの改変


```C:HumanClass.h
typedef struct human_class{
	char * name;
	bool (*can_drinking_beer)(human_class *);
	bool (*is_alound)(human_class *);
} human_class;

#define HUMAN_SUPER \
	char * name;\
	bool (*can_drinking_beer)(human_class *);\
	bool (*is_alound)(human_class *);\

human_class * human_class_new(コンストラクタパラメータ);
void human_class_delete(human_class *, int);
//継承相手向けにhuman_class_init(human_class*　this, XXXX )みたいな関数も定義する。
```

継承用の定義記載をHUMAN_SUPER で用意してあげます。
継承したい人は、同じ定義を先頭に記載。

```C:FamilyClass.h
typedef struct family_class_{
//先頭に定義を羅列。用意されたマクロを書くだけ
HUMAN_SUPER
	char * fatherName;
	char * motherName;
	int children_num;
	char ** children;
} family_class;

family_class * family_class_new(XXX);
```

すると、同じ名前でメンバーアクセスが可能になります！

```C:FamilyClass.c
#define  super_init(this, XXX) human_class_init((human_class*)(this), XXXX )

family_class * family_class_new(XXX) {
	family_class * family = (family_class*) malloc(sizeof(family_class));

	super_init(family, XXX)
	///初期処理色々

	//メンバーは直書きでOK！
	printf("name:%s", family->name)
	
	//親メソッド呼び出しはキャストは必要です。
	if(family->can_drinking_beer((human_class*)family)) {
		printf("ビールが飲めます！");
	}

	return family;
}
```

こちらの方がより継承チックなのでいいかなと。

### **多重継承はどうするの？**

複数のクラスを継承させたいとなると、一気にわかりにくくなります。位置をずらさなきゃいけないんですよね。
`human2_class`という別のクラスを継承しましょう。

```C:FamilyClass.h
typedef struct family_class_{
	human2_class super2;
	human_class super1;
	char * fatherName;
	char * motherName;
	int children_num;
	char ** children;
} family_class;

family_class * family_class_new(XXX);
```

で、`human1_class`のメソッドを呼びたいんだけど…

```C:FamilyClass.c
//位置が先頭じゃないので、human_classの場所を与えてあげないといけない。
void call_super1_init(family_class * this, XXX) {
	human_class_init(&this->super1, XXXXXXXXX);
}

family_class * family_class_new(XXX) {
	family_class * family = (family_class*) malloc(sizeof(family_class));

	call_super1_init(family, XXX)
	///初期処理色々
	...

	return family;
}

```

**位置をずらすため、ラッパーを用意**しなきゃいけません。

### **Cでのクラス継承表現 まとめ**

1 継承らしく利用出来るのは、親クラスにprivateメンバーがいるときのみ
2 うまく書けば継承を上手に表現できる。
3 多重継承は位置検索のラッパーが必要。

…制約中々ありますね。特に1, 3があるので、私のとしては、無理に継承を実現するメリットがあまりないかなと。
なのでいっそ、**「クラス設計の継承をCで実装する時は集約で実現する」**
としてしまった方がいいと思っています。

クラス設計と実際のコードにギャップがありますが、そこは

```C:FamilyClass.h
typedef struct family_class_{
	human_class * super;/*親クラスとしてhuman_class を保持*/
	char * fatherName;
	char * motherName;
	int children_num;
	char ** children;
} family_class;
```

とか1行コメント入れてあげればいいかと。