Page Top

今回は… anchor.png

オブジェクト指向について少しだけ触れていきます。
まず、オブジェクト指向とはどういうものなのかを簡単に説明すると、
「構造体をもっと使いやすくしたもの」
といった感じです。
C言語でかなり無理矢理にオブジェクト指向的なプログラムを組もうと考えた場合、まさに構造体をあれこれすることになります。

さて、前回までに構造体とポインタを使って関数に渡す云々という話をしましたが、これがオブジェクト指向の入り口になります。
たとえば、次のようなプログラムを組む時のことを考えてみます。

  • 人がいます(学生とします)。(「人」型の構造体の定義)
  • 一日の行動をプログラムで表します。(メインの流れ)
  • 行動は以下のとおりです
    • 起床(WakeUp)
    • 食事(Eat)
    • 移動(Move)
    • 勉強(Study)
    • 就寝(Sleep)
  • 行動のたびに、行動開始時に現在「人」がいる場所を表示します
  • 起床時「人」は自室にいます。

このような場合、主人公となる「人」が一人いて、その人が行動すると考えることができます。
ところで、その「人」は名前があったりしますね。
たとえばここでは仮に「前原圭一」としましょう。
「人」型の変数、「前原圭一」です。

「人」という、種族や集団のような、種類、型を表すものの呼び方を、オブジェクト指向ではクラスと言います。
また、「前原圭一」のような、「人」の集団に属する個に対する呼び方を、オブジェクト志向ではインスタンスと言います。
そんでもって、「人」や「前原圭一」のようなクラスやインスタンスをひっくるめて、オブジェクトと言います。
…ということで以降の表現を統一しますね。(この辺は表記にブレが多いのです。オブジェクト=インスタンスだと言ったり、オブジェクト=クラスだといったり、オブジェクト⊃[クラス,インスタンス]だといったり…ある程度はオブジェクト=インスタンスという傾向が強いようですが…)
この辺の詳しい解説はC++やJavaなどの解説ページや、Wikipediaなどで情報が収集できます。独習してみましょう。

さて、プログラムのほうですが…
「人」型は、「名前」をメンバに持っていて、さらに、現在位置を知っていなければならないので、「場所」も保存しておかなければなりません。
行動については、
「起床」は、誰が起きるかを指定するため、「人」を引数にします。
「食事」は、誰が何を食べるかを指定するため、「人」と、食べ物の名前を引数にします。
「移動」は、誰がどこに行くかを指定するため、「人」と、場所の名前を引数にします。
「勉強」は、誰が勉強するのかを指定するため、「人」を引数にします。
「就寝」は、誰が寝るのかを指定するため、「人」を引数にします。
さて、この条件でプログラムを書いてみるので、サンプルコードをご覧ください。
(これがC言語でやるようなオブジェクト指向プログラミングのやり方です。)

Page Top

今回のミソ anchor.png

  • オブジェクトの入り口は構造体をポインタで関数に渡すこと
  • 種類や型を示す呼び方をオブジェクト志向では「クラス」という
  • 種類や型に属する個を示す呼び方をオブジェクト志向では「インスタンス」という
Page Top

サンプルコード anchor.png

filesample1501p.d
Everything is expanded.Everything is shortened.
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
 
 
-
!
-
-
!
-
!
!
 
-
!
-
|
|
!
-
!
-
|
|
!
-
|
!
-
|
|
|
!
-
!
-
|
|
!
-
!
-
|
|
!
 
-
!
-
|
|
-
|
|
!
|
|
|
|
|
|
|
|
|
|
|
|
|
|
!
import std.stdio;
 
// 「人」型を定義
struct Parson
{
    // 名前
    char[] name;
    // 場所
    char[] location;
}
 
