ポインターとリファレンス


256

機能する元の変数を関数に与える場合のより良い実践とは:

unsigned long x = 4;

void func1(unsigned long& val) {
     val = 5;            
}
func1(x);

または:

void func2(unsigned long* val) {
     *val = 5;
}
func2(&x);

IOW:別のものを選ぶ理由はありますか?


1
参照はもちろん貴重ですが、私はポインタがどこにでもあるCから来ています。参照の価値を理解するには、最初にポインタに習熟する必要があります。
Jay D

これは、関数型プログラミングからの参照透過性などの目標にどのように適合しますか?関数が常に新しいオブジェクトを返し、特に関数に渡される変数の状態を内部で変更しないようにするにはどうすればよいでしょうか。この概念がC ++のような言語のポインターと参照で引き続き使用される方法はありますか?(注意:誰かが参照の透明性の目標をすでに持っていると想定しています。それが適切な目標であるかどうかについて話すことに興味はありません。)
ely

参照を優先します。選択肢がない場合のユーザーポインタ。
Ferruccio

回答:


285

私の経験則は:

それらを使用してポインタ演算を実行する場合(たとえば、ポインタアドレスをインクリメントして配列をステップスルーする場合)、またはNULLポインタを渡す必要がある場合は、ポインタを使用します。

それ以外の場合は参照を使用します。


10
ポインターがNULLであるという優れた点。ポインター・パラメーターがある場合は、それがNULLでないことを明示的に確認するか、関数のすべての使用法を検索して、決してNULLにならないようにする必要があります。この作業は参照には必要ありません。
Richard Corden

26
算術とはどういう意味か説明してください。新しいユーザーは、ポインタが指しているものを調整する必要があることを理解していない可能性があります。
マーティンヨーク

7
マーティン、算術では、構造体へのポインタを渡しますが、それは単純な構造体ではなく、その配列であることを知っています。この場合、[]を使用してインデックスを付けるか、ポインタに++ /-を使用して算術を実行できます。それが一言で言えば違いです。
Nils Pipenbrinck 2008年

2
マーティン、これは直接ポインタでのみ実行できます。参照なし。確かに、あなたは参照へのポインタを取り、実際には同じことを行うが、あなたがしなければ、あなたは非常に汚いコードで終了するように、することができます。..
ニルスPipenbrinck

1
ポリモーフィズムはどうですか(例Base* b = new Derived())?これは、ポインターなしでは処理できないケースのようです。
Chris Redford

72

次の関数呼び出しのコーディングガイドラインを確立することでメリットが得られると思います。

  1. 他のすべての場所と同様に、常に- const正しいこと。

    • 注:これは、特に、アウト値(項目3を参照)と値によって渡された値(項目4を参照)のみがconst指定子を欠くことを意味します。
  2. 値0 / NULLが現在のコンテキストで有効な入力である場合にのみ、ポインターによって値を渡します。

    • 理論的根拠1:呼び出し元として渡したものはすべて使用可能な状態でなければならないことがわかります。

    • 理由2:としては、と呼ばれるあなたが来るものは何でもそれは知っている、ある使用可能な状態に。したがって、その値に対してNULLチェックやエラー処理を行う必要はありません。

    • 根拠3:根拠1と2はコンパイラーによって強制されます。可能であれば、コンパイル時に常にエラーをキャッチします。

  3. 関数の引数がout-valueの場合は、参照によって渡します。

    • 理論的根拠:項目2を壊したくない...
  4. 値がPOD(Plain old Datastructure)、または十分に小さい(メモリの観点から)、または他の方法で十分に安価(時間の観点から)でコピーできる場合にのみ、「pass by const reference」より「pass by value」を選択します。

    • 理論的根拠:不要なコピーを避けます。
    • 注:十分に小さく、十分安くは、絶対測定可能ではありません。

