オブジェクト自体ではなくポインタを使用する必要があるのはなぜですか?

1664
gEdringer 2014-03-04 01:54.

私はJavaのバックグラウンドを持っており、C ++でオブジェクトの操作を開始しました。しかし、私が思いついたのは、人々がオブジェクト自体ではなくオブジェクトへのポインタを使用することが多いということです。たとえば、次の宣言です。

Object *myObject = new Object;

のではなく:

Object myObject;

または、関数を使用する代わりにtestFunc()、次のようにしましょう。

myObject.testFunc();

私たちは書く必要があります:

myObject->testFunc();

しかし、なぜこのようにすべきなのか理解できません。メモリアドレスに直接アクセスできるので、効率と速度に関係していると思います。私は正しいですか?

20 answers

1621
Joseph Mansfield 2014-03-04 02:01.

動的割り当てが頻繁に発生するのは非常に残念です。これは、悪いC ++プログラマーが何人いるかを示しています。

ある意味で、2つの質問が1つにまとめられています。1つ目は、(を使用してnew)動的割り当てをいつ使用する必要があるかです。2つ目は、いつポインターを使用する必要があるかです。

重要な持ち帰りメッセージは、常に仕事に適したツールを使用する必要があるということです。ほとんどすべての状況で、手動の動的割り当てを実行したり、生のポインタを使用したりするよりも適切で安全な方法があります。

動的割り当て

あなたの質問では、オブジェクトを作成する2つの方法を示しました。主な違いは、オブジェクトの保存期間です。Object myObject;ブロック内で実行する場合、オブジェクトは自動保存期間で作成されます。つまり、スコープ外になると自動的に破棄されます。を実行するnew Object()と、オブジェクトには動的な保存期間がありますdelete。つまり、明示的に指定するまでオブジェクトは存続します。動的ストレージ期間は、必要な場合にのみ使用してください。つまり、可能な場合は常に自動保存期間でオブジェクトを作成することをお勧めします

動的割り当てが必要になる可能性がある主な2つの状況:

  1. 現在のスコープよりも長持ちするオブジェクトが必要です。つまり、その特定のメモリ位置にある特定のオブジェクトであり、そのコピーではありません。オブジェクトのコピー/移動に問題がない場合(ほとんどの場合はそうする必要があります)、自動オブジェクトを選択する必要があります。
  2. 大量のメモリを割り当てる必要があり、スタックが簡単にいっぱいになる可能性があります。これは実際にはC ++の範囲外であるため、これについて気にする必要がない場合は(ほとんどの場合、気にする必要はありません)、それは素晴らしいことですが、残念ながら、システムの現実に対処する必要があります。私たちはのために開発しています。

動的割り当てがどうしても必要な場合は、スマートポインター、またはRAIIを実行するその他のタイプ(標準コンテナーなど)にカプセル化する必要があります。スマートポインタは、動的に割り当てられたオブジェクトの所有権セマンティクスを提供します。たとえば、std::unique_ptrとを見てくださいstd::shared_ptr。それらを適切に使用すれば、独自のメモリ管理を実行することをほぼ完全に回避できます(ゼロルールを参照)。

ポインタ

ただし、動的割り当て以外にも、rawポインターのより一般的な使用法がありますが、ほとんどの場合、優先する代替手段があります。以前と同様に、本当にポインタが必要でない限り常に代替手段を優先します

  1. 参照セマンティクスが必要です。オブジェクトを渡す先の関数がその特定のオブジェクト(オブジェクトのコピーではない)にアクセスできるようにしたいので、ポインターを使用してオブジェクトを渡したい場合があります(オブジェクトの割り当て方法に関係なく)。ただし、ほとんどの場合、ポインターよりも参照型を優先する必要があります。これは、ポインターが特に設計されているためです。上記の状況1のように、これは必ずしもオブジェクトの存続期間を現在のスコープを超えて延長することではないことに注意してください。以前と同様に、オブジェクトのコピーを渡しても問題がない場合は、参照セマンティクスは必要ありません。

  2. ポリモーフィズムが必要です。関数は、オブジェクトへのポインタまたは参照を介してのみ、多態的に(つまり、オブジェクトの動的タイプに従って)呼び出すことができます。それが必要な動作である場合は、ポインターまたは参照を使用する必要があります。繰り返しますが、参照が優先されます。

  3. オブジェクトが省略されnullptrているときにを渡すことができるようにすることで、オブジェクトがオプションであることを表現したいとします。引数の場合は、デフォルトの引数または関数のオーバーロードを使用することをお勧めします。それ以外の場合は、この動作をカプセル化するタイプを使用することをお勧めしますstd::optional(C ++ 17で導入-以前のC ++標準では、を使用boost::optional)。

  4. コンパイル時間を改善するために、コンパイルユニットを分離したいとします。ポインタの便利なプロパティは、ポイント先の型の前方宣言のみが必要なことです(実際にオブジェクトを使用するには、定義が必要です)。これにより、コンパイルプロセスの一部を切り離すことができ、コンパイル時間が大幅に改善される可能性があります。Pimplイディオムを参照してください。

  5. CライブラリまたはCスタイルライブラリとインターフェイスする必要があります。この時点で、生のポインタを使用する必要があります。あなたができる最善のことは、可能な限り最後の瞬間に生のポインタだけを緩めるようにすることです。たとえば、そのgetメンバー関数を使用して、スマートポインターから生のポインターを取得できます。ライブラリがハンドルを介して割り当てを解除することを期待する割り当てを実行する場合、オブジェクトを適切に割り当て解除するカスタム削除機能を使用して、ハンドルをスマートポインターでラップすることができます。

