C++によるプログラミングに関する世界観A(戯言)です。
Cも参照のこと。
ツール関係はC/C++ツールに移動しました。
システムに依存する部分はLinux(システムコール・API)やWindowsプログラミングも参照のこと。
Visual C++/MFCも参照のこと。
C++は、他のどの言語よりも難解です。FORTRANよりも、Javaよりも、Perlよりも、Pythonよりも、Lispよりも難解であると言っていいでしょう。
しかしながら、もっとも難解かつ習得しづらいからこそ習得すべきです。
C++という言語が使えるということは、プログラマとして決定的な武器になります。
たとえば、Windowsやアプリケーションを使うだけの平均的なエンドユーザーは、プログラムを書くことはできません。これに対して、VBAやPythonのプログラマは、プログラムを書いて高度な処理を行うことができます。
しかしながら、PythonやRubyを使っていると、言語的な「用意されているもの」がとても多く、ほとんどは自分でロジックを書かなくても、モジュールやgemなどを用いて、いくらかスニペットを書くだけで、簡単にできてしまいます。もちろん、これがPythonやRubyの強みであり、言語的な水準が高く、少ない行でロジックが書けることは、プログラマにとっては強力で、また楽をすることができます。
しかしながら、WindowsやLinuxは、Pythonではコア部分を作ることができません。また、Pythonでは、インタープリタの上で動くスクリプトを書くことはできても、インタープリタやコンパイラのような「言語処理系そのもの」を作ることはできません。
C/C++は、そこが違います。WindowsやLinuxは、C/C++をもってすれば自分で開発することが(少なくとも組織の規模や手間の問題ではなく技術的な問題としては)可能です。また、言語処理系を作ることも、C++ならば可能です。
C/C++をもってすれば、カーネル、サーバーデーモン、言語処理系、ウィンドウシステム、ツールキット、Webブラウザ、Officeソフトウェアなど、「システムのすべてを開発できる」ということが言えます。
そう、Windowsを使っているだけのエンドユーザーがPythonでプログラムを書くことに目覚めるのと同じように、Pythonスクリプトを書いている普通のプログラマがC/C++で自分の言語処理系を書くことに目覚めることができるのです。
しかしながら、C/C++は難解な言語です。C言語そのものはシンプルですが、ポインタのように低水準過ぎる点があり、メモリ安全性もありません。C++は、Cに対してこれでもかというぐらい機能をつめこんだ、もっとも難解かつカオスな言語になっており、その習得は困難を極めます。これに拍車をかけているのが、特にGUIプログラムにおけるプラットフォーム依存性です。Windows APIで作るのと、X11で作るのでは、まったく違ったプログラムを書かなければいけないため、「単にC++を覚えただけでは何もできず、さらにたくさんの技術を学ばなければいけない」のです。
しかしながら、皆さんにはC++の習得をおすすめします。ある意味、僕自身が、C++だけがきちんと習得できていない、というのもここに書く理由のひとつです。C++を理解するためには、とても長い修行が必要です。WindowsユーザがPythonプログラマになるのは難しい道のりですが、PythonプログラマがC++プログラマになるのもそれと同じぐらい難しい道のりであると言えるでしょう。
C++言語を習得することは、一流のプログラマにとっては必ず必要です。C++はあらゆる場面で使われる、プログラミング言語の「王道」と言っても良いでしょう。あなたがプログラミングのことを「本当に習得したい」と思っているのであれば、C++を習得しましょう。
ただし、最近は必ずしもC++で書く場面は少なくなってきています。Java, VB, C#のような高水準のオブジェクト指向言語や、Python, PHP, Rubyのようなお手軽なスクリプト言語を使うことも多いです。C++を習得してからそれらに移行すれば、Webやサーバー、それからWindowsのようなプラットフォームにも十分に対応できます。
ですが、C++には挫折者がとても多いです。それは、「ポインタ、クラス、テンプレート、STL、わけのわからない難しいものが多すぎる」からです。難しいと思うなら、PerlやDelphiのような「コンピュータとしての方法や構造設計が分かりやすい言語」を試してみてください。それらを経験してからC++に戻ってくれば、きっと理解や習得が容易になるでしょう。
まさに、C/C++は、プログラミング界の帝王と言えます。それは、LinuxやWindowsの多くのコンポーネントは、C/C++で書かれており、多くのネイティブアプリケーションもC/C++で書かれているからです。
JavaやPythonのような他のプログラミング言語も、言語処理系そのものはC/C++で書かれています。
そう、C/C++で書かれている入手可能なソフトウェアリソースは、おそらく90%以上ではないかと思います。
よって、LinuxとC/C++を学びましょう。Linuxを使う理由は、gccやLLVM/ClangなどのC/C++コンパイラが無料かつオープンソースで手に入るからです。Linuxは多くのアプリケーションがオープンソースですが、この中でも、C/C++が使われているソフトウェアが多勢を占めています。
言ってしまえば、LinuxとC/C++以外、学ぶ必要はありません。
ただし、ひとつ注意しておくべきことは、「ポインタでメモリ管理をきちんとするのは難しい」ということです。このことだけに注意すれば、むしろ、JavaやPythonのような他の言語が「お遊び言語」に過ぎないことが良く分かるでしょう。
後日注記:基本的に、WindowsやLinuxのような「OSやネイティブアプリケーション」を書く場合はC/C++を使い、「大規模なプログラミングやシステム構築」を行う場合はJavaやその他の言語を使えば良いでしょう。
C++に言えることとして、「ポインタとクラスの両立は難解」であるということが言えます。
C++では、ポインタを使いながらクラスベースのオブジェクト指向を行います。これにより、柔軟性が高く、どんな場合のコードにも対応できるという利点がありますが、難解で何をやりたいのかコードを見ただけでは全く分からないという欠点もあります。
Javaとは違い、メモリ管理を自分でしなければなりません。ですが、基本的に「プログラムの中で確保したものは、必ずプログラムの中で廃棄する」ということをきちんとやれば良いのです。これは難しいことではありません。
C++はシステムプログラマ向けの言語で、システムの中にすでに存在するアプリケーションを改造したい場合、ほとんどはC/C++で開発を行います。
Javaの方が使いやすいとは言われますが、Javaは専門の巨大システムを構築するために使うのであって、Webブラウザを改良したいなどの「システムプログラミング」における用途では、C++を使わざるを得ません。
こうした分野では速度と性能が重視されるため、Javaで作ることは現実的ではありません。
また、このような分野で必要なのは「きちんと動くアプリケーション」であり、C++を使って「正しく動くコモディティ的(個性のない汎用製品的)なコード」を書かなければなりません。
C++はSmalltalkを参考にしたクラスベースのオブジェクト指向言語であり、Smalltalkの用語である「レシーバ」「メソッド」「メッセージ」は以下のような意味を持つ。
用語 | 説明 |
---|---|
レシーバ | レシーバは直訳すれば「受け手」であり、メッセージを受け取る側のオブジェクトを表す。 |
メソッド | メソッドは、レシーバに対して働きかけるメッセージとその時の処理のことで、C++では関数として実装する。 |
メッセージ | レシーバのメソッドを実行することを、「レシーバにメッセージを与える」と言う。 たとえば、オブジェクトobjに対してメソッドmeth()を実行することを、「objにmethメッセージを与える」と表現する。 |
C++の「.」や「->」で言えば、.の左側にあるものをレシーバ、右側にあるものをメソッドとする。オブジェクト指向言語では、レシーバに対してメソッドを働きかけることで、あるデータに対する関数の「ふるまい」を記述して、プログラミングを行う。
C++でオブジェクト指向のクラスやメソッドを使うことを「なんとなく理解する」ためには、こうしたSmalltalkの基礎知識が有用である。メソッドを実行する、ということが何を意味しているのか、分かると理解しやすい。
SmalltalkやC++のようなオブジェクト指向言語では、オブジェクトを作成するために「クラス階層図」を利用する。
クラスとは、直訳すれば「階級」。
クラス階層図は、あらゆるデータ型は基本となるデータ型である「基本クラス」から派生したデータ型である「派生クラス」の、ひとつの抽象的な階層構造である「クラス階層」として表現される。全てのクラスは単一の「継承関係」にあり、基本のクラスを元にして派生クラスが作られる。
クラスには、オブジェクトの保持するデータの構造体メンバである「フィールド」あるいは「メンバ変数」と、そのメンバにはたらきかける付随の「メソッド」によって成り立つ。データ型とそれに付属するメソッドは、全体が継承関係にあり、基本のデータ型から派生したデータ型が作られ、クラス階層に記述されているメソッドからしか、データ型にはアクセスできない。
クラスベースのオブジェクト指向言語では、全てのオブジェクトの作成のために、こうした「クラス」を利用する。クラスはデータと関数をひとまとめにしたような構造体であると同時に、カプセル化や継承のような機能を提供する。自分でオリジナルのクラスを作るために、基本となるクラスを自分で継承することもできる。
クラスライブラリには以下のようなものがある。
ライブラリ | 説明 |
---|---|
MFC | MicrosoftによるWindows向けのクラスライブラリ。 標準のC++では提供されないさまざまなクラスや、 Windowsコントロールの高度なAPIを提供する。 また、ドキュメント・ビュー・アーキテクチャにより、 C++でWindowsアプリケーションを実現するための、 ドキュメントとビューを中心とした高度なフレームワークを提供する。 言語はC++。 |
Java | Javaのパッケージは、Java SE, Java EE, Java EEにおいて それぞれのクラスライブラリを提供する。 言語はJava。 |
.NET Framework | .NET FrameworkはMicrosoft版のJavaのようなもので、 Windowsアプリケーションの開発のためのクラスライブラリを提供する。 言語はC#/VB.NETなど。 |
OPENSTEP | OPENSTEPはNeXTSTEP向けのオブジェクト指向開発環境。 macOSでCocoaとして採用されているほか、 フリー版実装のオープンソースなGNUstepなどがある。 |
GLib/GObject/GTK+ GNOME | GNOMEで採用されている、C言語によるオブジェクト指向を行うための機構。 GTK+のGUIウィジェットを使用できる。 言語はC/C++/Python/JavaScript/Valaなど多数のバインディングが開発されている。 オープンソース。 |
Qt KDE Framework | KDEのC++のフレームワーク。 QtやKDEと美しく統合されたアプリケーションを開発できる。 言語はC++のほかPythonなど。 オープンソース。 |
C++におけるオブジェクト指向は、オブジェクトに対する任意の演算子を定義する感覚に近いです。
たとえば、数値なら、1+1=2の「+」のようにしたいところを、さまざまなオブジェクトに対して、「.add()」や「.remove()」のような形で定義するのです。
Smalltalkでは、こうしたメッセージ式を演算子と全く同じように定義して利用することができます。
Effective C++ 第3版の受け売りですが、C++には4つの領域があります。
まず一つ目は、C言語の領域。C++では、C言語のスーパーセットとして、「C言語で書くことのできるすべての言語仕様が引き継がれている」という特徴があります。
二つ目は、オブジェクト指向の領域。C++では、データ構造とメソッドを統合する「クラス」をベースとしたオブジェクト指向が可能となっています。また、名前空間により、グローバル変数などの識別子の衝突を回避できます。これらの特徴により、グローバル変数や構造体のポインタ、staticなどを使わなくても、大規模な開発が可能です。
三つ目は、ジェネリックの領域。C++ではテンプレートを使うことで、異なる型を汎用的に記述できます。
最後に、四つ目はSTLです。テンプレートを用いたライブラリであるSTLを使うことで、C言語にはないリストやベクターなどのデータ構造や、それに対する処理の機能を提供しています。
Effective C++で言われている通り、C++はこれらの「異なる言語の集合体」であると理解することができます。
また、僕が個人的に思うことを言えば、これらに対して「プラットフォーム別のクラスライブラリ」が追加で言えると思います。たとえば、特にGUIなら、WindowsならばWindows API(Win32 API)やMFC、UNIXならばQtやGTKです。
以下のEffective C++は、C++の基本が分かった新米エンジニアが、「どのようなコードを書くべきか」ということを学ぶために有用です。
この本は、「C++のコードのデザイン原則」のような本で、可能な限りconstを使えとか、インターフェースは正しく使う時には使いやすく、間違った使い方では使いづらいようにしろとか、オブジェクトをコピーする時は全体をコピーしろとか、そうした「C++における細かな原則」が具体的で大まかに書かれた本です。
この本に目を通すことで、C++の基本的な「守るべき原則」が分かります。ですが、実際のC++のプログラミングができなければ分からないような内容も多く、初心者にはおすすめできません。初心者はもっと別のC++の入門本を買いましょう。
C/C++はさまざまな分野に用いられることが多く、柔軟な記述が可能であることを求められるため、実際のC++の現場についてどのようなコードを書いたらいいか検討がつかないような時に、この本にざっと目を通すと、「普通はこのように書くべきなのだ」ということが分かるでしょう。
Effective C++は時にとてもアルゴリズム的でコンピュータの内部に迫るようなことを言うために、数学のできない文系プログラマにとっては扱いづらい本かもしれませんが、きちんと同じことを考えると、「そうか、本当にこの方が効率が悪いのだな、そしてそれは私のせいなのだな」ということが分かって、とてもためになります。
後日注記:Effective C++はC++のカプセル化や効率などの面から見た「正しい選択」の分かる本だと思います。特に、コンストラクタが無駄に実行されるのを少なくするとか、カプセル化の面から見てクラスのメソッドでもfriendでもない関数を使うべきなのはなぜなのかなど、さまざまな「よく考えられた方法」を一気に習得できる本です。そのような観点から見ると、C++を使う上で参考にすべき設計の方法が見えてくると思います。
Linuxカーネルの開発者であるリーナス・トーバルズは、C++が大嫌いであることで知られます。
リーナスは、LinuxカーネルをC++で書くことをしません。LinuxカーネルはあくまでC言語(のGCC拡張)を使って記述します。
この理由は、カーネルの開発にC++が適していないだけではなく、リーナスがC++の言語仕様が大嫌いだからです。
僕はこのことを知って、C++を学ぶことをやめました。僕もリーナスと同じく、C++の言語仕様が好きではありません。C++の言語仕様は、特にC言語から引き継いだポインタの考え方や記法を、オブジェクト指向の動的なオブジェクトの作成のために転用している点など、多くがエレガントではないと思います。テンプレートやSTLの考え方も、僕は好きになれません。特にイテレータの記法などはまったく醜く、入出力ストリームの記法も醜いと思います。
なので、僕はこれ以上C++を学びません。僕はこれからはPythonやRubyやJavaScriptを学びたいと思います。
C++という言語では、新しい機能が追加される際に、「ゼロ・オーバーヘッドの原則」が守られる。
C++には、その機能を使わなかった場合に、プログラムのパフォーマンスを害することのないような機能しか追加されない。
なので、C++にたくさんの機能があるからといって、使いもしないのにその機能がプログラムのパフォーマンスを悪化させることはない。言語をCからC++にしたからといって、それだけでプログラムの速度悪化を気にせず、安心して使いたい機能だけを使うことができる。
ゼロ・オーバーヘッドの原則があるために、C++には不要になったメモリ領域を自動的に解放するガーベッジコレクション(GC)のような機能は実装されない。だが、最近のC++ではGCと同様の機能はスマートポインタが代わりに用意されている。
2023.05.10
C/C++では、ポインタの指し示す値を参照する*演算子や、ポインタの先にある構造体やオブジェクトを参照する->演算子をよく使います。
これを感覚的に理解するには、「メモリアドレスの先を指し示す」と考えることです。
そのメモリ上にあるデータの場所、インスタンスの存在する場所、その場所の先にあるデータを演算子やメソッドから参照するということを常に意識しましょう。
C/C++は、良くも悪くもメモリアドレスの言語です。malloc()やfree()のような関数、newやdeleteのような演算子を見ても、「C/C++はメモリアドレスの言語」だということがよく分かります。
メモリアドレスを直接操作できるため、C/C++はカーネルやコンパイラやシステムソフトウェアのような低水準レイヤーを書くのに向いています。
また、ゲームや画像ソフトのような、グラフィックス処理をする場合、C/C++で書くと高速に動くことが多いです。そのため、スピードと効率が必要なWebブラウザや2D/3DゲームにもC/C++がよく使われます。
2023.05.11
C/C++の開発をする上で、ひとりだけで巨大ソフトウェアを開発することは難しいです。
C/C++は、確かにカーネルやコンパイラ、あるいはシステムソフトウェアやWebブラウザなどを書くのに適した言語です。
ですが、このような巨大ソフトウェアは、高度かつ複雑であり、サイズも大きく、ひとりだけでゼロから作り出すのは困難を極めます。
なので、自分ひとりだけでC/C++の開発を行うのは、現実的ではありません。
会社で行うなら、会社の技術を学んで作ればいいことになりますが、何も技術力や経験がないのに会社に入ったところで、いきなり即戦力で開発をすることは難しいです。
幸いにも、わたしたちにはオープンソースソフトウェアがあります。カーネルを書きたいならLinuxや*BSD、コンパイラやインタープリタを書きたいならGCCやLLVMあるいはPerlのような言語処理系、Webブラウザを書きたいならMozilla Firefoxなど、オープンに開発されている「既存のソフトウェア」をベースに、自分の作りたい部分や機能だけを追加・変更することができます。
最初から、C/C++で高度なソフトウェアを全部作ることはできません。挫折どころか、スタートラインにすら立てずにやめていく人が多いです。なので、最初はオープンソフトウェアの改良から始めることをおすすめします。
C/C++ではありませんが、僕もオープンソースの2ちゃんねる専用ブラウザのOpenJaneをDelphiでビルドすることに成功し、改造して派生版を作ることでプログラミングの道を開きました。同じように、まず最初は既に存在するオープンソースソフトウェアのビルドができるようになることを目指しましょう。ビルドすらできてしまえば、コードを自由に書くことで自在にプログラムの開発ができます。まずは、オープンソースソフトウェアのビルドを行うことを最初の仕事にしてはいかがでしょうか。
2023.05.11