まったく理解したことがありません。それはできますが、なぜそうしたいのかわかりません。
たとえば、私は昨日ゲームをプログラミングしていて、ゲーム内で動的に割り当てられた敵にポインターの配列を設定し、それをそれらの位置を更新する関数に渡しました。
私がゲームを実行したとき、私はそれらの非説明的なアサーションエラーの1つを受け取りました、メモリブロックに関するものは何か、私にはわかりません。これは実行時エラーだったので、どこに問題があるのかはわかりませんでした。だから私はそれをねじ込んで静的インスタンス化で書き直した、つまり:
while(n<4)
{
Enemy tempEnemy = Enemy(3, 4);
enemyVector.Push_back(tempEnemy);
n++;
}
updatePositions(&enemyVector);
そして、それはすぐに完璧に機能しました。
確かに、「あなたが何をしていたのかわかっていれば」または「n00bはポインターL0Lを使用できない」という影響について何かを考えている人もいるかもしれませんが、率直に言って、実際に彼らが物事を作っていることを否定することはできません方法が複雑すぎるため、最新のほとんどの言語は完全に廃止されています。
しかし-誰か-何[〜#〜]は[〜#〜]動的割り当てのポイントですか?それにはどのような利点がありますか?上記の例でやったことをやらないのはなぜですか?
まず、動的割り当てを使用してareします。生のポインタを自分で処理しないでください。だが std::vector
は、他のほぼすべての有用なデータ構造と同様に、内部的にメモリを動的に割り当てます。これは、選択肢が1つしかないためです:静的割り当て(制限が多すぎる、コンパイル時でサイズを知る必要があります)およびスタック割り当て(制限が多すぎる、最大で数メガバイトで、すぐに割り当て解除されます)割り当て関数が戻るとき)。ダイナミックは、最大量のメモリを提供し、使用方法を最も自由にします。ポインターまたは参照も、割り当てられたサイズの大きさを抽象化します-スタックでは、コンパイル時に知っておく必要があります(またはalloca
を使用して手動で割り当てる必要がありますが、さらに注意が必要ですそれでもポインタを取得します)。
第二に、多くのC++プログラマーはrawポインターはほとんどの場合あまり有用ではないことに同意します。正確には、あなたが引用する複雑さとエラーの傾向があるためです。あなたは内部でそれらを必要とし、あなたはすべきそれらを絶対に使用することができますが、正気さ、正確さ、プログラマーのパフォーマンス、例外の安全性のために、すべてのコードの99%でそれを行うべきではありません、メモリリークの回避など。代替策は、コンテナの組み合わせ(std::vector
は氷山の一角にすぎません)、スマートポインタ、他の形式のRAII、それを回避できるときはいつでもプレーンなスタック割り当て、および参照。
また、ポインタ(およびその他の種類の間接参照)と動的割り当てには違いがあることに注意してください。ポインター(rawとsmart、および参照)は、ポインティングされたオブジェクトがどのように割り当てられているかに関係なく、(コメンターが指摘したように)多態性を提供します。あなたはwillをどのように/どこで/どのように割り当てるかに関係なく、それら、参照、スマートポインタ、それに続く所有権などの問題に頭を抱える必要があります。
デルナンは、動的割り当てが何であるかについての良い説明を提供しました。 +1。
追加したいのは、必要なときにダイナミックアロケーションを使用することだけです。オブジェクトの固定サイズが小さい場合は、静的に割り当てるだけで十分です。単一の関数内で寿命の短いオブジェクトが必要な場合は、スタック上のローカルオブジェクトが最適であることを確認してください。
他のメソッドでは不十分な場合にのみ、オブジェクトを動的に割り当てます。言語で許可されているからといって使用しないでください。遅かれ早かれ、必要になるでしょう。
C++テンプレートについても同様です。私はあまりにも多くの人がテンプレートをあちこちに配置し始めるのを見てきました。次に、同じ人々が、C++が複雑すぎて複雑すぎると文句を言います。 C++には多くの高度な機能がありますが、それらの機能をいつ、どこで使用するかは開発者が決定します。しかし、他に選択肢がない場合、ダイナミックアロケーション(およびテンプレート)を使用すると、他の方法では不可能だったことができるようになります。
C++とJavaのような言語の主な違いは、Javaでは次のようなステートメントを作成することです。
MyObject b = new MyObject(...);
myArrayOfObjects.add(b);
オブジェクトは参照です。この抽象化は、高水準言語で非常に強力であることが証明されています。オブジェクトはヒープに割り当てられ、参照がカウントされるため、そのメモリの割り当てを解除する責任がなくなります。ガベージコレクターは、オブジェクトへの参照が残っていない場合にそれを通知し、オブジェクトをクリーンアップします。
問題の事実は、C++はASより高いレベルではないということです。 C++の利点は、非常に手動のメモリ管理にドロップダウンして、パフォーマンスを向上させたり、コードの特定のパフォーマンス集中領域を微調整できることです。
C++では、オブジェクトがクラスのメンバーになるか、他のクラスに渡されて保存され、他のクラスで必要になる場合などポインターを使用する必要があります。
MyObject *myClassMemberVariable
現在、C++には、このような生のポインタを使用してneverを支持する支持者がいます。 boostのようなライブラリーや標準ライブラリーでも、スマートポインターなどのクラスを作成し、生のポインターを「ラップ」することでリソースの多少の自動メモリー管理を実装しようとし、メモリーを管理するためのさまざまなスキームを提供します。
すべては、ポインタをいつ使用するか、およびメモリをどのように管理するかについて自分で決定しなければならないことにかかっています。これが主にC++を他の言語よりも困難にしている理由です(とりわけ...)。ただし、その使用法と、それを使用するプロジェクトに対する利点はあります。
さて、あなたはポインタを使用しない現代の言語についてはかなり間違っています。彼らはそれらを別のものと呼びますが、それらはまだ内部ではポインタです。また、スタック割り当ても削除されません。たとえば、.NETを参照して、参照型と値型の違いを確認してください。
しかし、それはあなたにとって何の違いもありません。あなたの問題はあなたが分からないことです;あなたの割り当てで何が起こるかを完全に理解していません。
コンピュータは、ヒープとスタックの2つのグループに分割された大量のメモリで動作します。スタックは、関数呼び出し間でデータを一時的に渡すために使用されます。ヒープは、オブジェクトのためのより「グローバルな」ストレージスペースです。 C++では、割り当てたとおりにスタックを無料で取得します。_Enemy e;
_はスタック割り当てです。ヒープを使用するには、オブジェクトを作成し、通常はポインターを使用して、自分で割り当てられた場所を追跡する必要があります。したがって、Enemy* e = new Enemy();
は、ヒープ上にEnemyサイズのオブジェクトを割り当てますが、そのオブジェクト自体ではなく、そのオブジェクトへのポインタになります。 e
自体はスタックに割り当てられていることに注意してください(結局、すべての変数をどこかに格納する必要があります)。
次に、関数を呼び出すとどうなるか-値によるコピーと参照によるコピーについて知っていますか?スタックにオブジェクトを割り当てた場合は、関数を呼び出すときにオブジェクトのコピーを取得しますが、ヒープに割り当てた場合は、ポインターを渡すと、ポインターのコピーを取得します(もちろん、これはまだヒープ上の同じオブジェクトを指しています)。
Javaまたは.NETのヒープベースのオブジェクトの処理方法に慣れている場合、C++で最も近いのは、スマートポインタ、shared_ptr <>を使用することです。おそらくあなたが望むものです。生のポインタの代わりにこれらを使用するか、オブジェクトをスタックすれば、はるかに幸せになります。GC言語に対するボーナスは、誰もオブジェクトを使用しなくなったときに、それらの割り当てを即座に解除できることです。 (したがって、あなたは敵のデススロールーチンをデストラクタに入れて、敵が削除されたときにシステムに自動的にそれを呼び出させることができます)