2

我已经开始自学 C++ 并且一直在尝试编写一个二十一点程序。我正在尝试使用类来表示卡片、甲板和手。我相信到目前为止,除了dealCardToHand()方法之外,程序中的一切都在工作。

void dealCardToHand(deck& d, hand& h){
    h.setCard(h.getCardsInHand(), d.dealCard());
    h.setCardsInHand(h.getCardsInHand() + 1);
}

它似乎正确地增加了手牌的数量,但没有setCard()使用正确的数据调用该方法。任何帮助,将不胜感激。我包括相关的类和方法:

class deck{
    int topCard;
    card * cards[52];
 public:
     deck();
     void shuffle();
     void printDeck();
     card dealCard();
};

card deck::dealCard(){//returns top card of deck and increments top card one
    return *cards[topCard++];
}

class hand{
    card * handCards[12];
    int cardsInHand;
public:
    hand();
    card getCard(int i){ return *handCards[i]; }
    void setCard(int i, card c) { handCards[i] = &c; }
    int getCardsInHand() { return cardsInHand; }
    void setCardsInHand(int i) { cardsInHand = i; }
    void printHand();
};
4

2 回答 2

4

这是危险的(可能至少是您问题的一部分):

void setCard(int i, card c) {handCards[i]=&c;}

在这里,按值setCard(...)传递一个card对象。这意味着card在临时位置创建调用者的新副本。正是这个副本 ( c)setCard()起作用。通过设置handCards[i]=&c;,您可以保存此临时对象的位置。但是当setCard()返回时,该临时对象不再有效。

但是然后你继续取消引用handCards[i]. getCard()这会产生未定义的行为。理论上,你应该期待恶魔开始从你的鼻子里飞出来。在实践中,您会看到从getCard(). 或者崩溃。或者,如果你够倒霉,最后一个值传入setCard().

总的来说,看起来你在用指针玩得又快又松。我建议解决问题的两种方法之一:

  1. 到处使用指针,永远不要按值传递或返回。这可能会导致其他问题,但它们可能并不那么神秘。
  2. 无处使用指针。按值传递和返回所有内容。

当然,这些不是唯一的选择,但它们可能暂时让你的生活更轻松。

于 2012-06-21T04:38:06.410 回答
0

正如其他人所指出的那样,问题在于您将临时变量的地址存储在指针变量中,并在临时变量超出范围(并已被删除)后取消引用该指针变量。

让我的回答更深入一点:

在 C++ 中,我们区分了值类和多态类。你会发现它们有不同的名字,你还会发现两者之间的区别并不像人们想象的那么明显,但大致是这样的:

  1. 值类实例的状态彼此不同。如果两个实例的状态相等,则实例的行为也将相等。

    值类的示例是std::string、所有 STL 容器std::complex<>等。

    您可以像使用ints 一样使用它们:在堆栈上创建它们:

    std::string s = "Hello, World"; // NOT std::string * s = new std::string;
    

    按值聚合它们:

    class Widget {
        std::complex<double> m_value; // NOT std::complex<double> * m_value;
    public:
        // ...
    };
    

    您通常可以比较它们是否相等,复制它们,将它们存储在容器中:

    std::vector<std::string> vec;
    std::string s = "Hello, World";
    assert( s == "Hello, World" );
    vec.push_back( s );
    

    并且,与您的问题最相关的是,您通过 (const-)reference (或按值,特别是如果它们非常小)传递它们,并且您也按值返回它们:

    void func(const std::vector<double> & vec); // ok, just reading 'vec'
    void func(std::vector<double> & vec); // ok, possibly writing to 'vec'
    void func(std::vector<double> vec); // not so good, expensive in C++03; ok in C++11 in some situations
    std::vector<double> func(); // ok, return value optimisation (look it up!) will make this fast
    
  2. 多态类的行为不同,而不是状态。多态类的两个实例可能具有相同的状态,但行为仍然完全不同。多态类的状态也可能不同,但重点在于它们的行为。这就是 OOP(面向对象编程)的全部内容。

    从著名的 C++ 库“Qt”中借用示例类,aQLineEdit和 aQPushButton都是QWidgets。它们可能具有相同的状态(大小、位置……),但是当您用鼠标单击每个状态时,它们之间会发生完全不同的情况。

    在 C++ 中,要使用多态行为,您必须调用函数,并且必须通过指向公共基类的指针或引用来实现(QWidget如上)。因此,多态类通常分配在堆上:

    QLineEdit * le = new QLineEdit();
    QPushButton * pb = new QPushButton();
    
    QWidget * leAsWidget = le; // works
    QWidget * pbAsWidget = pb; // works
    

    并在(智能)指针变量中存储和传递:

    class MyWidget : public QWidget {
        QLineEdit * m_lineEdit;
        QPointer<QPushButton> m_pushButton; // QPointer is a smart pointer
    public:
        // ...
    };
    

对于您的程序,您必须决定是否需要deck以及是否hand是多态的。

如果是,请在堆上创建它们(使用new),通过指针存储和传递它们,并且不要忘记在完成它们后再次删除它们(或查看智能指针)。

如果不是,则给它们关系运算符(bool operator==(const deck &lhs, const deck &rhs), ...),复制语义(deck(const deck&), deck &operator=(const deck&)),在堆栈上创建它们并按值存储它们。通过 (const) 引用传递它们。您不需要删除它们,编译器会为您执行此操作。

于 2012-06-21T20:34:48.837 回答