2011-09-29 5 views
8

私はこの質問は最高の私は書いた小さなコードスニペットを問われると思う:オブジェクトを参照渡しするとC++で何が起こり、範囲外になりますか?

#include <iostream> 

using namespace std; 

class BasicClass 
{ 
public: 
    BasicClass() 
    { 
    } 
    void print() 
    { 
     cout << "I'm printing" << endl; 
    } 
}; 

class FriendlyClass 
{ 
public: 
    FriendlyClass(BasicClass& myFriend) : 
     _myFriend(myFriend) 
    { 
    } 
    void printFriend() 
    { 
     cout << "Printing my friend: "; 
     _myFriend.print(); 
    } 
private: 
    BasicClass& _myFriend; 
}; 

int main(int argv, char** argc) 
{ 
    FriendlyClass* fc; 
    { 
     BasicClass bc; 
     fc = new FriendlyClass(bc); 
     fc->printFriend(); 
    } 
    fc->printFriend(); 
    delete fc; 
    return 0; 
} 

をコードはコンパイルおよびg ++を使用して正常に動作:

$ g++ test.cc -o test 
$ ./test 
Printing my friend: I'm printing 
Printing my friend: I'm printing 

しかし、これは私がした動作ではありません期待している。 fc->printFriend()への2回目の呼び出しで何らかの失敗が予想されていました。参照渡し/格納がどのように正しく動作しないのか、小規模でしか機能しないようなもので、より洗練されたアプリケーションで爆発する可能性があるという私の理解はありますか?

+3

私たちは皆、「未定義の振る舞い」を叫ぶと、あなたはそれを愛しませんか?斉唱中? 8v) –

+0

"私は2番目に何らかの失敗が予想されていました"。単純にprint()を仮想的にするか、印刷しているテキストをstd :: stringメンバ変数として格納すれば、失敗に終わります。 – SigTerm

+1

一般的にC++のデータメンバーとしての参照は不要です。あなたはおそらくJavaの背景から来ています。 C++のリファレンスは、Javaリファレンスのようなものではありません。 Javaリファレンスのようなものが必要な場合は、 'std :: shared_ptr 'を使ってヒープ上にオブジェクトを作成してください。 – fredoverflow

答えて

11

これはポインタとまったく同じです:もう存在しないオブジェクトを参照するもの(ポインタ/参照)を使用すると、未定義の動作になります。それは動作するように見えるかもしれませんが、いつでも破損する可能性があります。

警告:このようなメソッド呼び出しは、情報目的のためにいくつかの場面で動作するように見える理由を簡単に説明します。実際のコードを書くとき、あなたは唯一の標準は、あなたが観察している行動については

を言うことに依存している必要があります。上のほとんど(すべて?)メソッドの呼び出しは、関数が参照隠さthisパラメータを指定して呼び出すように実装されているコンパイラメソッドが動作するクラスのインスタンス。しかし、あなたのケースでは、thisポインタがまったく使用されていません(関数内のコードはフィールドを参照しておらず、仮想ディスパッチもありません)ので、thisポインタは使用されず、呼び出しは成功します。

メモリがまだ再利用されていないためにスコープ外のオブジェクトを参照していても、デストラクタがすでに実行されているため、オブジェクトが参照される可能性があります。矛盾した状態)。

また、この情報に頼るべきではありません。その呼び出しがなぜ機能するのかを伝えるだけです。

1

未定義の動作。定義上、そのコードが実行されたときに何が起こるかを前提にすることはできません。コンパイラはまだbcが存在するメモリをクリアしていないかもしれませんが、それにはカウントできません。

私は実際に仕事場のプログラムで同じバグを修正しました。インテルのコンパイラを使用すると、スコープ外になった変数はまだ "クリーンアップ"されていないので、メモリはまだ "有効"でした(ただし、動作は未定義です)。 Microsoftのコンパイラは、しかし、より積極的にそれをきれいにし、バグは明らかだった。

12

オブジェクトの存続期間が終了した参照を格納すると、そのオブジェクトにアクセスすることは未定義の動作です。何かが起こり、うまくいく、失敗する、クラッシュする、そして現れるように私はピザを注文することができると言うのが好きです。

+1

ピザを支払うことはできますか? ;) –

+1

@ R.MartinhoFernandes:これは標準に準拠した動作です! –

+1

@ R。 Martinho Fernandes:そうですが、そうする必要はありません。あなたはその未定義の動作以来、それを中継することはできません。 –

1

参照がありません。結果として未定義の動作が発生します。

1
  1. こちらをご覧:Can a local variable's memory be accessed outside its scope?

  2. あなたは何の仮想関数を持っていないので、それはほとんど、動作するだろう、とあなたはBasicClassのフィールドをアクセスもしていない:あなたが呼び出すすべてのメソッドは、バインディング、静的持っており、「この'は使用されません。 実際には「割り当てられていないメモリ」にアクセスすることは決してありません。

関連する問題