2

学校向けのプロジェクトに取り組んでいます。自動販売機でソーダを買う学生をシミュレートします。クラス Student のメンバーである Card というクラスがあります。あれは、

すべての生徒がカードを持っていますが、これは理にかなっています。

class Student {
public:
    Student( Office &cardOffice );
    ~Student();
    bool action();
    private:
    Office* studentOffice;          // stores cardoffice.
    Card* card;                 // stores card
};

学生証は、studentOffice.create() 関数の呼び出しによって作成されます。その関数はカードを返します。

Card* Office::create( int id, int money ) {
    Card* card = new Card();
    card->id = id;
    card->amount = money;
    return card;
}

学生は、VendingMachine クラスの action() という関数を呼び出して、食べ物を購入します。VendingMachine の buy 関数は、VendingMachine クラスの Status 列挙から型列挙を返します。

0 から 9 までの乱数を生成する prng があります。課題では、10 分の 1 の確率で学生のカードが破棄されることが示されています。そして、次に Student.action() が呼び出されたときに新しいものを取得します。

VendingMachine::Status VendingMachine::buy(Card* &card)
{
    if(prng(9) == 0) // generates number from 0-9
    {
        delete card;
    }
    return status;
}

もともと、学生の action() ルーチンをチェックインして、カードが NULL (削除された場合) かどうかを確認し、それが発生した場合は新しいカードを作成することを考えていました。ただし、コードがカードの削除部分に到達することはわかっていますが、カードがNULLであることを確認すると失敗します。つまり、カードが null ではないということです。つまり、削除が機能しなかったということです。

しかし、渡されたカードのタイプが

Card* &card

次に、学生がこのルーチンを呼び出したものであり、「this」がそれを呼び出したオブジェクトを指すことを知っているので、「this」ポインターを使用して呼び出しを使用することを考えていました。

メンバー関数が呼び出されるオブジェクトを指します。http://msdn.microsoft.com/en-us/library/y0dddwwd(v=vs.80).aspxから

しかし、もしそうなら:

if(prng(9) == 0)
{
    delete this->card;
}

メイクファイルを実行すると、次のエラーが表示されます。

エラー: クラス VendingMachine に card という名前のメンバーがありません

これは本当ですが、そうではありません。コンパイラは、VendingMachine がこのメソッドを呼び出すと想定していますか? 生徒がそうするからです。

  1. すべての自動販売機に生徒を追加し、代わりにそのメンバーからカードを削除する必要がありますか? 複数の学生がいて、この自動販売機に割り当てられている場合は全員を保管する必要があるため、これを行わないことを強く望んでいます。とはいえ、やっぱりこうすればいいんですけどね。

  2. カードの削除が発生したが、カードが NULL ではない場合、カードを削除したときに正確に何がダウンしたのですか?

  3. カードを削除するにはどうすればよいですか?

ありがとう!

編集:変更を適用した後、コードは次のようになります。

if(prng(9) == 0)
{
    cout << "Destroying card" << endl;
    delete card;
    card = NULL;
    cout << "Card Destroyed" << endl;
    }

残念ながら、segfault が発生します。これはおそらく、存在しない破壊されたカードにアクセスしているためです。Destroying card と Card Destroyed が表示されるので、

しかし、この通話で私が持っているカウントは表示されません:

    if(card == NULL)
    {
        cout << "CARD DESTROYEDADJIWJDOQIODJWDIOJWQODWODIQODJWJOWDW" << endl;
        card = studentOffice->create(id, 5);
    }

明らかに、カードはまだ NULL ではありませんか? これは奇妙です。

EDIT2:問題がどこにあるのか、なぜセグメンテーション違反があるのか​​ はわかっていると思います。現在取り組んでいます。

EDIT3: カードが破壊されたときに使用した呼び出しの順序を並べ替えて解決しました。

4

4 に答える 4

3

メソッドbuyでは、ポインターを削除して に設定する必要がありますNULL(deleteはポインターを に自動的に設定しませんNULL)。

VendingMachine::Status VendingMachine::buy(Card* &card)
{
    if(prng(9) == 0) // generates number from 0-9
    {
        delete card;
        card = NULL;
    }
    return status;
}

これが、ポインターが参照によって渡される理由です (そのため、ポインターNULLのコピーではなく、元のポインターに割り当てることができます)。

それに加えて、ではなく classに 属してthis->cardいるため、コンパイルされません。の観点からは、これはメソッド内の単なるパラメーターです。cardStudentVendingMachineVendingMachinebuy

于 2012-07-24T13:23:55.320 に答える
2

呼び出すと、呼び出しdeleteたポインターが に設定されることは言うまでもありませんNULL。削除後に NULL であることを確認したい場合は、削除後に自分で行う必要があります。

于 2012-07-24T13:24:44.387 に答える
1

@betabandido の回答に加えて、いつでもマクロで削除メソッドを定義して、これを行うことができます。

#define DELETE(ptr) ( delete ptr; ptr = NULL;) 

確かに、これはほとんど常に悪い考えであり、特に学校に通っている間は、ポインターを削除した後、ポインターを NULL に戻す習慣を身に付ける必要があります。

また、これは誤った安心感につながる可能性があります。

void MethodDeleteThis(void* item)
{
   delete item;
   item = NULL;
}

item は渡されたポインタの COPY であるため、問題は実際には修正されません。そのため、delete によって item が指すオブジェクトが解放された可能性がありますが、ポインターを NULL に設定しても、メソッドに渡されたポインターの値は NULL に変更されません。これを回避する唯一の方法は、ダブル ポインターを使用するか、参照によってポインターを渡すことです。ただし、gobject と gstreamer がこれを行うのを見てきました。

私が気づいた多くのライブラリは、割り当てまたは割り当て解除がある場合は常にポインターを返すため、値をより確実に取得してテストできます。

于 2012-07-24T13:44:13.407 に答える
1

他の人が指摘しているようにdelete、ポインターを使用するときはいつでも、NULL直後にそれを設定する必要があります。マクロを使用しないでください。これを行う習慣を身につけてください。

さて、私のベストプラクティスの帽子をかぶろうとしています:

この種のことを行うときは、ポインターを参照で渡すよりも、ダブルポインターを渡す方が便利だといつも思っています。

VendingMachine::Status VendingMachine::buy(Card** card) {
    // ...
    if (NULL == *card) {
        delete *card;
        *card = NULL;
    }

これにより、コード内でポインターを少し異なる方法で処理する必要がありますが、ポインターを操作していることがより明確になり、メソッド呼び出しのあいまいさがなくなるという利点があります。

  vend.buy(card);  // Pointer reference
  vend.buy(&card); // Pointer to pointer

2 番目の呼び出しでは、メソッドが の値を変更できる可能性があり、おそらく変更することがわかりますcard

于 2012-07-24T14:15:09.753 に答える