У меня два класса. Один позвонил Agent
, другой позвонил Player
. Agent
— базовый класс, а Player
— производный класс.
При вызове функции SetHand()
из производного Player
класса я сталкиваюсь с ошибкой сегмента.
#include "deck.hpp"
#include <cstdint>
#include <cstdlib>
#include <memory>
#include <stdexcept>
#include <vector>
#ifndef BLACKJACK_INCLUDE_AGENT_H_
#define BLACKJACK_INCLUDE_AGENT_H_
class Agent {
public:
virtual std::vector<Card> GetHand() { return hand_; }
virtual void UpdateHand() {
if (deck_->empty())
throw std::runtime_error("deck is empty, cannot draw a card");
hand_.push_back(deck_->back());
deck_->pop_back();
}
void SetHand() {
for (uint8_t i = 0; i < 1; i++) {
if (deck_ == NULL)
throw std::runtime_error("deck is empty, cannot draw a card");
hand_.push_back(deck_->back());
deck_->pop_back();
}
}
virtual void UpdateDeck(std::vector<Card> deck) {
deck_ = std::make_shared<std::vector<Card>>(deck);
}
private:
std::shared_ptr<std::vector<Card>> deck_;
std::vector<Card> hand_;
};
#endif // !BLACKJACK_INCLUDE_AGENT_H_
При просмотре в отладчике это происходит потому, что он пытается использовать deck_
в Agent
, который не инициализирован.
#include "agent.hpp"
#include "deck.hpp"
#include <memory>
#ifndef BLACKJACK_INCLUDE_PLAYER_H_
#define BLACKJACK_INCLUDE_PLAYER_H_
class Player : public Agent {
public:
Player(std::vector<Card> deck, int initial_chips);
// use negative argument to reduce chip count
void UpdateChips(int chips);
private:
int id_;
bool hand_in_progress = false;
std::shared_ptr<std::vector<Card>> deck_;
std::vector<Card> hand_;
unsigned long chips_;
};
#endif // BLACKJACK_INCLUDE_PLAYER_H_
Если я перенесу функцию в класс Player
, все будет работать нормально. Однако мне бы хотелось, чтобы он мог наследовать эту функцию, поскольку я собираюсь использовать ее в других классах.
Код, в котором вызывается метод:
TEST(PlayerDeckTest, InitDeck) {
Deck deck(10);
Player player1(deck.GetDeck(), 100);
player1.SetHand();
std::vector<Card> hand = player1.GetHand();
while(!hand.empty()) {
EXPECT_FALSE(hand.back().GetSuit().empty());
EXPECT_FALSE(hand.back().GetValue() != 0);
hand.pop_back();
}
}
Пожалуйста, также покажите код, который создает объект и вызывает метод.
почему у базового и производного классов есть член с именем deck_
? Это рецепт катастрофы
В Player
есть два участника по имени deck_
и два участника по имени hand_
. Я думаю, что на самом деле вам нужно, чтобы эти участники были только один раз в жизни Agent
У вас не может быть виртуальных или полиморфных переменных-членов.
Функции базового класса будут использовать переменные того же базового класса. Они понятия не имеют, сколько может быть производных классов и какие у них есть члены.
Ваша функция SetHand()
путает deck_ == NULL
с deck_->empty()
. В любой из этих функций deck_
может иметь значение «ноль». И когда он не «нулевой», он все равно может быть пустым.
@ 463035818_is_not_an_ai удаление колоды и руки из игрока решило проблему.
У Agent
и Player
есть свои отдельные члены deck_
и hand_
. Нет веской причины для того, чтобы Player
объявлял свой собственный, вместо этого он должен использовать члены, унаследованные от Agent
:
class Agent {
public:
...
protected: // <-- not private!
std::shared_ptr<std::vector<Card>> deck_;
std::vector<Card> hand_;
};
class Player : public Agent {
public:
...
private:
int id_;
bool hand_in_progress = false;
//std::shared_ptr<std::vector<Card>> deck_; // <- get rid of this!
//std::vector<Card> hand_; // <- get rid of this!
unsigned long chips_;
};
При этом не следует заставлять каждый Agent
/Player
помещать свою колоду std::vector
внутрь std::shared_ptr
. Это просто перебор. Если вы хотите использовать shared_ptr
, то его следует создать внутри класса Deck
, а затем передать каждому Agent
/Player
по мере необходимости.
Я бы предложил изменить Agent
, чтобы хранить необработанный vector*
указатель или vector&
ссылку на исходный vector
объект, нет необходимости делать отдельную копию vector
для каждого Agent
/Player
. В каждом Agent
/Player
не должны быть игральные карты из своей колоды, это должны быть игральные карты из общей колоды:
class Agent {
public:
Agent(std::vector<Card>& deck)
: deck_(deck)
{
}
std::vector<Card> GetHand() { return hand_; }
void DrawCard() {
if (deck_.empty())
throw std::runtime_error("deck is empty, cannot draw a card");
hand_.push_back(deck_.back());
deck_.pop_back();
}
void SetHand() {
hand_.clear();
for (uint8_t i = 0; i < 2; ++i) {
DrawCard();
}
}
protected:
std::vector<Card>& deck_;
std::vector<Card> hand_;
};
class Player : public Agent {
public:
Player(std::vector<Card>& deck, int initial_chips)
: Agent(deck), chips_(initial_chips)
{
}
...
private:
int id_ = 0;
bool hand_in_progress = false;
unsigned long chips_= 0;
};
TEST(PlayerDeckTest, InitDeck) {
Deck deck(10);
std::vector<Card> deckCards = deck.GetDeck();
Player player1(deckCards, 100);
player1.SetHand();
for (auto &card : player1.GetHand()) {
EXPECT_FALSE(card.GetSuit().empty());
EXPECT_FALSE(card.GetValue() != 0);
}
Player player2(deckCards, 10);
player2.SetHand();
for (auto &card : player2.GetHand()) {
EXPECT_FALSE(card.GetSuit().empty());
EXPECT_FALSE(card.GetValue() != 0);
}
}
Еще лучше было бы, если бы каждый Agent
/Player
содержал указатель/ссылку на реальный объект Deck
вместо его внутреннего std::vector
, например:
class Deck{
public:
Card DrawCard() {
if (cards_.empty())
throw std::runtime_error("deck is empty, cannot draw a card");
Card card = cards_.back();
cards_.pop_back();
return card;
}
private:
std::vector<Card> cards_;
};
class Agent {
public:
Agent(Deck& deck)
: deck_(deck)
{
}
std::vector<Card> GetHand() { return hand_; }
void DrawCard() {
hand_.push_back(deck_.DrawCard());
}
void SetHand() {
hand_.clear();
for (uint8_t i = 0; i < 2; ++i) {
DrawCard();
}
}
protected:
Deck& deck_;
std::vector<Card> hand_;
};
class Player : public Agent {
public:
Player(Deck& deck, int initial_chips)
: Agent(deck), chips_(initial_chips)
{
}
...
private:
int id_ = 0;
bool hand_in_progress = false;
unsigned long chips_= 0;
};
TEST(PlayerDeckTest, InitDeck) {
Deck deck(10);
Player player1(deck, 100);
player1.SetHand();
for (auto &card : player1.GetHand()) {
EXPECT_FALSE(card.GetSuit().empty());
EXPECT_FALSE(card.GetValue() != 0);
}
Player player2(deck, 10);
player2.SetHand();
for (auto &card : player2.GetHand()) {
EXPECT_FALSE(card.GetSuit().empty());
EXPECT_FALSE(card.GetValue() != 0);
}
}
Ре.
"When calling the function SetHand() ..."
: нигде в показанном кодеSetHand
на самом деле не вызывается. Пожалуйста, отредактируйте свой вопрос, чтобы предоставить минимально воспроизводимый пример.