web-dev-qa-db-ja.com

C ++の参照変数を返すのは悪いことですか?

これは少し主観的なことだと思います。意見が全会一致になるかどうかはわかりません(参照が返されるコードスニペットが多数見られます)。

私が今質問した、参照の初期化に関しての質問 に対するコメントによると、参照を返すことは悪くなる可能性があります。 。

例によって物事を想像しているのでなければ、これは私を心配させています。そして、かなり少数の場所でこれを行っています…私は誤解しましたか?それは悪ですか?もしそうなら、どれだけ悪ですか?

私はC++に不慣れであるという事実と、私のアプリケーションがメモリリーク地獄でなければならないときの完全な混乱とを組み合わせた、ポインタと参照が混在しているために...

また、スマート/共有ポインタを使用することが、メモリリークを回避するための最善の方法として一般的に受け入れられていることを理解しています。

314
Nick Bolton

一般に、参照を返すことは完全に正常であり、常に発生します。

あなたが意味するならば:

int& getInt() {
    int i;
    return i;  // DON'T DO THIS.
}

それはあらゆる種類の悪です。スタックに割り当てられたiは消え、あなたは何も参照していません。これも悪です。

int& getInt() {
    int* i = new int;
    return *i;  // DON'T DO THIS.
}

クライアントは結局奇妙なことをしなければならないからです。

int& myInt = getInt(); // note the &, we cannot lose this reference!
delete &myInt;         // must delete...totally weird and  evil

int oops = getInt(); 
delete &oops; // undefined behavior, we're wrongly deleting a copy, not the original

右辺値参照はまだ参照にすぎないので、すべての邪悪なアプリケーションは同じままです。

関数の範囲を超えて存在するものを割り当てたい場合は、スマートポインタ(または一般的にはコンテナ)を使用してください。

std::unique_ptr<int> getInt() {
    return std::make_unique<int>(0);
}

そして今、クライアントはスマートポインタを格納します。

std::unique_ptr<int> x = getInt();

生涯がより高いレベルで開かれていることがわかっているものにアクセスするための参考文献もまた問題ありません。

struct immutableint {
    immutableint(int i) : i_(i) {}

    const int& get() const { return i_; }
private:
    int i_;
};

ここでは、i_への参照を返しても問題ないことがわかっています。呼び出しているものはすべてクラスインスタンスの有効期間を管理しているので、i_は少なくともそれ以上存続します。

そしてもちろん、ただ問題はありません。

int getInt() {
   return 0;
}

存続期間を呼び出し元に任せる必要がある場合は、単に値を計算するだけです。

要約:オブジェクトの存続期間が呼び出しの後で終わらないならば、参照を返しても大丈夫です。

374
GManNickG

いいえ、いいえ、千回いいえ。

悪いのは、動的に割り当てられたオブジェクトを参照して、元のポインタを失うことです。オブジェクトをnewにすると、保証されたdeleteを持つ義務を負うことになります。

しかし、例を見てみましょう:operator<<:それは必須参照を返す、または

cout << "foo" << "bar" << "bletch" << endl ;

動作しません。

62
Charlie Martin

すぐに消えない既存のオブジェクトへの参照、および所有権の移転を意図していない場所への参照を返す必要があります。

ローカル変数などへの参照は絶対に返さないでください。参照される場所には存在しないからです。

関数から独立したものへの参照を返すことができます。呼び出し元の関数が削除の責任を負うことは期待できません。これは典型的なoperator[]関数の場合です。

何かを作成している場合は、値またはポインタ(通常またはスマート)を返す必要があります。値は呼び出し元の関数の変数または式に入れられるため、自由に値を返すことができます。ローカル変数へのポインタは絶対に返さないでください。消えてしまいます。

44
David Thornley

私は答えが満足のいくものではないと思うので、2セント追加します。

以下のケースを分析しましょう。

誤った使い方

int& getInt()
{
    int x = 4;
    return x;
}

これは明らかにエラーです

int& x = getInt(); // will refer to garbage

静的変数を使った使い方

int& getInt()
{
   static int x = 4;
   return x;
}

静的変数はプログラムの存続期間を通して存在するので、これは正しいです。

int& x = getInt(); // valid reference, x = 4

これは、シングルトンパターンを実装するときにも非常に一般的です。

Class Singleton
{
    public:
        static Singleton& instance()
        {
            static Singleton instance;
            return instance;
        };

        void printHello()
        {
             printf("Hello");
        };

}

使用法:

 Singleton& my_sing = Singleton::instance(); // Valid Singleton instance
 my_sing.printHello();  // "Hello"

オペレータ

標準ライブラリコンテナは、参照を返す演算子の使い方に大きく依存します。

T & operator*();

以下で使用することができます