// 起床
void WakeUp(Parson* parson)
{
    writefln( "%sは現在%sにいます。", parson.name, parson.location );
    writefln( "%sは%sで目を覚ました。\n", parson.name, parson.location );
}
// 食事
void Eat(Parson* parson, char[] foodName)
{
    writefln( "%sは現在%sにいます。", parson.name, parson.location );
    writefln( "%sは%sを食べた。少し元気になった。\n", parson.name, foodName );
}
// 移動
// locationName は移動先
void Move(Parson* parson, char[] locationName)
{
    writefln( "%sは現在%sにいます。", parson.name, parson.location );
    writefln( "%sは%sから%sへ移動した。\n", parson.name, parson.location, locationName );
    parson.location = locationName;
}
// 勉強
void Study(Parson* parson)
{
    writefln( "%sは現在%sにいます。", parson.name, parson.location );
    writefln( "%sは勉強した。少し賢くなった。\n", parson.name );
}
// 就寝
void Sleep(Parson* parson)
{
    writefln( "%sは現在%sにいます。", parson.name, parson.location );
    writefln( "%sは%sで眠りに就いた。\n", parson.name, parson.location );
}
 
// メインの流れ
int main(char[][] args)
{
    Parson keiichi;
    with (keiichi)
    {
        name = "前原圭一".dup;
        location = "自室".dup;
    }
    WakeUp( &keiichi );
    Move( &keiichi, "食卓".dup );
    Eat( &keiichi, "朝食".dup );
    Move( &keiichi, "学校".dup );
    Study( &keiichi );
    Eat( &keiichi, "お昼の弁当".dup );
    Study( &keiichi );
    Move( &keiichi, "家".dup );
    Move( &keiichi, "食卓".dup );
    Eat( &keiichi, "夕食".dup );
    Move( &keiichi, "自室".dup );
    Study( &keiichi );
    Sleep( &keiichi );
    return 0;
}
+  Tango用はこちら
filesample1501t.d
Everything is expanded.Everything is shortened.
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
 
 
-
!
-
-
!
-
!
!
 
-
!
-
|
|
|
|
!
-
!
-
|
|
|
|
!
-
|
!
-
|
|
|
|
|
!
-
!
-
|
|
|
|
!
-
!
-
|
|
|
|
!
 
-
!
-
|
|
-
|
|
!
|
|
|
|
|
|
|
|
|
|
|
|
|
|
!
import tango.io.Stdout;
 
// 「人」型を定義
struct Parson
{
    // 名前
    char[] name;
    // 場所
    char[] location;
}
 
// 起床
void WakeUp(Parson* parson)
{
    Stdout.formatln( "{}は現在{}にいます。",
        parson.name, parson.location );
    Stdout.formatln( "{}は{}で目を覚ました。\n",
        parson.name, parson.location );
}
// 食事
void Eat(Parson* parson, char[] foodName)
{
    Stdout.formatln( "{}は現在{}にいます。",
        parson.name, parson.location );
    Stdout.formatln( "{}は{}を食べた。少し元気になった。\n",
        parson.name, foodName );
}
// 移動
// locationName は移動先
void Move(Parson* parson, char[] locationName)
{
    Stdout.formatln( "{}は現在{}にいます。",
        parson.name, parson.location );
    Stdout.formatln( "{}は{}から{}へ移動した。\n",
        parson.name, parson.location, locationName );
    parson.location = locationName;
}
// 勉強
void Study(Parson* parson)
{
    Stdout.formatln( "{}は現在{}にいます。",
        parson.name, parson.location );
    Stdout.formatln( "{}は勉強した。少し賢くなった。\n",
        parson.name );
}
// 就寝
void Sleep(Parson* parson)
{
    Stdout.formatln( "{}は現在{}にいます。",
        parson.name, parson.location );
    Stdout.formatln( "{}は{}で眠りに就いた。\n",
        parson.name, parson.location );
}
 
