2010年7月23日金曜日

C++がマルチパラダイムだということについて素人なりに考えてみた

Check
C++ってオブジェクト指向だけじゃなくて、いろんな書き方ができるよねってお話。それゆえに僕みたいな素人はいろんな書き方がごっちゃになってしまうというお話。

C++でプログラミングを始めて2年くらい。最初はC++について「オブジェクト指向を取り入れたC言語」という認識を持っていたんだけど、最近どうもそれは違うらしいと思うようになってきた。

僕みたいな初心者は、入門書を読むと「C++はオブジェクト指向を取り入れていて素晴らしい! 」とか書いてあるのを見て、「C++ = オブジェクト指向ヤッホオオオ!!!! 」とか思っちゃう。

んで、Baseクラスから作り始めてDrawableがあって、これを元にPlayerとEnemyが分かれて…いやまてTaskの方が先か? といったオブジェクト指向の迷宮に突入しちゃう。あと、Getter/Setterなんかに絶対負けたり(作ったり)しない! -> Getterには勝てなかったよ…とか。

クラスを説明するのに簡単なサンプルコードとかがあって、
#include <iostream>

class Hoge
{
public:
    int a, b, c;
};

int main(int argc, char *argv[])
{
    Hoge hoge;
    std::cout << hoge.a << hoge.b << hoge.c << std::endl;
    
    return 0;
}
とか書かれていると、初心者としてはこれが「オブジェクト指向か! 」とか勘違いしちゃうわけですよ。

まあ紙面の都合とかもあるだろうし、C++の基本とかオブジェクト指向うんぬんについて語れるほど僕はデキるプログラマでもないのでこの話はここまで。何が言いたいかというと、C++っていろんな考え方が一緒になっていて、それらをごちゃ混ぜにして書くことが許されている、ってこと。すべてプログラマの責任において

Wikipediaによると、C++はマルチパラダイム言語らしい。つまり、複数のプログラミングパラダイムを持つ、ということだ。

僕がとらえている限りでもC++は、
といったそれぞれ異なるパラダイムが共存(混在?)しているようだ。