ガイドラインが欠けている場合:... "いつconst&を使用するか" ...ガイドライン2は、「[in]値の場合、NULLが有効な場合にのみポインターで渡す必要があります。それ以外の場合は、const参照(または "小さな」オブジェクト、コピー)、または参照潜在的に+1を追加するために、私はこのポストを監視しています[出]の値である場合。。
paercebal

アイテム1は、あなたが説明するケースをカバーします。
ヨハンゲレル

デフォルトで構成できない場合、参照によって出力パラメーターを渡すのは少し難しいです。それは私のコードではかなり一般的です-関数がそのアウトオブジェクトを作成する理由は、それが重要なためです。
MSalters 2008

@MSalters:関数内にメモリを割り当てる場合(つまり、これが私が意味するところだと思う)、割り当てられたメモリへのポインタを返さないのはなぜですか?
クライスト、2011

@Kleist:@MSaltersに代わって、多くの理由が考えられます。1つは、プリサイズ済みのように、埋めるためにすでにメモリを割り当てている可能性があることですstd::vector<>
Johann Gerell、2011

24

これは最終的には主観的です。これまでの議論は有用ですが、これに対する正しいまたは決定的な答えはないと思います。多くは、スタイルガイドラインとそのときのニーズによって異なります。

ポインターにはいくつかの異なる機能(何かがNULLであるかどうかにかかわらず)がありますが、出力パラメーターの実際的な最大の違いは純粋に構文です。たとえば、GoogleのC ++スタイルガイド(https://google.github.io/styleguide/cppguide.html#Reference_Arguments)では、出力パラメーターのポインターのみが要求され、constの参照のみが許可されます。推論は読みやすさの1つです。値の構文を持つものは、ポインタの意味論的な意味を持つべきではありません。これが必ずしも正しいか間違っているかを示唆しているわけではありませんが、ここでのポイントは、それがスタイルの問題であり、正確さの問題ではないということです。


参照には値の構文がありますが、ポインタの意味論的な意味は何ですか?
エリックアンドリュールイス

"参照渡し"の部分は関数定義(値の構文)からのみ明らかであるため、コピーを渡しているように見えますが、渡す値をコピーするのではなく、基本的にはポインターをフードの下に渡します。これにより、値を変更する関数。
phant0m 2016年

Google C ++スタイルガイドが大嫌いであることを忘れてはなりません。
Deduplicator

7

変数の値を変更する場合は、ポインターを渡す必要があります。技術的に参照またはポインターを渡すことは同じですが、ユースケースでポインターを渡すと、関数によって値が変更されることを「アドバタイズ」するため、より読みやすくなります。


2
Johann Gerellのガイドラインに従っている場合、非const参照も変更可能な変数を通知するため、ここではポインターにその利点はありません。
Alexander Kondratskiy

4
@AlexanderKondratskiy:あなたがポイントを逃している...あなたはすぐに見ることができない呼び出しサイトで呼び出される関数のようにPARAMTERを受け入れるかどうかconstまたは非const参照が、パラメータのは、ALA渡されたかどうかを確認することができます&xx、および使用をパラメータが変更される可能性があるかどうかをエンコードするための変換。(とconstはいえ、ポインターを渡したい場合があるので、コンテンションはヒントにすぎません。何かが変更されないかもしれないという疑いは、それがいつになるかを考えるよりも危険ではありません。 ....)
トニー・デルロイ

5

値がないことを示す必要がある可能性のあるパラメーターがある場合、パラメーターをポインター値にしてNULLで渡すのが一般的な方法です。

(安全性の観点から)ほとんどの場合のより良い解決策は、boost :: optionalを使用することです。これにより、参照によって、および戻り値としてオプションの値を渡すことができます。

// Sample method using optional as input parameter
void PrintOptional(const boost::optional<std::string>& optional_str)
{
    if (optional_str)
    {
       cout << *optional_str << std::endl;
    }
    else
    {
       cout << "(no string)" << std::endl;
    }
}

// Sample method using optional as return value
boost::optional<int> ReturnOptional(bool return_nothing)
{
    if (return_nothing)
    {
       return boost::optional<int>();
    }

    return boost::optional<int>(42);
}


4

ポインタ

  • ポインタは、メモリアドレスを保持する変数です。
  • ポインター宣言は、基本型、*、および変数名で構成されます。
  • ポインターは、存続期間中の任意の数の変数を指すことができます
  • 現在有効なメモリ位置を指し示していないポインタには、値null(ゼロ)が与えられます

    BaseType* ptrBaseType;
    BaseType objBaseType;
    ptrBaseType = &objBaseType;
  • &は、そのオペランドのメモリアドレスを返す単項演算子です。

  • 逆参照演算子(*)は、ポインターが指す変数に格納されている値にアクセスするために使用されます。

       int nVar = 7;
       int* ptrVar = &nVar;
       int nVar2 = *ptrVar;

参照

  • 参照(&)は、既存の変数のエイリアスのようなものです。

  • 参照(&)は、自動的に逆参照される定数ポインターのようなものです。

  • 通常、関数の引数リストと関数の戻り値に使用されます。

  • 参照は、作成時に初期化する必要があります。

  • 参照がオブジェクトに初期化されると、別のオブジェクトを参照するように変更することはできません。

  • NULL参照を含めることはできません。

  • const参照はconst intを参照できます。これはconstの値を持つ一時変数で行われます

    int i = 3;    //integer declaration
    int * pi = &i;    //pi points to the integer i
    int& ri = i;    //ri is refers to integer i – creation of reference and initialization

ここに画像の説明を入力してください

ここに画像の説明を入力してください


3

参照は暗黙のポインタです。基本的に、参照が指す値を変更できますが、参照を他の何かを指すように変更することはできません。したがって、私の2セントは、パラメーターの値を変更するだけの場合は参照として渡しますが、別のオブジェクトを指すようにパラメーターを変更する必要がある場合は、ポインターを使用して渡します。


3

C#のoutキーワードを検討してください。コンパイラーは、メソッドの呼び出し元がout引数にoutキーワードを適用する必要があります。これは読みやすさを向上させることを目的としています。最近のIDEでは、これは構文(またはセマンティック)強調表示の仕事だと思いがちです。


typo:セマンティックではなく、セマンティック。+1書き出す代わりに強調表示する可能性について同意します(C#)、または&(Cの場合は参照なし)
peenut

2

渡す内容を変更/保持したい理由がない限り、const参照で渡します。

これは、ほとんどの場合、最も効率的な方法です。

変更したくない各パラメーターには必ずconstを使用してください。これは、関数で愚かなことを行うのを防ぐだけでなく、渡された値に対して関数が何を行うかを他のユーザーに示すのに役立ちます。これは、あなたが指しているものを変更したいだけのときにポインタをconstにすることを含みます...


2

ポインタ:

  • 割り当てることができますnullptr(またはNULL)。
  • 呼び出しサイトで&は、型がポインタ自体ではない場合に使用し、オブジェクトを明示的に変更する必要があります。
  • ポインターはリバウンドできます。

参照:

  • nullにすることはできません。
  • バインドすると、変更できません。
  • 呼び出し元は明示的にを使用する必要はありません&。パラメータが変更されているかどうかを確認するには、関数の実装に移動する必要があるため、これは時々悪いと考えられます。

知らない人に小さな点:nullptrまたはNULLは単に0ですstackoverflow.com/questions/462165/...
セルゲイ・フェドロフ

2
nullptrは0と同じではありません。inta = nullptrを試してください。stackoverflow.com/questions/1282295/what-exactly-is-nullptr
Johan Lundberg、2015

0

参照は、参照によって参照される値にアクセスするためにプレフィックス∗を使用する必要がないことを除いて、ポインターに似ています。また、初期化後に別のオブジェクトを参照することはできません。

参照は、関数の引数を指定するのに特に役立ちます。

詳細については、「Bjarne Stroustrup」(2014)の「C ++のツアー」ページ11〜12を参照してください。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.