// メインの流れ
int main(char[][] args)
{
    Parson keiichi;
    with (keiichi)
    {
        name = "前原圭一".dup;
        location = "自室".dup;
    }
    WakeUp( &keiichi );
    Move( &keiichi, "食卓".dup );
    Eat( &keiichi, "朝食".dup );
    Move( &keiichi, "学校".dup );
    Study( &keiichi );
    Eat( &keiichi, "お昼の弁当".dup );
    Study( &keiichi );
    Move( &keiichi, "家".dup );
    Move( &keiichi, "食卓".dup );
    Eat( &keiichi, "夕食".dup );
    Move( &keiichi, "自室".dup );
    Study( &keiichi );
    Sleep( &keiichi );
    return 0;
}
Page Top

実行結果 anchor.png

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
$ dmd -run sample1501p.d
前原圭一は現在自室にいます。
前原圭一は自室のベッドで目を覚ました。
 
前原圭一は現在自室のベッドにいます。
前原圭一は自室のベッドから食卓へ移動した。
 
前原圭一は現在食卓にいます。
前原圭一は朝食を食べた。少し元気になった。
 
前原圭一は現在食卓にいます。
前原圭一は食卓から学校へ移動した。
 
前原圭一は現在学校にいます。
前原圭一は勉強した。少し賢くなった。
 
前原圭一は現在学校にいます。
前原圭一はお昼の弁当を食べた。少し元気になった。
 
前原圭一は現在学校にいます。
前原圭一は勉強した。少し賢くなった。
 
前原圭一は現在学校にいます。
前原圭一は学校から家へ移動した。
 
前原圭一は現在家にいます。
前原圭一は家から食卓へ移動した。
 
前原圭一は現在食卓にいます。
前原圭一は夕食を食べた。少し元気になった。
 
前原圭一は現在食卓にいます。
前原圭一は食卓から自室へ移動した。
 
前原圭一は現在自室にいます。
前原圭一は勉強した。少し賢くなった。
 
前原圭一は現在自室にいます。
前原圭一は自室のベッドで眠りに就いた。
Page Top

まとめ anchor.png

さて、コードをみるとわかると思いますが、WakeUpにしろEatにしろ、関数の第一引数に常にParson*という「人」型を示すポインタを引数に取っています。
関数を定義するときも、関数を使用するときも、面倒ですね。
………まぁ、面倒と思うかどうかは人それぞれだとは思いますが…
少なくとも、オブジェクト指向というのは、このような使い方をする場合が非常に多いので、D言語にはこれを簡単にする方法が備わっています。
次回はそんな話です。

Page Top

#02 メンバ関数 anchor.png

Page Top

今回は… anchor.png

メンバ関数についてお話します。
メンバ関数とは、構造体などが持つ、メンバの、関数のことです。
など、というのは、構造体だけでなく、のちに解説するクラス(structに対するclassというもの)や共有体(union)もそうだからです。

さて、前回ではいちいち

WakeUp( &keiichi );

などとして、構造体のポインタを関数に引き渡してやりました。
メンバ関数では、

keiichi.WakeUp();

のように、"."(ピリオド)を使ってアクセスします。
引数の構造体の変数へのポインタは必要ありません。
これによって、 WakeUp という関数が、より Parson 構造体に密接した関係であるように見えるようになります。
また、メンバ関数の中では、メンバ変数にアクセスする際に parson.name のようにしていたような、変数名の必要がなくなります。
詳しくはサンプルコードのメンバ関数の中で name や location に注目してください。
このようなことを自動的にやるのが、メンバ関数というものなのです。

以下のサンプルコードに前回のものと全く同等なプログラムを、メンバ関数を用いて表現します。

Page Top

今回のミソ anchor.png

  • 構造体のメンバの変数をメンバ変数というのに対してメンバの関数のことをメンバ関数という
  • メンバ関数のスコープには、同じ構造体(やクラス・共有体)のメンバも含まれる。
  • メンバ関数を定義する場合は構造体(やクラス・共有体)の中に関数を記述する。
  • メンバ関数を使用する場合は
    変数名.メンバ関数名(引数);
    というようにしてアクセスします。
