83

C++ 参照には、次の 2 つのプロパティがあります。

  • それらは常に同じオブジェクトを指します。
  • 0 にすることはできません。

ポインタは反対です:

  • それらは異なるオブジェクトを指すことができます。
  • それらは 0 になる可能性があります。

C++に「null不可で再配置可能な参照またはポインタ」がないのはなぜですか? 参照が再配置可能であってはならない正当な理由が思いつきません。

編集: 「関連付け」(ここでは「参照」または「ポインター」という言葉を避けている)が決して無効ではないことを確認したいときに参照を使用するため、この質問は頻繁に発生します。

「この参照が常に同じオブジェクトを参照するのは素晴らしい」と思ったことはないと思います。参照が再配置可能な場合、次のような現在の動作を取得できます。

int i = 3;
int& const j = i;

これはすでに正当な C++ ですが、意味がありません。

私は自分の質問を次のよう言い直します。

乾杯、フェリックス

4

17 に答える 17

112

C++ で参照を再バインドできない理由は、Stroustrup の「Design and Evolution of C++」に記載されています。

初期化後に参照が参照するものを変更することはできません。つまり、C++ 参照が初期化されると、後で別のオブジェクトを参照することはできません。再バインドすることはできません。私は過去に Algol68 参照に悩まされていました。ここでは、参照されるオブジェクトに代入するか、新しい参照値を(re-binding ) に代入することがr1=r2できます。C++ でこのような問題を回避したかったのです。r1r1r1r2

于 2009-04-08T02:16:42.547 に答える
32

C++ では、「参照はオブジェクトである」とよく言われます。ソース コードのコンパイル時に参照はポインターとして処理されますが、参照は、関数が呼び出されたときにコピーされないオブジェクトを示すことを目的としています。参照は直接アドレス指定できないため (たとえば、参照にはアドレスがなく、 & はオブジェクトのアドレスを返します)、それらを再割り当てする意味はありません。さらに、C++ には既にポインターがあり、リセットのセマンティクスを処理します。

于 2009-04-08T02:05:04.210 に答える
19

その場合、0にすることのできない再封可能タイプがないためです。ただし、3つのタイプの参照/ポインターを含めた場合を除きます。どちらが言語を複雑にするだけで、ほとんど利益が得られません(そして、4番目のタイプも追加しませんか?0になる可能性のある再装着不可能な参照?)

より良い質問は、なぜ参照を再封可能にしたいのかということかもしれません。もしそうなら、それは多くの状況でそれらをあまり役に立たないでしょう。コンパイラがエイリアス解析を行うのが難しくなります。

JavaまたはC#での参照が再封可能である主な理由は、それらがポインターの働きをするためであるように思われます。それらはオブジェクトを指します。これらはオブジェクトのエイリアスではありません。

次の効果はどうあるべきですか?

int i = 42;
int& j = i;
j = 43;

今日のC++では、再設定不可能な参照があるため、単純です。jはiのエイリアスであり、iの値は43になります。

参照が再封可能であった場合、3行目は参照jを別の値にバインドします。エイリアスはiではなくなり、代わりに整数リテラル43(もちろん無効です)になります。または、おそらくより単純な(または少なくとも構文的に有効な)例:

int i = 42;
int k = 43;
int& j = i;
j = k;

再封可能な参照付き。このコードを評価した後、jはkを指します。C ++の再装着不可能な参照では、jは引き続きiを指し、iには値43が割り当てられます。

参照を再封可能にすることで、言語のセマンティクスが変更されます。参照を別の変数のエイリアスにすることはできなくなりました。代わりに、独自の代入演算子を使用して、別のタイプの値になります。そして、参照の最も一般的な使用法の1つは不可能です。そして、それと引き換えに何も得られないでしょう。新しく得られた参照機能は、ポインターの形ですでに存在していました。したがって、同じことを行うには2つの方法があり、現在のC++言語の参照が行うことを行う方法はありません。

于 2009-04-08T02:34:14.157 に答える
5

参照はポインターではなく、バックグラウンドでポインターとして実装される場合がありますが、そのコアコンセプトはポインターと同等ではありません。*is*参照は、それが参照しているオブジェクトのように見る必要があります。したがって、変更することはできず、NULLにすることもできません。