そして、僕の経験からいって初心者ほどこれらの概念を区別せずにまとめて理解しようとする。「C++? 知ってますよオブジェクト指向言語っすよねー(キリッ」とか言っちゃうわけだ(間違ってはないけど、それで全てじゃないよね? というか個人的に黒歴史…うあああ)。

そこで、上記3つの考え方について違いが分かるように、クラスを2つ作ってその内容を表示するコードを書いてみた。

まずはBetter-CとしてのC++だ。
#include <iostream>

class Hoge
{
public:
    Hoge(int _a, int _b, int _c):a(_a),b(_b),c(_c){}
    int a, b, c;
};

void Print(const Hoge &hoge)
{
    std::cout << hoge.a << hoge.b << hoge.c << std::endl;    
}

class Fuga
{
public:
    Fuga(float _x, float _y, float _z:x(_x),y(_y),z(_z){}
    float x, y, z;
};

void Print(const Fuga &fuga)
{
    std::cout << fuga.x << fuga.y << fuga.z << std::endl;    
}

int main(int argc, char *argv[])
{
    Hoge hoge(1,2,3);
    Print(hoge);
    
    Fuga fuga(0.5f, 1.5f, 2.5f);
    Print(fuga);

    return 0;
}
コンストラクタというはっきりした初期化方法があったり、参照が使えるところなどはC++がC言語より優れている点だと思う。あとPrint関数の多重定義(オーバーロード)にも注目。他にもここでは書いてないけど、変数を後方で宣言できたり、for文の中だけで制御変数を表示できるのもいいと思う。一方、出力の仕方は従来のままだ(std::cout使ってるけど)。

次にオブジェクト指向っぽい書き方。
#include <iostream>

class Base
{
public:
    virtual ~Base(){}
    virtual void Print()const
    {
        std::cout << "Base Class." << std::endl;
    }
};

class Hoge: public Base
{
public:
    Hoge(int _a, int _b, int _c):a(_a),b(_b),c(_c){}
    virtual void Print()const
    {
        std::cout << a << " " << b << " " << c << std::endl;
    }
    
private:
    int a, b, c;
};

class Fuga: public Base
{
public:
    Fuga(float _x, float _y, float _z):x(_x),y(_y),z(_z){}
    virtual void Print()const
    {
        std::cout << x << " " << y << " " << z << std::endl;
    }
    
private:
    float x, y, z;
};

int main(int argc, char *argv[])
{
    Base *hoge = new Hoge(1,2,3);
    hoge->Print();

    Base *fuga = new Fuga(0.5f, 1.5f, 2.5f);
    fuga->Print();

    delete hoge;
    delete fuga;

    return 0;
}
このわざとらしいポリモフィズム! よく見るC++によるオブジェクト指向プログラミングの例ですな。僕はこれがC++の全てだと勘違いしちゃった。念のためですが、別にオブジェクト指向がダメだとか言ってるわけではないですヨ。

さて、Collada読み込みライブラリ含め、僕が見たことのあるC++で記述されたライブラリのクラスはすべて基底となるクラス(BaseとかObjectとか)を継承して作られている。Java,Ruby,Pythonなどのオブジェクト指向で有名な言語はデフォルトでこの基底クラスを持つけど、C++は明示的に書かなきゃいけない(書くことができる、のほうが正しいか)。

最後はテンプレートを使った例。
#include <iostream>

class Hoge
{
public:
    Hoge(int _a, int _b, int _c):a(_a),b(_b),c(_c){}
    void Print()const
    {
        std::cout << a << " " << b << " " << c << std::endl;
    }
    
private:
    int a, b, c;
};

class Fuga
{
public:
    Fuga(float xx, float yy, float zz):x(xx),y(yy),z(zz){}
    int Print()const
    {
        std::cout << x << " " << y << " " << z << std::endl;
        return 0;
    }
private:
    float x, y, z;
};

template <class T>
void Print(const T &a)
{
    a.Print();
}

int main(int argc, char *argv[])
{
    Hoge hoge(1,2,3);
    Print(hoge);

    Fuga fuga(0.5f, 1.5f, 2.5f);
    Print(fuga);
    
    return 0;
}
あざといまでにテンプレート(とそれによる多態)を意識した書き方。Hoge::Print()とFuga::Print()の戻り値を見てほしい。voidとintと違っているがそんなのは問題じゃない。あるクラスがPrint()という関数を持っていればそれでいいのだ。

長々と書いたけど、いずれも出力結果は同じだ。
washi@pc:~$ ./test 
1 2 3
0.5 1.5 2.5
同じ出力結果を得るのに、コードの書き方は全然違うふしぎ! この辺がプログラムのおもしろいところであり、同時に難しい所だなぁと思う。実際にはこれらの考え方が合わさったようなコードが使われているんだろうけどね。C++によるライブラリの実装をもっと読まなきゃなー。

C++には上記に書いたこと以外にもたくさんの考え方や機能あると思うんだけど、僕が噛み砕いてお話できるはココまで。んで、何が言いたいかというと、C++がいろんな考え方を合わせて作られた言語であるってことを意識すれば混乱も少なくなると思う。そして、どの考え方(ぱらだいむ)が優れているかではなくて、それぞれのパラダイムの特徴を知った上でコードを書いていければいいなと思っている。

「C/C++は鋭いナイフ、取扱いには注意しましょう」という言葉を以前参加したセミナーで聞いたがその通りだと思う。自由と責任について教えてくれる素敵な言語、それが僕にとってのC++だ。

突然思い立って長々と書きましたが、分かりにくい点などありましたらコメントにてツッコミいれてくださいね。

1 件のコメント:

  1. Titanium Tail Profile | TITanium Art
    TITanium Art: A New Tone, Tagged titanium machining | Titsanium Art is designed to make the how to get titanium white octane TITanium Tail Profile. titanium ring Titsanium. Titsanium. TITanium. titanium cartilage earrings Titsanium. titanium earrings sensitive ears Titsanium. TITSUN.

    返信削除