Page Top

サンプルコード anchor.png

filesample1502p.d
Everything is expanded.Everything is shortened.
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
 
 
-
!
-
|
-
!
-
!
-
!
|
-
|
|
|
!
-
!
-
|
|
!
-
!
-
|
|
!
-
!
-
|
|
|
!
-
!
-
|
|
!
-
!
-
|
|
!
|
!
 
-
!
-
|
|
-
|
|
!
|
-
|
!
|
|
|
|
-
!
-
|
|
|
|
|
|
|
|
|
!
|
!
import std.stdio;
 
// 「人」型を定義
struct Parson
{
    
    // メンバ変数の定義
    
    // 名前
    char[] name;
    // 場所
    char[] location;
    
    // メンバ関数の定義
    // メンバ変数の定義は普通の関数の定義を構造体などの中に記述することで行われます。
    // メンバ関数の中では、同じクラス内のメンバの変数や関数がスコープに含まれるので、
    // name や location 、 WakeUp などにアクセスすることができます。
    
    // 起床
    void WakeUp()
    {
        writefln( "%sは現在%sにいます。", name, location );
        writefln( "%sは%sで目を覚ました。\n", name, location );
    }
    // 食事
    void Eat(char[] foodName)
    {
        writefln( "%sは現在%sにいます。", name, location );
        writefln( "%sは%sを食べた。少し元気になった。\n", name, foodName );
    }
    // 移動
    void Move(char[] locationName)
    {
        writefln( "%sは現在%sにいます。", name, location );
        writefln( "%sは%sから%sへ移動した。\n", name, location, locationName );
        location = locationName;
    }
    // 勉強
    void Study()
    {
        writefln( "%sは現在%sにいます。", name, location );
        writefln( "%sは勉強した。少し賢くなった。\n", name );
    }
    // 就寝
    void Sleep()
    {
        writefln( "%sは現在%sにいます。", name, location );
        writefln( "%sは%sで眠りに就いた。\n", name, location );
    }
 
}
 
// メインの流れ
int main(char[][] args)
{
    Parson keiichi;
    with (keiichi)
    {
        name = "前原圭一".dup;
        location = "自室".dup;
    }
    
    // メンバ関数の使い方。
    // 変数と同じように"."(ピリオド)でアクセスすることで使用することができます。
    keiichi.WakeUp();
    keiichi.Move( "食卓".dup );
    keiichi.Eat( "朝食".dup );
    keiichi.Move( "学校".dup );
    
    // メンバ関数にもwith文でアクセスすることができます。
    with (keiichi)
    {
        Study();
        Eat( "お昼の弁当".dup );
        Study();
        Move( "家".dup );
        Move( "食卓".dup );
        Eat( "夕食".dup );
        Move( "自室".dup );
        Study();
        Sleep();
    }
    return 0;
}
+  Tango用はこちら
filesample1502t.d
Everything is expanded.Everything is shortened.
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
 
 
-
!
-
|
-
!
-
!
-
!
|
-
|
|
|
!
-
!
-
|
|
|
|
!
-
!
-
|
|
|
|
!
-
!
-
|
|
|
|
|
!
-
!
-
|
|
|
|
!
-
!
-
|
|
|
|
!
|
!
 
-
!
-
|
|
-
|
|
!
|
-
|
!
|
|
|
|
-
!
-
|
|
|
|
|
|
|
|
|
!
|
!
import tango.io.Stdout;
 
// 「人」型を定義
struct Parson
{
    
    // メンバ変数の定義
    
    // 名前
    char[] name;
    // 場所
    char[] location;
    
    // メンバ関数の定義
    // メンバ変数の定義は普通の関数の定義を構造体などの中に記述することで行われます。
    // メンバ関数の中では、同じクラス内のメンバの変数や関数がスコープに含まれるので、
    // name や location 、 WakeUp などにアクセスすることができます。
    