176
TemplateRex 2014-03-04 02:06.

ポインタには多くのユースケースがあります。

ポリモーフィックな振る舞い。ポリモーフィック型の場合、スライスを回避するためにポインター(または参照)が使用されます。

class Base { ... };
class Derived : public Base { ... };

void fun(Base b) { ... }
void gun(Base* b) { ... }
void hun(Base& b) { ... }

Derived d;
fun(d);    // oops, all Derived parts silently "sliced" off
gun(&d);   // OK, a Derived object IS-A Base object
hun(d);    // also OK, reference also doesn't slice

セマンティクスを参照し、コピーを回避します。非ポリモーフィック型の場合、ポインター(または参照)は潜在的に高価なオブジェクトのコピーを回避します

Base b;
fun(b);  // copies b, potentially expensive 
gun(&b); // takes a pointer to b, no copying
hun(b);  // regular syntax, behaves as a pointer

C ++ 11には、高価なオブジェクトの関数の引数へのコピーや戻り値としてのコピーを回避できる移動セマンティクスがあることに注意してください。ただし、ポインターを使用すると、確実にそれらが回避され、同じオブジェクト上で複数のポインターが許可されます(オブジェクトは1回からしか移動できません)。

リソースの取得new演算子を使用してリソースへのポインターを作成することは、最新のC ++ではアンチパターンです。特別なリソースクラス(標準コンテナの1つ)またはスマートポインタstd::unique_ptr<>またはstd::shared_ptr<>)を使用します。考えてみましょう:

{
    auto b = new Base;
    ...       // oops, if an exception is thrown, destructor not called!
    delete b;
}

対。

{
    auto b = std::make_unique<Base>();
    ...       // OK, now exception safe
}

生のポインタは「ビュー」としてのみ使用する必要があり、直接作成によるものであれ、戻り値による暗黙的なものであれ、所有権に関与するものではありません。どの種類のポインタをいつ使用しますか?も参照してどの種類のポインタをいつ使用しますか?

よりきめ細かいライフタイムコントロール共有ポインタがコピーされるたびに(たとえば、関数の引数として)、それが指すリソースは存続します。通常のオブジェクト(newユーザーが直接作成したものでも、リソースクラス内で作成したものでもない)は、スコープ外になると破棄されます。

133
Gerasimos R 2014-03-07 08:40.

前方宣言やポリモーフィズムなどの重要なユースケースを含め、この質問には多くの優れた回答がありますが、質問の「魂」の一部、つまりJavaとC ++での構文の違いについては回答されていないと思います。

2つの言語を比較して状況を調べてみましょう。

Java:

Object object1 = new Object(); //A new object is allocated by Java
Object object2 = new Object(); //Another new object is allocated by Java

object1 = object2; 
//object1 now points to the object originally allocated for object2
//The object originally allocated for object1 is now "dead" - nothing points to it, so it
//will be reclaimed by the Garbage Collector.
//If either object1 or object2 is changed, the change will be reflected to the other

これに最も近いものは次のとおりです。

C ++:

Object * object1 = new Object(); //A new object is allocated on the heap
Object * object2 = new Object(); //Another new object is allocated on the heap
delete object1;
//Since C++ does not have a garbage collector, if we don't do that, the next line would 
//cause a "memory leak", i.e. a piece of claimed memory that the app cannot use 
//and that we have no way to reclaim...

object1 = object2; //Same as Java, object1 points to object2.

別のC ++の方法を見てみましょう。

Object object1; //A new object is allocated on the STACK
Object object2; //Another new object is allocated on the STACK
object1 = object2;//!!!! This is different! The CONTENTS of object2 are COPIED onto object1,
//using the "copy assignment operator", the definition of operator =.
//But, the two objects are still different. Change one, the other remains unchanged.
//Also, the objects get automatically destroyed once the function returns...

それを考える最良の方法は、Javaが(暗黙的に)オブジェクトへのポインタを処理するのに対し、C ++はオブジェクトへのポインタまたはオブジェクト自体のいずれかを処理する可能性があるということです。これには例外があります。たとえば、Javaの「プリミティブ」型を宣言した場合、それらはコピーされる実際の値であり、ポインタではありません。そう、

Java:

int object1; //An integer is allocated on the stack.
int object2; //Another integer is allocated on the stack.
object1 = object2; //The value of object2 is copied to object1.

とはいえ、ポインタを使用することは、必ずしも正しい方法でも間違った方法でもありません。しかし、他の答えはそれを十分にカバーしています。ただし、一般的な考え方は、C ++では、オブジェクトの存続期間と、オブジェクトが存在する場所をより詳細に制御できるということです。