ポインタは、メモリアドレスを保持する単なる変数です。 ポインタ自体には独自のメモリアドレスがあり、そのメモリアドレス内には、ポインタが指すと言われている別のメモリアドレスがあります。 参照は同じではなく、独自のアドレスを持たないため、別のアドレスを「保持」するように変更することはできません。

参考文献に関するパラシフトC++FAQは、それを最もよく言っていると思います。

重要な注意:参照は、基になるアセンブリ言語のアドレスを使用して実装されることがよくありますが、参照をオブジェクトへの見栄えの悪いポインタとは考えないでください。参照はオブジェクトです。オブジェクトへのポインタでも、オブジェクトのコピーでもありません。オブジェクトです。

そして再びFAQ8.5で:

ポインタとは異なり、参照がオブジェクトにバインドされると、別のオブジェクトに「再配置」することはできません。参照自体はオブジェクトではありません(IDはありません。参照のアドレスを取得すると、参照先のアドレスが得られます。覚えておいてください:参照はその指示対象です)。

于 2009-04-08T02:35:25.823 に答える
5

再装着可能な参照は、機能的にはポインターと同じです。

null 可能性について: このような「再配置可能な参照」がコンパイル時に非 NULL であることを保証することはできないため、そのようなテストは実行時に行う必要があります。これは、初期化または NULL が割り当てられたときに例外をスローするスマート ポインター スタイルのクラス テンプレートを作成することで、自分で実現できます。

struct null_pointer_exception { ... };

template<typename T>
struct non_null_pointer {
    // No default ctor as it could only sensibly produce a NULL pointer
    non_null_pointer(T* p) : _p(p) { die_if_null(); }
    non_null_pointer(non_null_pointer const& nnp) : _p(nnp._p) {}
    non_null_pointer& operator=(T* p) { _p = p; die_if_null(); }
    non_null_pointer& operator=(non_null_pointer const& nnp) { _p = nnp._p; }

    T& operator*() { return *_p; }
    T const& operator*() const { return *_p; }
    T* operator->() { return _p; }

    // Allow implicit conversion to T* for convenience
    operator T*() const { return _p; }

    // You also need to implement operators for +, -, +=, -=, ++, --

private:
    T* _p;
    void die_if_null() const {
        if (!_p) { throw null_pointer_exception(); }
    }
};

これは場合によっては便利です。non_null_pointer<int>パラメータを取る関数は、関数を取るよりも多くの情報を呼び出し元に確実に伝えますint*

于 2009-04-08T02:50:54.303 に答える
4

C++ 参照を「エイリアス」と名付けたほうが混乱しにくいのではないでしょうか? 他の人が述べたように、C++ での参照は、変数へのポインター/参照としてではなく、参照する変数として見なされるべきです。そのため、リセット可能にする正当な理由が思いつきません

ポインターを扱う場合、多くの場合、値として null を許可することが理にかなっています (そうでない場合は、代わりに参照が必要になる場合があります)。特にnullの保持を禁止したい場合は、いつでも独自のスマートポインタータイプをコーディングできます;)

于 2009-04-08T02:16:17.867 に答える
2

一部のコンパイラでは、 C++ 参照が強制的に0になることがあります(そうするのは悪い考えです*。標準に違反します*)。

int &x = *((int*)0); // Illegal but some compilers accept it

編集:私よりも標準をよく知っているさまざまな人々によると、上記のコードは「未定義の動作」を生成します。GCC と Visual Studio の少なくとも一部のバージョンでは、これが期待どおりの動作をするのを見てきました。ポインタを NULL に設定するのと同じです (アクセスすると NULL ポインタ例外が発生します)。

于 2009-04-08T02:04:28.067 に答える
1

あなたはこれを行うことはできません:

int theInt = 0;
int& refToTheInt = theInt;

int otherInt = 42;
refToTheInt = otherInt;

... secondInt と firstInt がここで同じ値を持たないのと同じ理由で:

int firstInt = 1;
int secondInt = 2;
secondInt = firstInt;
firstInt = 3;

assert( firstInt != secondInt );
于 2009-04-08T02:55:39.893 に答える
1