    // 起床
    void WakeUp()
    {
        Stdout.formatln( "{}は現在{}にいます。",
            name, location );
        Stdout.formatln( "{}は{}で目を覚ました。\n",
            name, location );
    }
    // 食事
    void Eat(char[] foodName)
    {
        Stdout.formatln( "{}は現在{}にいます。",
            name, location );
        Stdout.formatln( "{}は{}を食べた。少し元気になった。\n",
            name, foodName );
    }
    // 移動
    void Move(char[] locationName)
    {
        Stdout.formatln( "{}は現在{}にいます。",
            name, location );
        Stdout.formatln( "{}は{}から{}へ移動した。\n",
            name, location, locationName );
        location = locationName;
    }
    // 勉強
    void Study()
    {
        Stdout.formatln( "{}は現在{}にいます。",
            name, location );
        Stdout.formatln( "{}は勉強した。少し賢くなった。\n",
            name );
    }
    // 就寝
    void Sleep()
    {
        Stdout.formatln( "{}は現在{}にいます。",
            name, location );
        Stdout.formatln( "{}は{}で眠りに就いた。\n",
            name, location );
    }
 
}
 
// メインの流れ
int main(char[][] args)
{
    Parson keiichi;
    with (keiichi)
    {
        name = "前原圭一".dup;
        location = "自室".dup;
    }
    
    // メンバ関数の使い方。
    // 変数と同じように"."(ピリオド)でアクセスすることで使用することができます。
    keiichi.WakeUp();
    keiichi.Move( "食卓".dup );
    keiichi.Eat( "朝食".dup );
    keiichi.Move( "学校".dup );
    
    // メンバ関数にもwith文でアクセスすることができます。
    with (keiichi)
    {
        Study();
        Eat( "お昼の弁当".dup );
        Study();
        Move( "家".dup );
        Move( "食卓".dup );
        Eat( "夕食".dup );
        Move( "自室".dup );
        Study();
        Sleep();
    }
    return 0;
}
Page Top

実行結果 anchor.png

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
$ dmd -run sample1502p.d
前原圭一は現在自室にいます。
前原圭一は自室のベッドで目を覚ました。
 
前原圭一は現在自室のベッドにいます。
前原圭一は自室のベッドから食卓へ移動した。
 
前原圭一は現在食卓にいます。
前原圭一は朝食を食べた。少し元気になった。
 
前原圭一は現在食卓にいます。
前原圭一は食卓から学校へ移動した。
 
前原圭一は現在学校にいます。
前原圭一は勉強した。少し賢くなった。
 
前原圭一は現在学校にいます。
前原圭一はお昼の弁当を食べた。少し元気になった。
 
前原圭一は現在学校にいます。
前原圭一は勉強した。少し賢くなった。
 
前原圭一は現在学校にいます。
前原圭一は学校から家へ移動した。
 
前原圭一は現在家にいます。
前原圭一は家から食卓へ移動した。
 
前原圭一は現在食卓にいます。
前原圭一は夕食を食べた。少し元気になった。
 
前原圭一は現在食卓にいます。
前原圭一は食卓から自室へ移動した。
 
前原圭一は現在自室にいます。
前原圭一は勉強した。少し賢くなった。
 
前原圭一は現在自室にいます。
前原圭一は自室のベッドで眠りに就いた。
Page Top

まとめ anchor.png

とりあえず、今回の利点はコードが読みやすくなる程度の御利益があるメンバ関数についてお話ししました。
しかしながら、本来メンバ関数の実力はこんなものではありません。
メンバ関数の本領は「ポリモーフィズム」とか「カプセル化」いうあたりでお話しします。
あ、あとちなみに、メンバ関数ではない、今までやってきた普通の関数のことを、メンバ関数に対して「フリー関数」と呼ぶ場合があります。 覚えておくといつか得するかもしれません。
とりあえず、その前段階として次章では「クラス(class)」について説明していきます。