要点をObject * object = new Object()理解してください。構造は、実際には、典型的なJava(またはC#)のセマンティクスに最も近いものです。

82
user3391320 2014-03-07 21:30.

序文

誇大宣伝とは対照的に、JavaはC ++のようなものではありません。Javaの誇大宣伝機では、JavaにはC ++のような構文があるため、言語は似ていると信じてもらいたいと思います。真実から遠く離れることはできません。この誤った情報は、JavaプログラマーがC ++にアクセスし、コードの意味を理解せずにJavaのような構文を使用する理由の一部です。

以降、

しかし、なぜこのようにすべきなのか理解できません。メモリアドレスに直接アクセスできるので、効率と速度に関係していると思います。私は正しいですか?

それどころか、実際には。スタックはヒープに比べて非常に単純であるため、ヒープでのメモリ割り当てがスタックよりもはるかに遅いのはなぜですか?スタックヒープでのメモリ割り当てがスタックよりもはるかに遅いのはなぜですか?ヒープでのメモリ割り当てがスタックよりもはるかに遅いのはなぜですか?です。自動ストレージ変数(別名スタック変数)では、スコープ外になるとデストラクタが呼び出されます。例えば:

{
    std::string s;
}
// s is destroyed here

一方、動的に割り当てられたポインタを使用する場合は、そのデストラクタを手動で呼び出す必要があります。deleteこのデストラクタを呼び出します。

{
    std::string* s = new std::string;
}
delete s; // destructor called

これはnew、C#やJavaで普及している構文とは何の関係もありません。それらは完全に異なる目的で使用されます。

動的割り当ての利点

1.事前に配列のサイズを知る必要はありません

多くのC ++プログラマーが遭遇する最初の問題の1つは、ユーザーからの任意の入力を受け入れる場合、スタック変数に固定サイズしか割り当てることができないことです。配列のサイズも変更できません。例えば:

char buffer[100];
std::cin >> buffer;
// bad input = buffer overflow

もちろん、std::string代わりに使用した場合は、std::string内部的にサイズが変更されるため、問題は発生しません。しかし、本質的にこの問題の解決策は動的割り当てです。ユーザーの入力に基づいて動的メモリを割り当てることができます。次に例を示します。

int * pointer;
std::cout << "How many items do you need?";
std::cin >> n;
pointer = new int[n];

補足:多くの初心者が犯す間違いの1つは、可変長配列の使用です。これはGNU拡張機能であり、GCCの拡張機能の多くを反映しているため、Clangの拡張機能でもあります。したがって、以下 int arr[n]は信頼されるべきではありません。

ヒープはスタックよりもはるかに大きいため、必要なだけのメモリを任意に割り当て/再割り当てできますが、スタックには制限があります。

2.配列はポインタではありません

これはあなたが求めるメリットは何ですか?配列とポインタの背後にある混乱/神話を理解すると、答えが明らかになります。それらは同じであると一般に想定されていますが、そうではありません。この神話は、ポインターが配列と同じように添え字を付けることができ、配列が関数宣言の最上位のポインターに減衰するためです。ただし、配列がポインタに減衰すると、ポインタはそのsizeof情報を失います。したがってsizeof(pointer)、ポインタのサイズをバイト単位で示します。これは通常、64ビットシステムでは8バイトです。

配列に割り当てることはできず、初期化するだけです。例えば:

int arr[5] = {1, 2, 3, 4, 5}; // initialization 
int arr[] = {1, 2, 3, 4, 5}; // The standard dictates that the size of the array
                             // be given by the amount of members in the initializer  
arr = { 1, 2, 3, 4, 5 }; // ERROR

一方、ポインタを使用すると、好きなことを行うことができます。残念ながら、JavaとC#ではポインターと配列の区別が手作業で行われるため、初心者はその違いを理解していません。

3.ポリモーフィズム

JavaとC#には、たとえばasキーワードを使用して、オブジェクトを別のオブジェクトとして扱うことができる機能があります。したがって、誰かがEntityオブジェクトをオブジェクトとして扱いたい場合はPlayer、次のことができます。Player player = Entity as Player;これは、特定のタイプにのみ適用される同種のコンテナーで関数を呼び出す場合に非常に役立ちます。この機能は、以下と同様の方法で実現できます。

std::vector<Base*> vector;
vector.push_back(&square);
vector.push_back(&triangle);
for (auto& e : vector)
{
     auto test = dynamic_cast<Triangle*>(e); // I only care about triangles
     if (!test) // not a triangle
        e.GenericFunction();
     else
        e.TriangleOnlyMagic();
}

つまり、TrianglesだけにRotate関数がある場合、クラスのすべてのオブジェクトでそれを呼び出そうとすると、コンパイラエラーになります。を使用してdynamic_castasキーワードをシミュレートできます。明確にするために、キャストが失敗した場合、無効なポインタを返します。したがって、これ!testは基本的に、testがNULLであるか無効なポインタであるかを確認するための省略形であり、キャストが失敗したことを意味します。

自動変数の利点

動的割り当てで実行できるすべての優れた機能を確認した後、動的割り当てを常に使用しないのはなぜだろうかと疑問に思われるかもしれません。すでに1つの理由を説明しましたが、ヒープが遅いのです。そして、そのすべてのメモリが必要ない場合は、それを悪用しないでください。したがって、ここに特定の順序ではないいくつかの欠点があります。

  • エラーが発生しやすいです。手動でのメモリ割り当ては危険であり、リークが発生しやすくなります。デバッガーまたはvalgrind(メモリリークツール)の使用に習熟していない場合は、頭から髪の毛を抜くことができます。幸いなことに、RAIIのイディオムとスマートポインターはこれを少し軽減しますが、Rule OfThreeやRuleOfFiveなどのプラクティスに精通している必要があります。取り入れるべき情報はたくさんあり、知らないか気にしない初心者はこの罠に陥ります。

  • それは必要ない。newどこでもキーワードを使用するのが慣用的なJavaやC#とは異なり、C ++では、必要な場合にのみ使用する必要があります。一般的な言い回しは、ハンマーを持っているとすべてが釘のように見えるということです。C ++で始める初心者はポインターを怖がり、習慣によってスタック変数の使い方を学びますが、JavaおよびC#プログラマーは、ポインターを理解せずに使用することから始めます。それは文字通り間違った足で降りています。構文は1つであり、言語の学習は別のものであるため、知っていることはすべて放棄する必要があります。

1.(N)RVO-別名、(名前付き)戻り値の最適化

多くのコンパイラが行う最適化の1つは、省略戻り値の最適化と呼ばれるものです。これらのことにより、多くの要素を含むベクトルなど、非常に大きなオブジェクトに役立つ不要なコピーを取り除くことができます。通常、一般的な方法は、大きなオブジェクトをコピーして移動するのではなく、ポインタを使用して所有権譲渡することです。これは、移動セマンティクススマートポインタの始まりにつながりました。

ポインタを使用している場合、(N)RVOは発生しません。最適化が心配な場合は、ポインターを返したり渡したりするよりも、(N)RVOを利用する方が有益で、エラーが発生しにくくなります。関数の呼び出し元がdelete動的に割り当てられたオブジェクトなどを処理する責任がある場合、エラーリークが発生する可能性があります。ホットポテトのようにポインタが渡されている場合、オブジェクトの所有権を追跡するのは難しい場合があります。スタック変数はよりシンプルで優れているため、使用するだけです。

80
Burnt Toast 2014-03-04 04:34.

ポインターを使用するもう1つの理由は、前方宣言のためです。十分に大きなプロジェクトでは、コンパイル時間を大幅に短縮できます。

23
Kirill Gamazkov 2014-03-08 00:00.

C ++には、オブジェクトを渡す3つの方法があります。ポインタ、参照、および値です。Javaは後者で制限します(唯一の例外はint、booleanなどのプリミティブ型です)。奇妙なおもちゃのようにではなくC ++を使用したい場合は、これら3つの方法の違いを理解することをお勧めします。

Javaは、「誰がいつこれを破棄すべきか」などの問題はないふりをします。答えは次のとおりです。ガベージコレクター、素晴らしいとひどい。それにもかかわらず、メモリリークに対して100%の保護を提供することはできません(はい、Javaメモリリークする可能性があります)。実際、GCはあなたに誤った安心感を与えます。SUVが大きいほど、避難者への道は長くなります。

C ++を使用すると、オブジェクトのライフサイクル管理を直接行うことができます。それを処理する手段(スマートポインタファミリ、QtのQObjectなど)はありますが、GCのように「ファイアアンドフォーゲット」の方法で使用することはできません。メモリ処理を常に念頭に置く必要があります。オブジェクトの破壊に注意する必要があるだけでなく、同じオブジェクトを複数回破壊しないようにする必要もあります。

まだ怖くない?わかりました:循環参照-それらを自分で処理します、人間。そして覚えておいてください:各オブジェクトを正確に一度だけ殺してください。私たちC ++ランタイムは、死体をいじり、死んだものを放っておく人を嫌います。

では、質問に戻りましょう。

オブジェクトをポインタや参照ではなく値で渡す場合は、オブジェクトをコピーします(数バイトであろうと、巨大なデータベースダンプであろうと、オブジェクト全体です。後者を回避するのに十分賢いのではありません)。 t you?) '='を実行するたびに。また、オブジェクトのメンバーにアクセスするには、「。」を使用します。(ドット)。