std::vector<int> x = {1, 2, 3}; // create vector with 3 elements
std::vector<int>::iterator iter = x.begin(); // iterator points to first element (1)
*iter = 2; // modify first element, x = {2, 2, 3} now

内部データへの素早いアクセス

内部データにすばやくアクセスするために&を使用することがあります

Class Container
{
    private:
        std::vector<int> m_data;

    public:
        std::vector<int>& data()
        {
             return m_data;
        }
}

使用法で:

Container cont;
cont.data().Push_back(1); // appends element to std::vector<int>
cont.data()[0] // 1

しかし、これはこのような落とし穴につながる可能性があります。

Container* cont = new Container;
std::vector<int>& cont_data = cont->data();
cont_data.Push_back(1);
delete cont; // This is bad, because we still have a dangling reference to its internal data!
cont_data[0]; // dangling reference!
15
thorhunter

悪ではありません。 C++の多くのものと同様に、正しく使用しても問題ありませんが、使用する際に注意が必要な落とし穴がたくさんあります(ローカル変数への参照を返すなど)。

それによって達成できる良いことがあります(map [name] = "hello world"のように)

14
Mehrdad Afshari

「参照を返すことは悪いことです。なぜなら、(私が理解しているように)それを削除するのを見逃しやすくするためです。」

違います。参照を返すことは、所有権のセマンティクスを意味しません。それは、あなたがこれをするという理由だけで、です:

Value& v = thing->getTheValue();

... vで参照されるメモリを所有しているわけではありません。

しかし、これは恐ろしいコードです。

int& getTheValue()
{
   return *new int;
}

このようなことをしているのであれば "そのインスタンスにはポインタは必要ありません" then:1)参照が必要な場合はポインタを間接参照する、2)最終的にはポインタが必要になるなぜなら、newとdeleteを一致させる必要があり、deleteを呼び出すためのポインタが必要だからです。

10
John Dibling

2つの場合があります。

  • const reference - 時には、特にヘビーオブジェクトやプロキシクラスでは、コンパイラの最適化のための良いアイデア

  • non-const reference - バッドアイデアは、時々、カプセル化を破る

両方とも同じ問題を共有しています - 破壊されたオブジェクトを指す可能性があります...

参照/ポインタを返す必要がある多くの状況では、スマートポインタを使用することをお勧めします。

また、次の点にも注意してください。

正式な規則があります - C++標準(興味があればセクション13.3.3.1.4)は、一時的なものはconst参照のみに束縛できると述べています - もしあなたが非const参照を使おうとするなら、コンパイラこれをエラーとして報告する必要があります。

7
Sasha

それは悪ではないだけでなく、時には不可欠です。たとえば、参照戻り値を使用せずにstd :: vectorの[]演算子を実装することは不可能です。

4
anon

値を返すにはコピー操作が必要であるため、戻り参照は通常、C++でのラージオブジェクトの演算子のオーバーロードで使用されます(ペレータのオーバーロードでは、戻り値としてポインタを使用しません)

しかし、戻り参照はメモリ割り当ての問題を引き起こすかもしれません。結果への参照は戻り値への参照として関数から渡されるため、戻り値を自動変数にすることはできません。

参照を返すことを使用したい場合は、静的オブジェクトのバッファを使用できます。例えば

const max_tmp=5; 
Obj& get_tmp()
{
 static int buf=0;
 static Obj Buf[max_tmp];
  if(buf==max_tmp) buf=0;
  return Buf[buf++];
}
Obj& operator+(const Obj& o1, const Obj& o1)
{
 Obj& res=get_tmp();
 // +operation
  return res;
 }

このようにして、安全に戻り参照を使用できます。

しかし、functiongで値を返すためには、参照の代わりに常にポインタを使うことができます。

1
MinandLucy

受け入れられた答えへの追加:

struct immutableint {
    immutableint(int i) : i_(i) {}

    const int& get() const { return i_; }
private:
    int i_;
};

この例は大丈夫ではありませんであり、可能であれば避けるべきであると私は主張します。どうして? ぶら下がり参照で終わるのは非常に簡単です。

例でポイントを説明するには

struct Foo
{
    Foo(int i = 42) : boo_(i) {}
    immutableint boo()
    {
        return boo_;
    }  
private:
    immutableint boo_;
};

危険ゾーンに入る:

Foo foo;
const int& dangling = foo.boo().get(); // dangling reference!
1
AMA

最善のことは、オブジェクトを作成し、それを参照/ポインタパラメータとしてこの変数を割り当てる関数に渡すことです。

関数内でオブジェクトを割り当て、それを参照またはポインタとして返すこと(ポインタの方が安全です)は、関数ブロックの最後でメモリを解放するため、よくありません。

0
Drezir

関数の戻り値として参照を使用する方が、関数の戻り値としてポインタを使用するよりもはるかに簡単です。次に、戻り値が参照する静的変数を使用するのが常に安全です。

0
Tony