実際のアプリケーションに関して、C、C#、および C++ の違いは何ですか?

私は ASFAC++B を話すことに注意してください。 :) 最も重要な差別化要因を最初に置きました。

ガベージ コレクション

ガベージ コレクション (GC) は、これらの言語を区別する上で最も重要な要素です。

C と C++ は GC で使用できますが、後付けで後付けされたものであり、同様に機能させることはできません (最もよく知られているのはここにあります)。「保守的」である必要があります。つまり、未使用のメモリをすべて収集することはできません。

C# は、GC プラットフォームで動作するようにゼロから設計されており、標準ライブラリもそのように設計されています。これは、開発者の生産性に根本的な違いをもたらします。これは、実際に体験してみる必要があります。

C/C++ ユーザーの間では、GC は「悪いパフォーマンス」と同等であるという考えが広まっています。しかし、これは時代遅れの伝承です (C/C++ の Boehm コレクターでさえ、ほとんどの人が期待するよりもはるかに優れたパフォーマンスを発揮します)。典型的な恐怖は、GC が何らかの作業を行えるようにプログラムが停止する「長い一時停止」です。しかし実際には、これらの長い一時停止は非 GC プログラムで発生します。なぜなら、これらのプログラムは仮想メモリ システム上で実行され、物理メモリとディスクの間でデータを移動するために時折中断するからです。

また、GC を shared_ptr に置き換えることができると広く信じられていますが、それはできません。皮肉なことに、マルチスレッド プログラムでは shared_ptr GC ベースのシステムより遅いです。

質素すぎて GC が実用的でない環境もありますが、そのような環境はますますまれになっています。通常、携帯電話には GC が搭載されています。 C# が通常実行される CLR の GC は、最先端のようです。

約 18 か月前に C# を採用して以来、プロファイラーを使用した純粋なパフォーマンス チューニングのいくつかのフェーズを経てきました。GC は非常に効率的であるため、プログラムの操作中にはほとんど見えません。

GC は万能薬ではありません。すべてのプログラミングの問題を解決するわけではありません。実際にメモリ割り当てをクリーンアップするだけです。非常に大きなメモリ ブロックを割り当てている場合でも、注意が必要です。十分に複雑なプログラムではメモリ リークに相当しますが、GC が生産性に与える影響は万能薬にかなり近いものです!

未定義の動作

C++ は、未定義の動作の概念に基づいています。つまり、言語仕様は、言語機能の特定の狭義に定義された使用法の結果を定義し、他のすべての使用法を未定義の動作の原因として説明します。 、原則として、操作がまったく結果をもたらす可能性があることを意味します (実際には、これは明らかに非決定論的なデータの破損を含む診断が難しいバグを意味します)。

C++ に関するほとんどすべてが、未定義の動作に関係しています。ラムダ式のような非常に優れた今後の機能でさえ、スタックを破壊する便利な方法として簡単に使用できます (参照によってローカルをキャプチャし、ラムダ インスタンスがローカルより長く存続できるようにします)。

C# は、考えられるすべての操作で動作が定義されている必要があるという原則に基づいています。起こり得る最悪の事態は、例外がスローされることです。これにより、ソフトウェア構築のエクスペリエンスが完全に変わります。

(ポインタがあるため動作が定義されていない unsafe モードがありますが、一般的な使用には強くお勧めしません - 組み込みアセンブリ言語に似ていると考えてください。)

複雑さ

複雑さの点では、C++ を抜き出す必要があります。特に、間もなく標準化される新しいバージョンを考慮する場合はそうです。 C++ は、GC を前提とせずに、それ自体を効果的にするためにできる限りのことを絶対に行います。言語設計者は、「これらの機能はライブラリ作成者専用であり、通常のユーザー向けではない」と言い訳していますが、どの言語でも本当に効果的であるためには、コードを再利用可能なライブラリとして構築する必要があります。だから逃げられない。

良い面としては、C++ は非常に複雑で、オタクの遊び場のようなものです。すべてがどのように組み合わされるかを学ぶのはとても楽しいことです。しかし、メインストリーム プラットフォームでの生産的な新しい作業 (無駄な年月...) の基礎として、それを真剣にお勧めすることはできません.

C は言語をシンプルに保ちます (「コンパイラーは書きやすい」という意味でシンプルです) が、これによりコーディング手法がより難解になります。

すべての新しい言語機能が複雑さの増加につながるわけではないことに注意してください。一部の言語機能は、コンパイラが拡張する省略表現であるため、「シンタックス シュガー」と呼ばれます。これは、近年の C# の大幅な機能強化を考える良い方法です。言語標準では、一部の機能を手書きに翻訳することで指定しています。 using ステートメントは try に展開されます /finally .

ある時点で、C++ テンプレートを同じように考えることができました。しかし、それらは非常に強力になったため、独自の熱狂的なユーザー コミュニティとイディオムを備えた、言語のまったく別の次元の基礎を形成しています。

図書館

C と C++ の最も奇妙な点は、コンパイル済みライブラリの標準的な交換可能な形式がないことです。他人のコードをあなたのプロジェクトに統合することは常に少し面倒で、どのようにリンクするかについて曖昧な決定を下さなければなりません.

また、標準ライブラリは非常に基本的なものです。C++ には、データ構造の完全なセットと、文字列を表す方法 (std::string) があります。 )、しかしそれはまだ最小限です。ディレクトリ内のファイルのリストを見つける標準的な方法はありますか?驚くべきことに、いいえ! XML を解析または生成するための標準ライブラリ サポートはありますか?いいえ。データベースへのアクセスについてはどうですか?真剣になってください! Web サイトのバックエンドを作成しますか?ばかじゃないの?など