オブジェクトをポインタで渡すときは、ほんの数バイト(32ビットシステムでは4バイト、64ビットシステムでは8バイト)、つまりこのオブジェクトのアドレスをコピーします。そして、これをすべての人に示すために、メンバーにアクセスするときにこの派手な「->」演算子を使用します。または、「*」と「。」の組み合わせを使用できます。

参照を使用すると、値のふりをするポインターを取得します。これはポインタですが、「。」を介してメンバーにアクセスします。

そして、もう一度頭を悩ませるために:コンマで区切られたいくつかの変数を宣言するとき、(手を見てください):

  • タイプはすべての人に与えられます
  • 値/ポインタ/参照修飾子は個別です

例:

struct MyStruct
{
    int* someIntPointer, someInt; //here comes the surprise
    MyStruct *somePointer;
    MyStruct &someReference;
};

MyStruct s1; //we allocated an object on stack, not in heap

s1.someInt = 1; //someInt is of type 'int', not 'int*' - value/pointer modifier is individual
s1.someIntPointer = &s1.someInt;
*s1.someIntPointer = 2; //now s1.someInt has value '2'
s1.somePointer = &s1;
s1.someReference = s1; //note there is no '&' operator: reference tries to look like value
s1.somePointer->someInt = 3; //now s1.someInt has value '3'
*(s1.somePointer).someInt = 3; //same as above line
*s1.somePointer->someIntPointer = 4; //now s1.someInt has value '4'

