C ++ Blackjack功能

时间:2012-06-21 04:04:30

标签: c++ blackjack

我开始自学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();
};

2 个答案:

答案 0 :(得分: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. 无处使用指针。按价值递交并归还所有内容。
  3. 当然,这些并不是唯一的选择,但它们可能会让你的生活更加轻松。

答案 1 :(得分:0)

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

允许我的回答更多地进入摘要:

在C ++中,我们区分值类和多态类。你会发现它们的名称不同,你也会发现两者之间的区别并不像人们想的那么尖锐,但它大致是这样的:

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

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

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

    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-)引用(或者通过值,特别是如果它们非常小)传递它们,并且你也按值返回它们:

    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”中的示例类,QLineEditQPushButton都是QWidget。它们可能具有相同的状态(大小,位置......),但当您用鼠标点击它们时会发生什么情况完全不同。

    在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:
        // ...
    };
    
  3. 对于您的计划,您必须决定是否需要deckhand是多态的。

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

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