2

我正在为学校做一个项目。它模拟学生从自动售货机购买汽水。有一个名为 Card 的类,它是 Student 类的成员。那是,

每个学生都有一张卡片,这是有道理的。

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 枚举中返回一个类型枚举。

有一个 prng,生成一个从 0 到 9 的随机数。作业说学生的卡片有十分之一的机会被破坏。下次调用 student.action() 时,他/她将获得一个新的。

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

最初,我想检查学生的 action() 例程以查看卡是否为 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;
}

运行我的makefile时它给了我这个错误:

错误:VendingMachine 类没有名为 card 的成员

这是真的,它没有。编译器是否假设 VendingMachine 将调用此方法?因为学生会。

  1. 也许我应该在每台自动售货机上添加一个学生,然后从该成员那里删除该卡?我强烈不希望这样做,因为有多个学生,这意味着如果他们被分配到这台自动售货机,我需要将他们全部存储起来。虽然,如果归根结底,我可以这样做。

  2. 如果删除卡发生,但卡不是NULL,我删除卡时到底发生了什么?

  3. 我将如何删除卡?

谢谢!

编辑:应用更改后,代码现在是:

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

不幸的是,我遇到了段错误,这可能是因为我正在访问一张不存在的已损坏卡。因为显示了销毁卡和已销毁卡,

但是我在这个电话中的 cout 没有出现:

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

那么显然该卡仍然不是NULL?这很奇怪。

EDIT2:我想我知道问题出在哪里,以及为什么会出现段错误。现在正在处理它。

EDIT3:通过重新排列使用卡时使用卡的调用顺序来解决。

4

4 回答 4

3

在方法buy中,您应该删除指针并将其设置为NULLdelete不会自动将指针设置为NULL):

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

这就是指针通过引用传递的原因(因此您可以分配NULL给原始指针而不是它的副本)。

除此之外,this->card不编译,因为card属于 class Student,而不是 VendingMachine. 从VendingMachine的角度来看,它只是方法中的一个参数buy

于 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 是传递的任何指针的副本。因此,虽然 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

通过第二次调用,您只需查看它就知道该方法可以并且可能会修改card.

于 2012-07-24T14:15:09.753 回答