s1.someReference.someInt = 5; //now s1.someInt has value '5'
                              //although someReference is not value, it's members are accessed through '.'

MyStruct s2 = s1; //'NO WAY' the compiler will say. Go define your '=' operator and come back.

//OK, assume we have '=' defined in MyStruct

s2.someInt = 0; //s2.someInt == 0, but s1.someInt is still 5 - it's two completely different objects, not the references to the same one
21
Karthik Kalyanasundaram 2014-03-04 02:00.

C ++では、(Object object;ブロック内のステートメントを使用して)スタックに割り当てられたオブジェクトは、宣言されたスコープ内にのみ存在します。コードのブロックが実行を終了すると、宣言されたオブジェクトは破棄されます。一方、を使用してヒープにメモリを割り当てると、をObject* obj = new Object()呼び出すまでヒープ内に存在し続けますdelete obj

オブジェクトを宣言/割り当てたコードのブロックだけでなく、オブジェクトを使用したい場合は、ヒープ上にオブジェクトを作成します。

20
marcinj 2014-03-04 02:19.

しかし、なぜこのように使用する必要があるのか​​理解できません。

以下を使用すると、関数本体内でどのように機能するかを比較します。

Object myObject;

関数内では、myObjectこの関数が戻ると破壊されます。したがって、これは、関数の外部にオブジェクトが必要ない場合に役立ちます。このオブジェクトは、現在のスレッドスタックに配置されます。

関数本体の内部に書き込む場合:

 Object *myObject = new Object;

その場合myObject、関数が終了し、割り当てがヒープ上にあると、が指すオブジェクトクラスインスタンスは破棄されません。