Page Top

#summary - まとめ anchor.png

Page Top

第15章のミソ anchor.png

  • オブジェクトの入り口は構造体をポインタで関数に渡すこと
  • 種類や型を示す呼び方をオブジェクト志向では「クラス」という
  • 種類や型に属する個を示す呼び方をオブジェクト志向では「インスタンス」という
  • 構造体のメンバの変数をメンバ変数というのに対してメンバの関数のことをメンバ関数という
  • メンバ関数のスコープには、同じ構造体(やクラス・共有体)のメンバも含まれる。
  • メンバ関数を定義する場合は構造体(やクラス・共有体)の中に関数を記述する。
  • メンバ関数を使用する場合は
    変数名.メンバ関数名(引数);
    というようにしてアクセスします。
Page Top

宿題 anchor.png

今回のメンバ関数を用いて、サンプルコードに追加の関数、「Play」を付け加えましょう。
学校から帰ったら遊ぶことにします。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
前原圭一は現在自室にいます。
前原圭一は自室のベッドで目を覚ました。
 
前原圭一は現在自室のベッドにいます。
前原圭一は自室のベッドから食卓へ移動した。
 
前原圭一は現在食卓にいます。
前原圭一は朝食を食べた。少し元気になった。
 
前原圭一は現在食卓にいます。
前原圭一は食卓から学校へ移動した。
 
前原圭一は現在学校にいます。
前原圭一は勉強した。少し賢くなった。
 
前原圭一は現在学校にいます。
前原圭一はお昼の弁当を食べた。少し元気になった。
 
前原圭一は現在学校にいます。
前原圭一は勉強した。少し賢くなった。
 
前原圭一は現在学校にいます。
前原圭一は学校から家へ移動した。
 
前原圭一は現在家にいます。
前原圭一は家から食卓へ移動した。
 
前原圭一は現在家にいます。
前原圭一は家から自室へ移動した。
 
前原圭一は現在自室にいます。
前原圭一はゲームをして遊んだ。ちょっと愉快になった。
 
前原圭一は現在自室にいます。
前原圭一は自室から食卓へ移動した。
 
前原圭一は現在食卓にいます。
前原圭一は夕食を食べた。少し元気になった。
 
前原圭一は現在食卓にいます。
前原圭一は食卓から自室へ移動した。
 
前原圭一は現在自室にいます。
前原圭一は勉強した。少し賢くなった。
 
前原圭一は現在自室にいます。
前原圭一は自室のベッドで眠りに就いた。
Page Top

コメント anchor.png

次回は、classについて取り扱っていきます。
D言語風にオブジェクト指向をする場合はclassを主に使うことになると思います。

そんなわけで、宿題できたら、あるいは予想ついたら次にいきましょう~


Page Top

投票とコメント anchor.png

選択肢 投票
大変参考になった1  
参考になった0  
あまり参考にならなかった0  
まったく参考にならなかった0  

最新の10件を表示しています。 コメントページを参照

  • sample1501p.d がどうも実行できないのですが・・・ -- ゲスト編集 2012-05-05 (土) 20:04:51
  • 使っているdmdのバージョンを教えて下さい。現在のgit headではコンパイル・実行できるようです。 -- SHOO編集 2012-05-14 (月) 23:28:46
お名前:

トップ   凍結 差分 バックアップ 複製 名前変更 リロード印刷に適した表示   ページ新規作成 全ページ一覧 単語検索 最新ページの一覧   ヘルプ   最新ページのRSS 1.0 最新ページのRSS 2.0 最新ページのRSS Atom Powered by xpWiki
Counter: 647, today: 1, yesterday: 0
初版日時: 2012-05-14 (月) 23:28:46
最終更新: 2012-05-14 (月) 23:28:46 (JST) (1957d) by SHOO
メインメニュー

ログイン

ユーザー名:


パスワード:





パスワード紛失  |新規登録

Menu