そのため、遠くに狩りに行かなければなりません。 XML の場合は、Xerces を試してください。しかし、それは std::string を使用しますか? 文字列を表すには?もちろん違います!

また、これらすべてのサードパーティ ライブラリには、クラスと関数の命名に関する独自の奇妙な習慣がありますか?きっと!

C# での状況はこれ以上に違いはありません。基本は最初から整っていたので、すべてが美しく相互運用されます (また、基本は CLR によって提供されるため、複数言語のサポートがあります)。

すべてが完璧というわけではありません。ジェネリックは最初から適切に配置されているはずでしたが、そうではありませんでした。これにより、一部の古いライブラリに目に見える傷跡が残ります。しかし、これを外部から修正するのは通常簡単です。また、多くの一般的なライブラリが Java から移植されていますが、これは最初に登場したほど適切ではありません。

クロージャ (ローカル変数をキャプチャする匿名メソッド)

Java と C は、事実上、クロージャーを欠いている最後の主流言語であり、ライブラリーは、クロージャーがない場合よりもはるかにきれいに設計および使用できます (これが、移植された Java ライブラリーが C# ユーザーにとって扱いにくいように見える場合がある理由の 1 つです)。

C++ の面白い点は、その標準ライブラリが言語でクロージャが利用可能であるかのように設計されていることです (コンテナ タイプ、<algorithm><functional> )。それから10年の時を経て、ついに仲間入り!それらは大きな影響を与えます (ただし、前述のように、それらは未熟な動作を漏らします)。

C# と JavaScript は、クロージャーが「慣用的に確立されている」最も広く使用されている言語です。 (これらの言語の主な違いは、JavaScript が動的に型付けされるのに対し、C# は静的に型付けされることです)。

プラットフォームのサポート

これを最後に置いたのは、あなたが思っているほどこれらの言語を区別していないように見えるからです.これらの言語はすべて、複数の OS およびマシン アーキテクチャで実行できます。 C が最も広くサポートされており、次に C++、最後に C# です (ただし、C# は Mono と呼ばれるオープン ソース実装のおかげで、ほとんどの主要なプラットフォームで使用できます)。

Windows とさまざまな Unix フレーバーの間で C++ プログラムを移植した経験は不快でした。 C# で非常に複雑なものを Mono に移植しようとしたことがないので、それについてコメントすることはできません.


C と C++ はどちらも、より低いレベルの抽象化を提供し、複雑さが増すにつれて、他の言語では必ずしも公開されていない、基礎となるマシン機能への幅広いアクセスを提供します。 C と比較して、C++ は完全なオブジェクト指向言語 (開発時間の短縮) の利便性を追加しますが、潜在的にパフォーマンス コストが追加される可能性があります。実際のアプリケーションに関しては、これらの言語が次のドメインに適用されていることがわかります:

C

  • カーネル レベルのソフトウェア
  • ハードウェア デバイス ドライバ
  • 古い安定したコードへのアクセスが必要なアプリケーション

C、C++

  • メモリ管理を微調整する必要があるアプリケーションまたはサーバーの開発 (および一般的なガベージ コレクション ソリューションに任せることができない)
  • 最新のマネージ言語とうまく連動しないライブラリへのアクセスを必要とする開発環境
  • マネージ C++ を使用して .NET フレームワークにアクセスできますが、シームレスな移行ではありません。

C# は、さらに高いレベルの抽象化を追加するマネージ メモリ モデルを提供します。このレベルの抽象化により利便性が向上し、開発時間が短縮されますが、下位レベルの API へのアクセスが複雑になり、特殊なパフォーマンス要件が問題になります。

マネージ メモリ環境で非常に高性能なソフトウェアを実装することは確かに可能ですが、その影響を認識することが不可欠です。

C# の構文は確かに C/C++ よりも要求が少なく (エラーが発生しにくく)、初心者のプログラマにとっては学習曲線が浅くなっています。

C#

  • クライアント アプリケーションの迅速な開発
  • .NET フレームワークを活用したハイ パフォーマンス サーバー開発 (例:StackOverflow)
  • 設計された言語で .NET フレームワークのメリットを必要とするアプリケーション

Johannes Rössel は、use C# Pointers、Unsafe、および Unchecked キーワードが、C# が構築されている抽象化のレイヤーを突破するという正当な主張をしています。プログラミングの種類は、ほとんどの C# 開発シナリオの例外であり、言語の基本的な部分ではないことを強調します (C/C++ の場合のように)。


C は必要最低限​​の、シンプルでクリーンな言語であり、すべてを自分で行うことができます。手を握ったり、自分の足を撃ったりするのを止めません。しかし、それはあなたがやりたいことをするために必要なすべてを持っています.

C++ は、C にクラスが追加されたものであり、その他にもさまざまなものが追加されています。それはあなたの手を握ることはありませんが、アドオン GC、または RAII とスマート ポインターを使用して、自分の手を握ることができます。達成したいことがある場合、テンプレート システムを悪用して比較的簡単な構文を提供する方法がある可能性があります。 (さらに C++0x の場合)。この複雑さにより、誤って自分自身のインスタンスを多数作成し、それらすべてを台無しにしてしまう可能性もあります。

C# は、C++ と Java を改善するための Microsoft の試みです。大量の構文機能がありますが、C++ の複雑さには及びません。完全に管理された環境で実行されるため、メモリ管理は自動的に行われます。必要に応じて「汚い」コードを使用したり、安全でないコードを使用したりできますが、これはデフォルトではなく、自分自身を撃つためにいくつかの作業を行う必要があります.