Cには、オブジェクトと見なすことができる「構造」などの独自の準オブジェクトがあるようです(通常は高レベルの方法で考えられます)。
また、Cファイル自体は基本的に別個の「モジュール」ですよね?モジュールも「オブジェクト」のようなものではありませんか?C ++に非常に似ているCが、C ++が高レベルの「オブジェクト指向」である低レベルの「手続き型」言語と見なされる理由について混乱しています
*編集:(説明)なぜ、どこで、「オブジェクト」が何のために、そして何のために線が引かれているのか?
Cには、オブジェクトと見なすことができる「構造」などの独自の準オブジェクトがあるようです(通常は高レベルの方法で考えられます)。
また、Cファイル自体は基本的に別個の「モジュール」ですよね?モジュールも「オブジェクト」のようなものではありませんか?C ++に非常に似ているCが、C ++が高レベルの「オブジェクト指向」である低レベルの「手続き型」言語と見なされる理由について混乱しています
*編集:(説明)なぜ、どこで、「オブジェクト」が何のために、そして何のために線が引かれているのか?
回答:
Cには、オブジェクトと見なすことができる「構造体」などの独自の準オブジェクトがあるようです
あなたと一緒にオブジェクト指向プログラミングのウィキペディアのページを読み、伝統的にオブジェクト指向スタイルと考えられているものに対応するCスタイル構造体の機能をチェックしてみましょう。
(OOP)は、「オブジェクト」を使用したプログラミングパラダイムです。データフィールドとメソッドとそれらの相互作用で構成されるデータ構造
C構造体は、フィールドとメソッド、およびそれらの相互作用で構成されていますか?番号。
プログラミング手法には、データの抽象化、カプセル化、メッセージング、モジュール性、多態性、継承などの機能が含まれる場合があります。
C構造体は、これらのことを「ファーストクラス」の方法で実行しますか?いいえ。言語はあらゆる段階であなたに対して機能します。
オブジェクト指向のアプローチは、プログラムの残りの部分から直接アクセスできない場所にデータを配置することをプログラマに奨励します
C構造体はこれを行いますか?番号。
オブジェクト指向プログラムには、通常、さまざまなタイプのオブジェクトが含まれます。各タイプは、管理対象の特定の種類の複雑なデータ、またはおそらく実際のオブジェクトまたは概念に対応します
C構造体はこれを行いますか?はい。
オブジェクトは、データが適切に使用されるようにするために設計された一連の関数内にデータをラップすると考えることができます。
番号。
各オブジェクトは、メッセージを受信し、データを処理し、他のオブジェクトにメッセージを送信できます
構造体自体はメッセージを送受信できますか?いいえ。データを処理できますか?番号。
OOPデータ構造は、「独自の演算子を持ち歩く」傾向がある
これはCで起こりますか?番号。
動的ディスパッチ...カプセル化...サブタイプポリモーフィズム...オブジェクトの継承...オープン再帰...オブジェクトのクラス...クラスのインスタンス...接続されたオブジェクトに作用するメソッド...メッセージの受け渡し.. 。抽象化
C構造体のこれらの機能はありますか?番号。
構造体のどの特性が「オブジェクト指向」だと思いますか?私は見つけることができませんので任意の構造体が定義されているという事実以外のタイプを。
これで、もちろん、関数へのポインタであるフィールドを持つ構造体を作成できます。構造体に、仮想メソッドテーブルに対応する関数ポインターの配列へのポインターであるフィールドを持たせることができます。等々。もちろん、CでC ++をエミュレートできます。しかし、それはCでプログラミングする非常に非定型的な方法です。C ++を使用する方が良いでしょう。
また、Cファイル自体は基本的に別個の「モジュール」ですよね?モジュールも「オブジェクト」のようなものではありませんか?
繰り返しますが、オブジェクトのように振る舞うモジュールの特性は何ですか?モジュールは、抽象化、カプセル化、メッセージング、モジュール性、多態性、および継承をサポートしていますか?
抽象化とカプセル化はかなり弱いです。モジュールはモジュール式です。それがモジュールと呼ばれる理由です。メッセージング?メソッド呼び出しがメッセージであり、モジュールがメソッドを含むことができるという意味でのみ。多型?いや。継承?いや。モジュールは「オブジェクト」のかなり弱い候補です。
キーワードは「オブジェクト」ではなく「指向」です。オブジェクトを使用するが構造体のように使用するC ++コードでさえ、オブジェクト指向ではありません。
CとC ++は両方ともOOPを実行できますが(Cのアクセス制御はありません)、Cで実行するための構文は(控えめに言っても)不便ですが、C ++の構文は非常に魅力的です。その点でほぼ同一のコア機能にもかかわらず、Cは手続き型であり、C ++はオブジェクト型です。
オブジェクトのみを使用して実行できる設計を実装するためにオブジェクトを使用するコード(通常は多態性を利用することを意味します)は、オブジェクト指向のコードです。オブジェクト指向言語での継承を使用したとしても、オブジェクトをデータのバッグとほとんど同じように使用するコードは、実際には必要以上に複雑な手続き型コードです。実行時にデータで満たされた構造体で変更される関数ポインタを使用するCのコードは、多態性を実行しているため、手続き指向言語でも「オブジェクト指向」と言えます。
最高レベルのプリンシパルに基づいて:
オブジェクトは、相互リンクされた方法でのデータと動作のカプセル化であり、複数のインスタンスを作成でき、外部インターフェイスがわかっている場合はブラックボックスとして機能する全体として動作します。
構造体にはデータが含まれますが、動作はないため、オブジェクトとは見なされません。
モジュールには動作とデータの両方が含まれていますが、この2つが関連し、確実に複数回インスタンス化できないような方法でカプセル化されていません。
そして、それは継承とポリモーフィズムに入る前です...
「構造体」はデータのみです。「オブジェクト指向」の通常の迅速で汚れたテストは、「コードとデータを単一のユニットとしてカプセル化できる構造がありますか?」です。Cはそれを失敗し、手続き型です。C ++はそのテストに合格します。
this
ポインターが必要になりますが、その場合、データとデータ処理手段は構造内にカプセル化されます。
#include
dを加えたもので、条件付きで含まれないことで削除されたもの(#if
、#ifdef
など)を除いたものです。
Cは、C ++と同様に、データ抽象化を提供する機能を備えています。これは、以前に存在したオブジェクト指向プログラミングパラダイムの1つのイディオムです。
C ++のOOPは、データを抽象化する手段を拡張します。有害だと言う人もいれば、正しく使用すると良いツールだと考える人もいます。
しかし、Cがちょうど適切な量の抽象化を完全に実行できる方法と、C ++によって作成されたオーバーヘッドが実際の問題の解決から注意をそらす方法について説教する多くのC「ハッカー」が見つかります。
効率の悪い抽象プログラミングモデルでは、2年後には抽象化があまり効率的ではないことに気づきましたが、今ではすべてのコードが周囲のすべての素晴らしいオブジェクトモデルに依存しており、アプリを書き直さずに修正することはできません。-ライナス・トーバルズ
他の人は、よりバランスの取れた方法でそれを見て、欠点と欠点の両方を受け入れる傾向があります。
Cを使用すると、自分自身を足で簡単に撃つことができます。C ++はそれを難しくしますが、それを行うと、足全体が吹き飛ばされます。-Bjarne Stroustrup
コインの反対側であるC ++を調べる必要があります。
OOPでは、たとえば、停止、加速、左または右に曲がる車など、抽象的なオブジェクトを考えます(それに応じてプログラムを設計します)。関数のバンドルを持つ構造体は、概念に適合しません。
「実際の」オブジェクトでは、たとえばメンバーを非表示にする必要があります。または、実際の「is a」関係などを継承することもできます。
以下のコメントを読んだ後:( ほとんど)すべてをCで行うことができるのは正しいことです(常に真実です)。
本当に違いを生む唯一のものは、コンパイラによるポリシーの課しです。すなわち、純粋な仮想関数など。しかし、この答えは技術的な問題にのみ関連しますが、主な違いは(言及されているように)、コーディング中にあなたが考える最初の方法だと思います。なぜなら、C ++は、 Cではやや不器用な方法
オブジェクト指向とは、アーキテクチャパターン(またはメタパターン)と、このパターンを使用して実装または強制するのに役立つ機能を持つ言語の両方を指します。
「OO」設計を実装できます(Gnomeデスクトップは、おそらく純粋なCで行われるOOの最良の例です)。
しかし、オブジェクト指向設計を実装できるということは、言語をオブジェクト指向にするわけではありません。純粋主義者は、「int」や「char」などの基本的な「タイプ」をオーバーライドまたは継承することはできず、Javaは多重継承をサポートしないため、JavaおよびC ++は本当にオブジェクト指向ではないと主張します。しかし、それらは最も広く使用されているオブジェクト指向言語であり、作業コードを生成するために報酬を得るほとんどの「本物の」プログラマーをオブジェクト指向言語と見なしています。
一方、Cは構造のみをサポートします(COBOL、Pascal、および他の多数の手続き言語と同様)、任意のデータに対して任意の関数を使用できるが、ほとんどはこれをバグと見なすという点で、多重継承をサポートすると主張できます機能より。
オブジェクト指向の定義を見てみましょう。
Cはこれらの3つを提供しません。特に、最も重要なMessagingを提供していません。
static
)関数とデータメンバーを持つことができる個別のファイルです。
OOには多くの重要な要素がありますが、大きなものは、コードの大部分がオブジェクトの内部(実装ではなく表面インターフェースを参照)を知らないこと、オブジェクトの状態が管理対象ユニットであることです(つまり、オブジェクトが停止し、その状態も停止した場合)、一部のコードがオブジェクトの操作を呼び出した場合、その操作が何であるか、または何を含んでいるかを正確に知ることなくそうします(実行するのはパターンに従うだけです壁を越えて「メッセージ」)。
Cはカプセル化をうまく行います。構造体の定義が見えないコードは、その内部を(正規に)覗くことはできません。必要なのは、次のような定義をヘッダーファイルに入れることだけです。
struct FooImpl;
typedef struct FooImpl *Foo;
もちろん、Foo
s を構築する関数(つまり、ファクトリー)があり、割り当てられたオブジェクト自体に作業の一部を(つまり、「コンストラクター」メソッドを介して)委任する必要があります。 (「デストラクタ」メソッドを介してオブジェクトをクリーンアップしながら)オブジェクトを再度破棄することですが、それは詳細です。
メソッドのディスパッチ(つまり、メッセージング)は、構造体の最初の要素が実際には関数ポインターで満たされた構造体へのポインターであり、それらの各関数ポインターがFoo
最初の引数として取得する必要があるという規則によって行うこともできます。ディスパッチは、関数を検索し、適切な引数を書き換えて呼び出すことの問題になります。これは、マクロと少しずるいことで難しくありません。(その関数のテーブルは、C ++のような言語でクラスが実際に何であるかの中核です。)
さらに、これは遅延バインディングも提供します。ディスパッチコードが知っているのは、オブジェクトが指すテーブルへの特定のオフセットを呼び出すことだけです。オブジェクトの割り当てと初期化中にのみ設定する必要があります。実行時のダイナミズムを(速度を犠牲にして)購入するさらに複雑なディスパッチスキームを使用することもできますが、それらは基本的なメカニズムに加えてチェリーです。
しかし、それはCがオブジェクト指向言語であることを意味しません。重要なことは、Cが規約とディスパッチメカニズムを自分で記述する(またはサードパーティのライブラリを使用する)トリッキーな作業をすべてあなたに任せることです。それは大変な仕事です。また、構文またはセマンティックのサポートも提供しないため、完全なクラスシステム(継承など)を実装するのは不必要に苦痛です。OOモデルで適切に記述された複雑な問題を扱っている場合、OO言語はソリューションを書くのに非常に役立ちます。余分な複雑さは正当化できます。
Cはオブジェクト指向の概念shrugを実装するのに完璧で適切だと思います。オブジェクト指向と見なされる言語の共通分母サブセット間で見た違いの大部分は、私の種の実際的な観点から見ると、本質的にはマイナーで構文的なものです。
たとえば、情報の隠蔽から始めましょう。Cでは、単純に構造体の定義を非表示にし、不透明なポインターを使用して構造体を操作することで、これを実現できます。それは効果的なモデルpublic
対のprivate
データフィールドの区別を私たちはクラスで取得するとして。標準Cライブラリは情報隠蔽を達成するためにこれに大きく依存しているため、それは簡単で反イディオマティックではありません。
もちろん、不透明型を使用してメモリ内の構造を割り当てる正確な場所を簡単に制御する機能は失われますが、これは、たとえばCとC ++の注目すべき違いにすぎません。C ++は、メモリレイアウトの制御を維持しながらCでオブジェクト指向の概念をプログラムする能力を比較する場合、間違いなく優れたツールですが、その点で必ずしもJavaまたはC#がCより優れているという意味ではありません。メモリ内のオブジェクトの割り当て場所を制御する機能を完全に失います。
そして、大きなフープfopen(file, ...); fclose(file);
とは対照的な構文を使用する必要がありますfile.open(...); file.close();
。誰が本当に気にしますか?IDEのオートコンプリートに大きく依存している人がいるかもしれません。これは実用的な観点からは非常に便利な機能ですが、言語がOOPに適しているかどうかの議論を必要とするものではないかもしれません。
protected
フィールドを効果的に実装する能力はありません。私は完全にそこに提出します。しかし、「すべてのOO言語には、サブクラスが、通常のクライアントからはまだアクセスできない基本クラスのメンバーにアクセスできるようにする機能が必要です」という具体的なルールはないと思います。それに、メンテナンスのハードルになることを少なくとも少し疑っていない保護されたメンバーのユースケースはめったにありません。
そしてもちろん、私たちは類推それらを初期化するために、もう少し定型で動的ディスパッチのためにそれらに関数ポインタとポインタのテーブルをOO多型を「エミュレート」しなければならないvtables
とvptrs
、しかし、定型の少しは私に多くの悲しみを引き起こしたことはありません。
継承はほぼ同じ方法です。構成によってそれを簡単にモデル化することができ、コンパイラーの内部動作では、同じものに要約されます。私たちがしたい場合はもちろん、私たちは、型の安全性を失うダウンキャスト、そしてあなたがしたい場合はそこに私が言うと思いますダウンキャストのすべての人々がエミュレートするためにCで物事のでそれのためにCを使用しない喜ばせるために、ダウンキャストがタイプから恐ろしいことができます安全性の観点からですが、私はむしろ人々がまったく見落とさないようにしたいです。型安全性は、コンパイラがビットとバイトとして物事を解釈するための非常に多くの余裕を提供するため、Cで簡単に見逃し始めることができますが、コンパイル時に予想されるエラーをキャッチする機能を犠牲にしますが、オブジェクト指向と見なされる言語もあります静的に入力することもできません。
だから知らない、大丈夫だと思う。もちろん、Cを使用して、SOLIDの原則に準拠する大規模なコードベースを作成しようとはしませんが、必ずしもオブジェクト指向の面での短所によるものではありません。このような目的でCを使用しようとした場合に見逃してしまう機能の多くは、強力な型安全性、オブジェクトがスコープ外に出たときに自動的に呼び出されるデストラクタ、演算子など、OOPの前提条件とは直接見なされない言語機能に関連しますオーバーロード、テンプレート/ジェネリック、および例外処理。C ++に到達した補助機能が不足しているときです。
foo_create
、foo_destroy
andおよびの形式に似ている可能性がありますfoo_clone
。たとえば
struct Foo
このような場合に不変式を維持する必要があるのは、データ型のメンバーが外部の世界からアクセスおよび変更できないようにすることです。不透明な型(宣言されていますが、外部の世界に定義されていません)がすぐに提供します。これは間違いなく、pimpl
C ++ の情報隠蔽と同等の、より強力な情報隠蔽形式です。