C++はCとは完全に異なる言語であると多くの人々が言っていますが、Bjarne自身はC++はCから拡張された言語であり、したがって++
から来た。では、なぜCとC++は完全に異なる言語であると誰もが言い続けるのでしょうか。 CはC++の拡張機能以外の点でC++とどのように異なりますか?
1980年代、C++開発が始まったばかりの頃、C++はCのほぼ適切なスーパーセットでした。それがすべての始まりでした。
しかし、言語間の互換性が常に重要であると常に考えられてきたとしても、時間の経過とともに、CとC++の両方が進化し、互いに分岐しました。
さらに、CとC++の技術的な違いにより、これらの言語の典型的なイディオムが作られ、「良い習慣」と見なされるものはさらに多様化しています。
これが、「C/C++のような言語は存在しない」または「CとC++は2つの異なる言語である」などの人々の背後にある原動力です。 CコンパイラとC++コンパイラの両方に受け入れられるプログラムを作成することは可能ですが、コードは一般に、優れたCコードの例でも、優れたC++コードの例でもないと見なされます。
Stroustrup自身が 彼のFAQ でそれに答えます:
C++はCの直接の子孫であり、Cのほぼすべてをサブセットとして保持します。 C++は、Cよりも強力な型チェックを提供し、Cよりも幅広いプログラミングスタイルを直接サポートします。C++は、より優れた型チェックと表記サポートを備えたCを使用して行われたプログラミングスタイルをサポートするという意味で「優れたC」です(損失なし)。効率の)。同じ意味で、ANSI CはK&R Cより優れたCです。さらに、C++はデータ抽象化、オブジェクト指向プログラミング、および汎用プログラミングをサポートしています。
これは、C++をCと「完全に異なる」ようにするオブジェクト指向プログラミングと汎用プログラミングのサポートです。あなたはcanほぼ純粋なCを記述し、それをC++コンパイラ(より厳密な型チェックに注意を払っている限り)。しかし、あなたはまだCを書いています-あなたはC++を書いていません。
C++を作成している場合は、オブジェクト指向とテンプレートの機能を利用しているため、Cで表示されるものとはまったく異なります。
簡単に言えば、Cで慣用と見なされているものは、C++では間違いなく慣用ではありません。
CとC++は、人々がそれらを使用する方法のために、実際には非常に異なる言語です。 Cはミニマリズムを目指しており、C++はlotの機能を備えた非常に複雑な言語です。
Cはほとんどすべての言語から簡単に呼び出すことができ、多くの場合、プラットフォームのABIを定義しますが、C++は他のライブラリから使用するのが非常に困難です。ほとんどの言語には、C++(Javaなど)で実装された言語でさえ、CにFFIまたはインターフェースがあります。
C++がオブジェクト指向プログラミングをサポートしているという明白な事実は別として、私はあなたがここであなたの答えを持っていると思います: http://en.wikipedia.org/wiki/Compatibility_of_C_and_C++
その記事には、Cでは問題なくC++では問題がないことを示すコード例が含まれています。例えば:
int *j = malloc(sizeof(int) * 5); /* Implicit conversion from void* to int* */
CプログラムをC++に移植することは簡単であることが多く、主にコンパイルエラーの修正(キャストの追加、新しいキーワードなど)で構成されています。
C++は、Cに新しい機能だけでなく、新しい概念と新しいイディオムを追加します。C++とCは密接に関連していますが、言語で効果的に書くためには、その言語のスタイルで考える必要があるという事実は変わりません。最高のCコードでさえ、C++のさまざまな長所やイディオムを利用できないため、実際にはbad C++コードではない可能性が高くなります。
「拡張機能」は、C++で追加したように聞こえるようにします。可変個のマクロなどです。 C++の「拡張機能」は言語の完全なオーバーホールであり、Cのベストプラクティスに完全に取って代わります。これは、新しいC++機能が元のC機能よりもはるかに優れているため、元のC機能が完全かつ大多数のケースでは完全に冗長です。 C++が単にCを拡張することを示唆することは、現代の戦車が戦争を行う目的でバターナイフを拡張することを示唆しています。
違いは、Cでは手続き的に考えることと、C++ではオブジェクト指向の方法で考えることです。言語は非常に似ていますが、アプローチは大きく異なります。
C++は、構文的にはCのスーパーセットにすることができます。つまり、Cプログラムの任意の構造をC++コンパイラでコンパイルできます。
ただし、C++プログラムをCプログラムで作成する方法で作成することはほとんどありません。リストは無限である場合もあれば、徹底的なレポートとしてそれを置くためにさらに調査を行う必要がある場合もあります。しかし、私は重要な違いをもたらすいくつかのポインターを置いています。
現在の投稿の要点は、Cに相当するものをコンパイルすることは可能ですが、優れたC++プログラマーがプログラミングのベストプラクティスとして使用する必要がある次の機能がC++にあるということです。
C++ではなくC++で行う方法
クラスと継承。それは、プログラミング式を非常に強力にする体系的なオブジェクト指向を可能にする最も重要な違いです。たぶん、この点についてはこれ以上説明する必要はありません。 C++を使用している場合-ほとんどの場合、クラスを使用する方が優れています。
民営化-クラス、そして構造でさえプライベートメンバーとは何かを持っています。これにより、クラスのカプセル化が可能になります。 Cでこれに相当するのは、オブジェクトをvoid *としてアプリケーションに型キャストして、アプリケーションが内部変数にアクセスできないようにすることです。ただし、C++では、プライベートクラスだけでなくパブリッククラスも持つことができます。
参照渡し。 C++では、ポインターの受け渡しが必要な参照に基づいた変更が可能です。参照渡しは、コードを非常にクリーンに保ち、ポインタの危険に対してより安全です。あなたもCスタイルポインターを渡し、それは機能しますが、C++を使用している場合は、
新規および削除対mallocおよびfree。 new()およびdelete()ステートメントは、メモリの割り当てと割り当て解除だけでなく、チェーン内で呼び出されるデストラクタの一部としてコードを実行することもできます。 C++を使用している場合-mallocとfreeを使用するのは実際には悪いことです。
IOの種類と演算子のオーバーロード演算子のオーバーロードにより、コードを読みやすくすることができます。 <<および>> io演算子についても同様です。これを行うCの方法は、関数ポインターを使用することです-しかし、これは面倒で、上級プログラマーのみが使用します。
「文字列」を使用します。 Cのchar *はどこでも機能します。したがって、CとC++はほとんど同じです。ただし、C++を使用している場合は、Stringクラスを使用する方が常にはるかに優れており(安全です)、ほとんどすべてのものである、実行中の配列の危険性を回避できます。
C++ではまだファンではない機能 1.テンプレート-多くのコードで重いテンプレートを使用していませんが、ライブラリにとって非常に強力であることがわかります。 Cにはこれに相当するものはほとんどありません。しかし、通常の日に-特に数学的に欠落している場合は。
私がCについて好きでC++で見逃していること
関数ポインタを使用した多態性アルゴリズム。 Cでは、複雑なアルゴリズムを実行している場合-関数ポインターのセットを使用できる場合があります。これにより、強力な方法でpolymorphismが実現します。 C++を使用している場合[〜#〜] [〜#〜]は関数ポインタを使用できますが、それは悪いことです。メソッドのみを使用する必要があります-それ以外の場合は、乱雑になる準備をしてください。 C++クラスでのポリモーフィズムの唯一の形式は、関数と演算子のオーバーロードですが、それは非常に制限があります。
単純なスレッド。スレッドを作成したときはpthreadでした-非常にシンプルで扱いやすいです。それは、クラスに対して「プライベート」であるはずのスレッドを作成する必要があるときになります(そのため、それらはプライベートメンバーにアクセスできます)。 boostタイプのフレームワークがありますが、基本的なC++にはありません。
ディパン。
特定の言語機能は、それが追加機能であっても、実際に使用する必要がある言語全体を変更する可能性があります。一例として、このケースを考えてみましょう:
lock_mutex(&mutex);
// call some functions
...
unlock_mutex(&mutex);
上記のコードにC++で実装された関数の呼び出しが含まれている場合、これらの関数呼び出しのいずれかがスローされ、これらの例外パスのミューテックスがロック解除されることはないため、問題が発生する可能性があります。
デストラクタは、プログラマがその時点でリソースを解放/解放するのを忘れないようにするための便利な領域にはありません。重要な例でスローされる可能性のあるすべてのコード行を予測することは人間にとって現実的ではないため、RAIIは実用的な要件になります(これらの行は現在スローされず、後で変更が加えられる可能性があることは言うまでもありません)。別の例を見てみましょう:
void f(const Foo* f1)
{
Foo f2;
memcpy(&f2, f1, sizeof f2);
...
}
そのようなコードは、Cでは一般的に無害ですが、memcpy
がこれらのオブジェクトのビットとバイトをブルドーズし、コピーコンストラクターなどをバイパスするため、C++で大混乱をもたらす地獄のようなものです。 memset
、realloc
、memcpy
などの関数は、メモリ内のビットとバイトのかなり均一な方法で物事を見るのに使用されていたC開発者の間の日常のツールですが、より複雑で豊富なC++の型システムと調和しない。 C++は、ユーザー定義型のより抽象的なビューを奨励します。
したがって、これらのタイプのものは、C++を正しく使用しようとする人がCの単なる「スーパーセット」と見なすことを許可しなくなりました。これらの言語は、非常に異なる考え方、規律、および考え方を最も効果的に使用する必要があります。 。
私はC++をあらゆる点で完全に優れていると見なしている陣営にはいません。実際、私のお気に入りのサードパーティライブラリのほとんどは、何らかの理由でCライブラリです。正確な理由はわかりませんが、Cライブラリは本質的にミニマリストになる傾向があります(おそらく、このような豊富な型システムがないため、開発者は、大規模で階層化された抽象セットを構築することなく、必要な最小限の機能を提供することに集中するためです)ただし、目的に合わせてC++ラッパーを配置して、使用方法を簡略化および調整することもよくありますが、その場合でも、そのシンプルな性質の方が私にとっては望ましい方法です。私は、そのような資質を追求するために余分な時間を費やす人々にとって、ライブラリの魅力的な機能としてミニマリズムを本当に愛しています。おそらく、そのような大きくて階層化された抽象的なコードは、 Cで開発する実際のPITA.
私はC++をはるかに好んで使用していますが、実際には、最も広いバイナリ互換性(およびFFI)のためにC APIを使用する必要がありますが、ヘッダーにCを使用しているにもかかわらず、C++で実装することがよくあります。しかし、メモリアロケータのレベルや非常に低レベルのデータ構造など、非常に低レベルの場合(および埋め込みプログラミングを行う人の中に他の例があると確信している場合)は、作業している型とデータにvtable、costructor、destructorsなどの特定の機能がないと想定できるため、それらをビットおよびバイトとして扱い、シャッフル、コピー、解放、再割り当てを行うことができます。非常に特に低レベルの問題の場合、Cが提供するはるかに単純な型システムで作業すると役立つことがあります。もちろん、より速く構築される傾向があります。
明確化
ここで興味深いコメントをもう1つ詳しく説明したいと思います(ここでのコメントは文字数制限が非常に厳しいことに気づきます)。
memcpy(&f2, f1, sizeof f2);
は、Fooが所有ポインタを持っている場合はCの「hellfire reigning havoc」でもあり、それを処理するためのツールも不足しているため、さらに悪い状況です。
それは公平な点ですが、私が注目しているすべてのものは、主にC++の型システムとRAIIに焦点を合わせています。このようなx-rayishバイトコピーmemcpy
またはqsort
タイプの関数がCで実用上の危険性を少なくする理由の1つは、上記のf1
およびf2
の破壊が明示的であることです(一方、デストラクタが全体像に入ると、デストラクタは暗黙的かつ自動化されます(多くの場合、開発者には大きな価値があります)。それは、そのような関数がすぐに強引になるvptrsなどの隠された状態についてさえ言及しないことです。 f1
がポインターを所有し、f2
がそれらを一時的なコンテキストで浅くコピーする場合、所有しているポインターを2回目に明示的に解放しなくても問題はありません。 C++では、コンパイラーが自動的に実行したいものです。
そして、それは通常、より大きなifのようになります。 "If Fooには所有ポインタがあります」。リソース管理に必要な明示性により、通常は見過ごすことが難しくなることが多いため、C++では、UDTを、単純なメンバー変数を格納するだけで簡単に構築/破壊できなくすることができます。 t単純に構築可能/破壊可能(一般的に非常に役立つ方法ですが、memcpy
やrealloc
などの関数を使用したくなった場合はそうではありません)。
私の主なポイントは、この明示性の利点を主張しようとすることではありません(存在する場合、ほとんどの場合、それに伴うヒューマンエラーの可能性の増加の短所によって圧迫されます)。 memcpy
とmemmove
とqsort
とmemset
とrealloc
などの関数は、UDTが豊富な言語では使用できません。 C++としての機能。これらは関係なく存在しますが、C++開発者の大多数は、ペストのような関数を回避するのが賢明だと言っても過言ではないと思いますが、これらはCの非常に日常的な種類の関数です。 dは、その型システムがはるかに基本的で、おそらく「くだらない」ものであるという単純な理由により、Cでは問題が少ないと主張しています。 CタイプをX線撮影し、それらをビットおよびバイトとして処理することはエラーが発生しやすくなります。そのような関数は言語の非常に基本的な機能と、それが型システムに推奨するものと戦っているので、C++でそれを行うことは間違いなく間違いなく間違いです。
これは実際、私にとってCの最大の魅力です。具体的には、それが言語の相互運用性とどのように関連しているかについてです。 C#のFFIのようなものに、C++の本格的な型システムと言語機能を、コンストラクター、デストラクター、例外、仮想関数、関数/メソッドのオーバーロード、演算子のオーバーロード、さまざまな種類のすべてにまで理解させることは、はるかに困難です。継承など。Cでは、多くの異なる言語がFFIを介して直接インポートしたり、一部のC APIを介して間接的に目的の形式で関数をエクスポートしたりできるように、APIとしてはかなり標準的な言語になっています(例:Javaネイティブインターフェース)。C言語を使用せざるを得ない状況がほとんどです。私たちの場合、その言語の相互運用性が実際的な要件であるためです(多くの場合、C++でCインターフェースを記述しているだけです)それらの背後にある実装)。
しかし、あなたが知っている、私は実用主義者です(または少なくとも私はそうなるように努力しています)。 Cがこれで最も反則的でありがちな、エラーが発生しやすい、厄介な言語だった場合、私のC++愛好家の一部はそれをC++愛好家だと主張しました(どういうわけかそれは私の側でのCの憎しみにつながらないことを除けば、私はC++愛好家だと思います) ;それとは逆に、私には両方の言語をそれぞれの点と違いでより良く理解させるという反対の効果がありました)そして、私は現実の世界で最もバグの多い、最も漏れやすいものの形で現れることを期待します信頼性の低い製品とライブラリがCで記述されています。それはわかりません。私はLinuxが好きで、Apache、Lua、zlibが好きです。OpenGLは、Gimp、libpng、Cairoなど、そのような変化するハードウェア要件に対する長いレガシーに耐えることができます。少なくとも、言語がもたらすハードルが障害になることはないようです。いくつかのクールなライブラリと製品を有能な手で書いていて、それが本当に私が興味を持っているすべてです。したがって、私は、最も情熱的な言語戦争にそれほど興味を持ったタイプではありませんでした。彼らがそれを作った方法を学びましょう、そして多分私達が使っているどんな言語にも戻すことができるように、言語の慣用的な性質に特定されないクールなレッスンがあるでしょう。」 :-D