私は標準n4296(ドラフト)§1.8ページ7を読みます:
オブジェクトはストレージの領域です。 [注:オブジェクトが行う方法でストレージを占有するかどうかに関係なく、関数ではないオブジェクト。 —注を終了]
私は数日間ネット上でそのような除外の正当な理由を探してみましたが、運はありませんでした。オブジェクトを完全に理解していないためかもしれません。そう:
多くの違いは、ポインターとアドレス指定にあります。 C++¹では、関数へのポインターとオブジェクトへのポインターは厳密に別の種類のものです。
C++では、任意のオブジェクト型へのポインターをvoid
へのポインターに変換し、それを元の型に戻すことができなければなりません。結果は、開始したポインターと等しくなります²。言い換えると、実装方法は、正確にどのように行われても、ポインターからオブジェクト型へのポインターからボイドへの変換が無損失であることを保証する必要があります。 T*
からvoid *
に変換してT*
に戻すことで、最初と同じポインターを取得できるように再作成してください。
それはnot関数へのポインターではtrue ---関数へのポインターを取得し、それをvoid *
に変換してから、関数へのポインターに戻すと、 may loseプロセスの一部の情報。元のポインターを取り戻せない場合がありますが、取り戻した内容を逆参照すると、未定義の動作が発生します(要するに、そうしないでください)。
それが価値があるのは、あなたはcanですが、ある関数へのポインタを別のタイプの関数へのポインタに変換し、その結果を元のタイプに変換し直せば、結果は、最初と同じです。
手元の議論には特に関係ありませんが、注目に値する他の違いがいくつかあります。たとえば、ほとんどのオブジェクトをコピーできますが、コピーすることはできませんany関数。
関数オブジェクトとの関係に関する限り:まあ、実際には1ポイントを超えるものはほとんどありません:関数オブジェクトはsyntaxをサポートしますが、これは関数呼び出しのように見えますが、それでもオブジェクトです。機能。したがって、関数オブジェクトへのポインターは、オブジェクトへのポインターのままです。たとえば、1をvoid *
に変換してから元の型に戻す場合、元のポインター値を返すことが保証されます(関数へのポインターでは正しくありません) )。
whyに関しては、関数へのポインタは(少なくとも潜在的に)オブジェクトへのポインタとは異なります:その一部は既存のシステムに由来します。たとえば、MS-DOS(とりわけ)には、4つの完全に独立したメモリモデルがありました:小、中、コンパクト、大。スモールモデルでは、関数またはデータのいずれかに16ビットアドレス指定を使用しました。メディアは、データに16ビットアドレスを使用し、コードに20ビットアドレスを使用しました。コンパクトはそれを逆にしました(コード用の16ビットアドレス、データ用の20ビットアドレス)。コードとデータの両方に20ビットのアドレスが使用されています。そのため、コンパクトモデルまたはミディアムモデルのどちらでも、コードへのポインターと関数へのポインターを変換すると、実際に問題が発生する可能性があり、実際に問題が発生しました。
最近では、かなりの数のDSPがコードとデータ用に完全に別個のメモリバスを使用しており(MS-DOSメモリモデルのように)幅が異なることが多く、2つの間で変換すると情報を失う可能性がありました。
char
へのポインターへの変換、およびその価値のあるものへの変換についても、ほとんど同じことが言えます。関数がオブジェクトではないのはなぜですか?どう違いますか?
これを理解するために、関連する抽象化の観点から下から上に移動しましょう。したがって、メモリの状態を定義できるアドレス空間があります基本的には、操作するこの状態に関することをすべて覚えておく必要があります。
さて、抽象化の観点からもう少し上に移動しましょう。私はまだプログラミング言語によって課せられた抽象化(オブジェクト、配列など)については考えていませんが、単なる素人として、メモリの一部の記録を保持したいので、それを呼び出しましょうAb1
とAb2
と呼ばれる別のコード。
両方とも基本的に状態を持っていますが、私は状態異なるを操作/使用するつもりです。
どうして ?
私の要件のため(たとえば、2つの数字の加算を実行し、結果を保存する)。 use Ab1
を長い使用状態として使用し、Ab2
を比較的短い使用状態として使用します。したがって、Ab1
(追加する2つの数字を追加)の状態を作成し、この状態を使用してAb2
(一時的にコピー)の状態の一部を取り込み、さらに操作を実行しますのAb2
(それらを追加)し、結果のAb2
の一部をAb1
(追加された結果)に保存します。 Ab2
が役に立たなくなり、その状態をリセットしてください。
どうやって?
Ab1
からどの単語を選択し、Ab2
にコピーするかなどを追跡するために、両方の部分を管理する必要があります。この時点で、いくつかの簡単な操作を実行できるようにすることができることに気付きましたが、深刻なことには、このメモリを管理するためのレイアウト仕様が必要になります。
したがって、私はそのような管理仕様を探しますが、より優れた設計のさまざまなこれらの仕様が存在することがわかりました(一部のメモリモデルが内蔵されているもの、他のメモリを管理する柔軟性を提供する)。事実上、それら(メモリを直接管理する方法を指図することさえなし)は、この長寿命ストレージのカプセル化と、これをいつどのように作成および破棄できるかのルールを正常に定義したためです。
Ab2
についても同じことが言えますが、それらの表示方法は、Ab1
とは大きく異なるように感じます。そして実際、そうであることが判明しました。 Ab2
の状態操作にスタックを使用し、Ab1
のヒープからメモリを予約します。 Ab2
はしばらくしてから死にます(実行が終了した後)。
また、Ab2
で何をするかを定義する方法は、Ab2_Code
と呼ばれる別のストレージ部分を介して行われ、Ab1
の仕様も同様にAb1_Code
を含みます。
これは素晴らしいと思います!私は非常に多くの問題を解決できるように非常に便利です。
今、私はまだ素人の視点から見ているので、すべての思考プロセスを本当に経験したことに驚きはしませんが、物事をトップダウンで質問すると、物事を視点に入れるのが少し難しくなる可能性があります。(それがあなたのケースで起こったことだと思う)
ところで、私はAb1
が正式にobjectと呼ばれ、Ab2
が関数スタックと呼ばれるのを忘れていましたが、Ab1_Code
はクラス定義とAb2_Code
は関数です定義コード。
そして、PLによって課されたこれらの違いのために、あなたはそれらが非常に異なることがわかります。(あなたの質問)
注:Ab1
/Object
の表現を、ルールまたは具体的なものとしての長いストレージ抽象化として受け取らないでください-それは素人の観点からでした。プログラミング言語は、オブジェクトのライフサイクルを管理するという点ではるかに柔軟性を提供します。したがって、オブジェクトはAb1
のようにデプロイできますが、もっと多くの場合があります。
そして、これはファンクター(関数オブジェクト)と関係がありますか?
最初の部分の答えは、一般に多くのプログラミング言語(C++を含む)に有効であり、この部分はC++(仕様を引用した)に特に関係していることに注意してください。したがって、関数へのポインタを持っている、オブジェクトへのポインタも持つことができます。 C++が定義するもう1つのプログラミング構造。これは、Ab1
、Ab2
へのポインターを使用して、それらを操作する別の抽象概念を持たせるのではなく、それらを操作することに注意してください。
その定義、使用法についてはこちらをご覧ください:
より単純な言語(用語)で質問に答えさせてください。
基本的に何かを行うための指示が含まれています。命令を実行している間、関数は一時的にデータを保存したり使用したりできます。また、データを返すこともあります。
命令はどこかに保存されますが、これらの命令自体はオブジェクトとは見なされません。
一般に、オブジェクトはデータを含むエンティティであり、関数(命令)によって操作、変更、更新されます。
コンピュータは、命令がデータに依存しないように設計されているためです。
これを理解するために、計算機について考えてみましょう。電卓を使用してさまざまな数学演算を行います。いくつかの数字を追加したい場合は、計算機に数字を提供します。数字が何であっても、計算機は同じ指示に従って同じ方法でそれらを追加します(結果が計算機の保存能力を超える場合、エラーが表示されます-しかし、それは計算機の結果を保存する制限のためです(データ)、追加の指示ではありません)。
コンピューターも同様の方法で設計されています。そのため、関数と互換性のあるデータでライブラリ関数(たとえばqsort()
)を使用すると、期待どおりの結果が得られます。関数の機能は次の場合には変わりません。データの変更-関数の命令は変更されないため。
機能は一連の指示です。また、実行中に、一時的なデータを保存する必要がある場合があります。つまり、一部のオブジェクトは、関数の実行中に一時的に作成される場合があります。これらの一時オブジェクトはファンクターです。