これは実際には答えではありませんが、この制限の回避策です。

基本的に、参照を「再バインド」しようとすると、実際には同じ名前を使用して、次のコンテキストで新しい値を参照しようとしています。C++ では、ブロック スコープを導入することでこれを実現できます。

ジャルフの例では

int i = 42;
int k = 43;
int& j = i;
//change i, or change j?
j = k;

i を変更したい場合は、上記のように記述します。jただし、 の意味を meanに変更したい場合は、次のようkにすることができます。

int i = 42;
int k = 43;
int& j = i;
//change i, or change j?
//change j!
{
    int& j = k;
    //do what ever with j's new meaning
}
于 2013-05-30T00:30:50.487 に答える
0

C ++の参照がnull許容ではないという事実は、それらが単なるエイリアスであるという副作用です。

于 2009-04-08T22:36:35.250 に答える
0

私は受け入れられた答えに同意します。しかし、恒常性のために、それらはポインタのように動作します。

struct A{
    int y;
    int& x;
     A():y(0),x(y){}
};

int main(){
  A a;
  const A& ar=a;
  ar.x++;
}

動作します。見る

const参照によって渡されたクラスの参照メンバーの動作の設計上の理由

于 2012-07-31T13:44:17.027 に答える
0

時々物事は再ポイント可能であるべきではないからです。(たとえば、シングルトンへの参照。)

関数では、引数をnullにできないことを知っているのは素晴らしいことです。

しかし、ほとんどの場合、実際にはポインタであるが、ローカル値オブジェクトのように機能するものを使用できるためです。C ++は、Stroustrupを引用して、クラスインスタンスを「intsdとして実行」するように努力します。intはマシンレジスタに収まるため、intをvaueで渡すのは安価です。多くの場合、クラスはintよりも大きく、値で渡すとかなりのオーバーヘッドが発生します。

値オブジェクトに「似ている」ポインタ(多くの場合、intのサイズまたは2つのintのサイズ)を渡すことができるため、間接参照の「実装の詳細」なしで、よりクリーンなコードを記述できます。また、演算子のオーバーロードに加えて、intで使用される構文と同様の構文を使用してクラスを記述できます。特に、intなどのプリミティブとクラス(複素数クラスなど)に等しく適用できる構文を使用してテンプレートクラスを作成できます。

また、特に演算子のオーバーロードでは、オブジェクトを返す必要がある場所がありますが、ここでも、ポインタを返す方がはるかに安価です。繰り返しになりますが、参照を返すことは私たちの「アウト」です。

そして、ポインタは難しいです。あなたにとってではなく、多分、そしてポインタが単なるメモリアドレスの値であることに気付いた人にとってはそうではありません。しかし、私のCS 101クラスを思い出して、彼らは多くの生徒をつまずかせました。

char* p = s; *p = *s; *p++ = *s++; i = ++*p;

混乱する可能性があります。

ちなみに、Cの40年後、ポインタ宣言が次のようになるべきかどうかについて、人々はまだ同意することさえできません。

char* p;

また

char *p;
于 2009-04-08T02:21:48.320 に答える
0

最適化に関連していると思います。

変数が意味するメモリのビットを明確に知ることができる場合、静的最適化ははるかに簡単です。ポインターはこの条件を破り、再設定可能な参照も破ります。

于 2009-04-08T02:05:59.967 に答える
0

私はいつも、なぜ彼らが参照代入演算子 (:= など) を作成しなかったのか疑問に思っていました。

誰かの神経質になるために、構造内の参照のターゲットを変更するコードを書きました。

いいえ、私のトリックを繰り返すことはお勧めしません。十分に異なるアーキテクチャに移植すると壊れます。

于 2009-04-08T03:01:02.737 に答える
0

半分真剣に: IMHO はポインタと少し違うものにします ;) 次のように書けることがわかります:

MyClass & c = *new MyClass();

後で書くこともできれば:

c = *new MyClass("other")

ポインターと一緒に参照を持つことは理にかなっていますか?

MyClass * a =  new MyClass();
MyClass & b = *new MyClass();
a =  new MyClass("other");
b = *new MyClass("another");
于 2009-04-08T11:02:09.257 に答える