ここで、Javaプログラマーの場合、2番目の例は、Javaでのオブジェクト割り当ての動作に近いものです。この行:Object *myObject = new Object;はjava :と同等ですObject myObject = new Object();。違いは、JavaではmyObjectがガベージコレクションを取得し、c ++では解放されないため、どこかで明示的に `deletemyObject; 'を呼び出す必要があることです。そうしないと、メモリリークが発生します。

c ++ 11以降new Object、shared_ptr / unique_ptrに値を格納することにより、動的割り当ての安全な方法を使用できます。

std::shared_ptr<std::string> safe_str = make_shared<std::string>("make_shared");

// since c++14
std::unique_ptr<std::string> safe_str = make_unique<std::string>("make_shared"); 

また、オブジェクトはmap-sやvector-sなどのコンテナに保存されることが多く、オブジェクトの存続期間を自動的に管理します。

13
in need of help 2014-03-04 02:05.

技術的にはメモリ割り当ての問題ですが、これにはさらに2つの実用的な側面があります。それは2つのことと関係があります:1)スコープ、ポインタなしでオブジェクトを定義すると、それが定義されたコードブロックの後でオブジェクトにアクセスできなくなりますが、「new」でポインタを定義すると、同じポインタで「削除」を呼び出すまで、このメモリへのポインタがある場所ならどこからでもアクセスできます。2)関数に引数を渡したい場合は、効率を上げるためにポインターまたは参照を渡します。オブジェクトを渡すと、オブジェクトがコピーされます。これが大量のメモリを使用するオブジェクトである場合、CPUを消費する可能性があります(たとえば、データでいっぱいのベクトルをコピーする場合)。ポインターを渡すとき、渡すのは1つのintだけです(実装によって異なりますが、ほとんどは1つのintです)。

それ以外は、「新規」が、ある時点で解放する必要があるヒープにメモリを割り当てることを理解する必要があります。「新規」を使用する必要がない場合は、「スタック上」で通常のオブジェクト定義を使用することをお勧めします。

6
ST3 2014-03-09 00:48.

主な質問は、オブジェクト自体ではなくポインタを使用する必要があるのはなぜですか?そして私の答えは、C ++には参照があるため、(ほとんど)オブジェクトの代わりにポインターを使用しないでください。ポインターよりも安全であり、ポインターと同じパフォーマンスが保証されます。

あなたがあなたの質問で言及したもう一つのこと:

Object *myObject = new Object;

それはどのように機能しますか?Object型のポインタを作成し、1つのオブジェクトに合うようにメモリを割り当て、デフォルトのコンストラクタを呼び出します。しかし、実際にはそれほど良くありません。動的にメモリを割り当てた場合(keywordを使用new)、手動でメモリを解放する必要があります。つまり、コードでは次のようにする必要があります。

delete myObject;

これはデストラクタを呼び出してメモリを解放し、簡単に見えますが、大きなプロジェクトでは、1つのスレッドがメモリを解放したかどうかを検出するのが難しい場合がありますが、そのために共有ポインタを試すことができます。これらはパフォーマンスをわずかに低下させますが、操作ははるかに簡単です。それら。


そして今、いくつかの紹介が終わり、質問に戻ります。

オブジェクトの代わりにポインターを使用して、関数間でデータを転送する際のパフォーマンスを向上させることができます。

見てみましょう。std::stringこれはオブジェクトでもあり、大きなXMLなどの非常に多くのデータが含まれているため、解析する必要がありますが、そのためにはvoid foo(...)、さまざまな方法で宣言できる関数があります。

  1. void foo(std::string xml); この場合、変数から関数スタックにすべてのデータをコピーします。時間がかかるため、パフォーマンスが低下します。
  2. void foo(std::string* xml); この場合、size_t変数を渡すのと同じ速度でオブジェクトにポインタを渡しますが、NULLポインタまたは無効なポインタを渡すことができるため、この宣言にはエラーが発生しやすくなります。C参照がないため、通常はで使用されるポインタ。
  3. void foo(std::string& xml); ここで参照を渡します。基本的にはポインタを渡すのと同じですが、コンパイラはいくつかのことを行い、無効な参照を渡すことはできません(実際には、無効な参照で状況を作成することは可能ですが、コンパイラをだまします)。
  4. void foo(const std::string* xml); これは秒と同じですが、ポインタ値だけを変更することはできません。
  5. void foo(const std::string& xml); これは3番目と同じですが、オブジェクトの値は変更できません。

さらに言及したいのは、これらの5つの方法を使用して、選択した割り当て方法(withnewまたはregular)に関係なくデータを渡すことができるということです。


もう1つ言及すべきことは、通常の方法でオブジェクトを作成する場合、スタックにメモリを割り当てますが、それを使用して作成する場合は、newヒープを割り当てます。スタックを割り当てる方がはるかに高速ですが、データの非常に大きな配列には小さいので、大きなオブジェクトが必要な場合は、スタックオーバーフローが発生する可能性があるため、ヒープを使用する必要がありますが、通常、この問題はSTLコンテナを使用して解決されます。std::stringコンテナでもあります、何人かの人はそれを忘れました:)

5
Quest 2014-03-04 02:02.

あなたが持っているとしましょうclass A含んでいるclass Bあなたはいくつかの関数を呼び出すしたいときにclass B外をclass Aあなたは、単にこのクラスへのポインタを取得し、あなたがやりたいことができ、それはまたの文脈変化するclass B中を、あなたのclass A

ただし、動的オブジェクトには注意してください

5
Rohit 2014-03-04 02:18.

オブジェクトへのポインタを使用することには多くの利点があります-

  1. 効率(すでに指摘したように)。オブジェクトを関数に渡すということは、オブジェクトの新しいコピーを作成することを意味します。
  2. サードパーティライブラリのオブジェクトの操作。オブジェクトがサードパーティのコードに属し、作成者がポインタのみを介してオブジェクトを使用することを意図している場合(コピーコンストラクタなどは不可)、このオブジェクトを渡すことができる唯一の方法はポインタを使用することです。値を渡すと問題が発生する可能性があります。(ディープコピー/シャローコピーの問題)。
  3. オブジェクトがリソースを所有していて、所有権が他のオブジェクトと共有されないようにしたい場合。
4
cmollis 2014-03-08 14:45.

これについては詳細に説明されていますが、Javaではすべてがポインタです。スタックとヒープの割り当て(すべてのオブジェクトはヒープに割り当てられます)を区別しないため、ポインターを使用していることに気づきません。C ++では、メモリ要件に応じて、2つを混在させることができます。パフォーマンスとメモリ使用量は、C ++ではより決定論的です(duh)。

3
Palak Jain 2017-04-16 07:07.
Object *myObject = new Object;

これを行うと、メモリリークを回避するために明示的に削除する必要があるオブジェクト(ヒープ上)への参照が作成されます。

Object myObject;

これを行うと、自動タイプのオブジェクト(myObject)が(スタック上に)作成され、オブジェクト(myObject)がスコープ外になると自動的に削除されます。

2
RioRicoRick 2014-03-05 10:37.

ポインタは、オブジェクトのメモリ位置を直接参照します。Javaにはこのようなものはありません。Javaには、ハッシュテーブルを介してオブジェクトの場所を参照する参照があります。これらの参照を使用して、Javaでのポインタ演算のようなことはできません。

あなたの質問に答えるために、それはあなたの好みです。私はJavaのような構文を使用することを好みます。

0
lasan 2016-06-02 00:08.

ポインタ付き

  • メモリと直接話すことができます。

  • ポインタを操作することにより、プログラムの多くのメモリリークを防ぐことができます。

0
Noname 2017-01-12 10:03.

ポインターを使用する理由の1つは、C関数とのインターフェースです。もう1つの理由は、メモリを節約することです。例:大量のデータを含み、プロセッサを集中的に使用するコピーコンストラクターを持つオブジェクトを関数に渡す代わりに、オブジェクトへのポインターを渡すだけで、特にループ内にいる場合はメモリと速度を節約できます。その場合、Cスタイルの配列を使用しているのでない限り、参照の方が適しています。

0
seccpur 2018-02-19 07:11.

メモリ使用率が非常に重要な領域では、ポインタが便利です。たとえば、再帰ルーチンを使用して数千のノードが生成され、後でそれらを使用してゲーム内の次善の動きを評価するミニマックスアルゴリズムについて考えてみます。割り当て解除またはリセット(スマートポインターの場合のように)機能により、メモリ消費が大幅に削減されます。一方、非ポインタ変数は、再帰呼び出しが値を返すまでスペースを占有し続けます。

0
user18853 2018-03-16 00:25.

ポインタの重要なユースケースを1つ含めます。基本クラスにオブジェクトを格納しているが、それがポリモーフィックである可能性がある場合。

Class Base1 {
};

Class Derived1 : public Base1 {
};


Class Base2 {
  Base *bObj;
  virtual void createMemerObects() = 0;
};

Class Derived2 {
  virtual void createMemerObects() {
    bObj = new Derived1();
  }
};

したがって、この場合、bObjを直接オブジェクトとして宣言することはできず、ポインターが必要です。

0
RollerSimmer 2020-08-21 05:59.

C ++のオブジェクトポインタの主な強みは、同じスーパークラスのポインタのポリモーフィック配列とマップを可能にすることです。たとえば、インコ、ニワトリ、ロビン、ダチョウなどを一連の鳥に入れることができます。

さらに、動的に割り当てられたオブジェクトはより柔軟性があり、HEAPメモリを使用できますが、ローカルに割り当てられたオブジェクトは、静的でない限りSTACKメモリを使用します。特に再帰を使用する場合、スタックに大きなオブジェクトがあると、間違いなくスタックオーバーフローが発生します。

Related questions

MORE COOL STUFF

「水曜日」シーズン1の中心には大きなミステリーがあります

「水曜日」シーズン1の中心には大きなミステリーがあります

Netflixの「水曜日」は、典型的な10代のドラマ以上のものであり、実際、シーズン1にはその中心に大きなミステリーがあります.

ボディーランゲージの専門家は、州訪問中にカミラ・パーカー・ボウルズが輝くことを可能にした微妙なケイト・ミドルトンの動きを指摘しています

ボディーランゲージの専門家は、州訪問中にカミラ・パーカー・ボウルズが輝くことを可能にした微妙なケイト・ミドルトンの動きを指摘しています

ケイト・ミドルトンは、州の夕食会と州の訪問中にカミラ・パーカー・ボウルズからスポットライトを奪いたくなかった、と専門家は言う.

一部のファンがハリー・スタイルズとオリビア・ワイルドの「非常に友好的な」休憩が永続的であることを望んでいる理由

一部のファンがハリー・スタイルズとオリビア・ワイルドの「非常に友好的な」休憩が永続的であることを望んでいる理由

一部のファンが、オリビア・ワイルドが彼女とハリー・スタイルズとの間の「難しい」が「非常に友好的」な分割を恒久的にすることを望んでいる理由を見つけてください.

エリザベス女王の死後、ケイト・ミドルトンはまだ「非常に困難な時期」を過ごしている、と王室の専門家が明らかにする 

エリザベス女王の死後、ケイト・ミドルトンはまだ「非常に困難な時期」を過ごしている、と王室の専門家が明らかにする&nbsp;

エリザベス女王の死後、ケイト・ミドルトンが舞台裏で「非常に困難な時期」を過ごしていたと伝えられている理由を調べてください.

セントヘレナのジェイコブのはしごを登るのは、気弱な人向けではありません

セントヘレナのジェイコブのはしごを登るのは、気弱な人向けではありません

セント ヘレナ島のジェイコブズ ラダーは 699 段の真っ直ぐ上る階段で、頂上に到達すると証明書が発行されるほどの難易度です。

The Secrets of Airline Travel Quiz

The Secrets of Airline Travel Quiz

Air travel is far more than getting from point A to point B safely. How much do you know about the million little details that go into flying on airplanes?

Where in the World Are You? Take our GeoGuesser Quiz

Where in the World Are You? Take our GeoGuesser Quiz

The world is a huge place, yet some GeoGuessr players know locations in mere seconds. Are you one of GeoGuessr's gifted elite? Take our quiz to find out!

バイオニック読書はあなたをより速く読むことができますか?

バイオニック読書はあなたをより速く読むことができますか?

BionicReadingアプリの人気が爆発的に高まっています。しかし、それは本当にあなたを速読術にすることができますか?

Total War:Warhammer:Kotakuレビュー

Total War:Warhammer:Kotakuレビュー

私はこのゲームを嫌う準備ができていました。先週の前に、Total War:Warhammerについての私の考えがありました:それでもここに私は、私の手にある完成品であり、私は変わった男です。

涙の道:軍事化された帝国主義勢力がスタンディングロックキャンプを占領

涙の道:軍事化された帝国主義勢力がスタンディングロックキャンプを占領

スタンディングロックスー族のメンバーと水の保護者は、ノースダコタ州のスタンディングロックにあるオセティサコウィンキャンプを去ります。(Twitter経由のCNNスクリーンショット)火と煙がスカイラインを覆い、スタンディングロックスー族のメンバーと水の保護者が、聖なるものを守りながら建てた家、オセティサコウィン(セブンカウンシルファイアーズ)キャンプから行進し、太鼓を打ち、歌い、祈りました。ダコタアクセスパイプラインとしても知られる「ブラックスネーク」からの土地。

シアーズとKマートはイヴァンカ・トランプの商品を自分たちで取り除いています

シアーズとKマートはイヴァンカ・トランプの商品を自分たちで取り除いています

写真:APシアーズとKマートは、イヴァンカ・トランプのトランプホームアイテムのコレクションも、誰も購入したくないために削除しました。シアーズとKマートの両方の親会社であるシアーズホールディングスは、土曜日のABCニュースへの声明で、彼らが気にかけていると辛抱強く説明しましたトランプラインを売り続けるにはお金を稼ぐことについてあまりにも多く。

ポテトチップスでたった10分でスペインのトルティーヤを作る

ポテトチップスでたった10分でスペインのトルティーヤを作る

伝統的なスペインのトルティーヤは通常、オリーブオイルで柔らかくなるまで調理されたポテトから始まります(30分以上かかる場合があります)が、ケトルで調理されたポテトチップスの助けを借りてわずか10分でテーブルに置くことができます。上のビデオはすべてがバラバラにならないように裏返す方法を含め、レシピ全体を説明しますが、必要なのは4〜5個の卵と3カップのケトルチップスだけです。

ケイト・ミドルトンとウィリアム王子は、彼らが子供たちと行っているスパイをテーマにした活動を共有しています

ケイト・ミドルトンとウィリアム王子は、彼らが子供たちと行っているスパイをテーマにした活動を共有しています

ケイト・ミドルトンとウィリアム王子は、子供向けのパズルの本の序文を書き、ジョージ王子、シャーロット王女、ルイ王子と一緒にテキストを読むと述べた.

事故で押しつぶされたスイカは、動物を喜ばせ水分補給するために野生生物保護団体に寄付されました

事故で押しつぶされたスイカは、動物を喜ばせ水分補給するために野生生物保護団体に寄付されました

Yak's Produce は、数十個のつぶれたメロンを野生動物のリハビリ専門家であるレスリー グリーンと彼女のルイジアナ州の救助施設で暮らす 42 匹の動物に寄付しました。

デミ・ロヴァートは、新しいミュージシャンのボーイフレンドと「幸せで健康的な関係」にあります: ソース

デミ・ロヴァートは、新しいミュージシャンのボーイフレンドと「幸せで健康的な関係」にあります: ソース

8 枚目のスタジオ アルバムのリリースに向けて準備を進めているデミ ロヴァートは、「スーパー グレート ガイ」と付き合っている、と情報筋は PEOPLE に確認しています。

Plathville の Kim と Olivia Plath が数年ぶりに言葉を交わすことへようこそ

Plathville の Kim と Olivia Plath が数年ぶりに言葉を交わすことへようこそ

イーサン プラスの誕生日のお祝いは、TLC のウェルカム トゥ プラスビルのシーズン 4 のフィナーレで、戦争中の母親のキム プラスと妻のオリビア プラスを結びつけました。

仕事の生産性を高める 8 つのシンプルなホーム オフィスのセットアップのアイデア

仕事の生産性を高める 8 つのシンプルなホーム オフィスのセットアップのアイデア

ホームオフィスのセットアップ術を極めよう!AppExert の開発者は、家族全員が一緒にいる場合でも、在宅勤務の技術を習得しています。祖父や曽祖父が共同家族で暮らしていた頃の記憶がよみがえりました。

2022 年、私たちのデジタル ライフはどこで終わり、「リアル ライフ」はどこから始まるのでしょうか?

20 年前のタイムトラベラーでさえ、日常生活におけるデジタルおよびインターネットベースのサービスの重要性に驚くことでしょう。MySpace、eBay、Napster などのプラットフォームは、高速化に焦点を合わせた世界がどのようなものになるかを示してくれました。

ニューロマーケティングの秘密科学

ニューロマーケティングの秘密科学

マーケティング担当者が人間の欲望を操作するために使用する、最先端の (気味が悪いと言う人もいます) メソッドを探ります。カートをいっぱいにして 3 桁の領収書を持って店を出る前に、ほんの数点の商品を買いに行ったことはありませんか? あなたは一人じゃない。

地理情報システムの日: GIS 開発者として学ぶべき最高の技術スタック

地理情報システムの日: GIS 開発者として学ぶべき最高の技術スタック

私たちが住んでいる世界を確実に理解するには、データが必要です。ただし、空間参照がない場合、このデータは地理的コンテキストがないと役に立たなくなる可能性があります。

Language