调用作为 class 私有成员的对象的最佳实践?
Best practice for calling objects that are private members of a class?
如何为 class 的私有成员对象调用函数?我对我的 C++ 有点生疏,所以我很确定我开始重新思考这个问题并把自己挖进了一个洞。
为了复习我的 C++,我正在创建一个程序,列出一些不同的骰子游戏,用户选择他们想玩的游戏和玩家数量。每个游戏都有不同面数和不同规则的骰子,所以我认为最好的方法是为每个游戏创建一个 class,然后将玩家和骰子对象存储在 class 中,但我不能弄清楚我应该如何访问播放器或对象函数。这是一些示例代码。
Farkle.h
#ifndef FARKLE_FARKLE_H
#define FARKLE_FARKLE_H
#include "Player.h"
#include "Die.h"
class Farkle {
public:
Farkle();
void setPlayers(int t_numPlayers);
std::string getPlayers(int t_playerNum); // probably don't need
private:
std::vector<Player> m_playerList;
Die m_die;
};
#endif //FARKLE_FARKLE_H
Farkle.cpp
#include "Farkle.h"
#include "Die.h"
Farkle::Farkle() {
m_die = Die(6);
}
void Farkle::setPlayers(int t_numPlayers) {
int i;
for (i = 0; i < t_numPlayers; i++){
m_playerList.emplace_back(Player(("Player " + std::to_string(i + 1))));
}
}
std::string Farkle::getPlayers(int t_num){
return m_playerList[t_num].GetPlayerName();
}
Player.h
#ifndef FARKLE_PLAYER_H
#define FARKLE_PLAYER_H
#include <string>
#include <vector>
class Player {
public:
// constructor
Player(std::string t_playerName);
// setters
void SetPlayerName(std::string t_playerName);
void SetPlayerScore(int t_score);
// getters
std::string GetPlayerName();
int GetPlayerScore();
private:
std::string m_playerName;
int m_score;
};
#endif //FARKLE_PLAYER_H
Player.cpp
#include "Player.h"
Player::Player(std::string t_playerName){
m_playerName = t_playerName;
m_score = 0;
}
// set the players name
void Player::SetPlayerName(std::string t_playerName){
m_playerName = t_playerName;
}
// set the players score
void Player:: SetPlayerScore(int t_score){
m_score = t_score;
}
// get the name of the player
std::string Player::GetPlayerName() {
return m_playerName;
}
// get the players score
int Player::GetPlayerScore() {
return m_score;
}
Die.h
#ifndef FARKLE_DIE_H
#define FARKLE_DIE_H
class Die {
public:
explicit Die(int = 6);
void roll();
int getSides();
int getValue();
private:
int m_sides;
int m_value;
};
#endif //FARKLE_DIE_H
Die.cpp
#include "Die.h"
#include <cstdlib>
#include <ctime>
Die::Die(int t_numSides){
unsigned int seed = time(0);
srand(seed);
m_sides = t_numSides;
roll();
}
void Die::roll(){
const int MIN_VALUE = 1;
m_value = (rand() % (m_sides - MIN_VALUE +1)) + MIN_VALUE;
}
int Die::getSides() {
return m_sides;
}
int Die::getValue() {
return m_value;
}
只是想指出,这些 class 还没有接近完成,我只是想在投入更多工作之前想出我的方法。
目前我知道有两种方法可以解决这个问题,但它们似乎都不对。我可能又想多了。
我知道我可以在 Farkle 中创建一个函数来调用其中一个对象的函数,就像我在 Farkle.cpp 中使用 getPlayers 函数来获取玩家名称一样。对我来说,这种方法感觉有点傻,因为我只是使用该函数调用另一个 classes 函数。
我知道我也可以使对象成为 class 的 public 成员。例如,我可以移动“Die m_die;”到 Farkle 的 public 部分,然后稍后使用“game.die.getValue()”调用一个函数。我觉得这种方法虽然不是最佳实践,但应该避免。我找不到太多关于它的信息,所以我可能是不正确的。
哪种方法是最佳做法?
还有其他我可能会遗漏的方法吗?
与其为每种游戏类型创建 class,不如使用名称空间(不太熟悉)或函数之类的东西会更好吗?
好的,这将是 long-ish,但这里的代码用于演示我在评论中提到的内容。
不过我们将从 main.cpp
开始:
#include <iostream>
#include <memory>
#include "DiceGame.hpp"
#include "Player.hpp"
#include "TestGame.hpp"
int main() {
Player one("Gus");
Player two("Pat");
std::unique_ptr<DiceGame> game(new TestGame);
game->set_up();
game->register_player(one);
game->register_player(two);
// Game loop would go here instead
one.take_turn();
two.take_turn();
// When a game finishes, you'd clean up so you can play another
}
我们的玩家可以玩很多游戏,所以他们存在于此。选择游戏后,我们会对其进行设置并将我们的玩家与之相关联。然后我让每个玩家转一圈,但是你可以在这里放置一个游戏循环,或者让游戏负责它的循环,或者有一个 GameMaster
类型 class 来确保它被玩。
接下来,我说我不喜欢 Die
class 因为它的随机数。这是不同的看法:
#ifndef DIE_HPP
#define DIE_HPP
#include <random>
class Die {
public:
explicit Die();
explicit Die(int sides);
int roll();
private:
int m_sides = 6;
std::uniform_int_distribution<int> m_values;
static std::mt19937 m_roller;
};
#endif
static
成员作为一个实体存在于 Die
的所有成员中。换句话说,所有 Die
个对象共享这个单一资源。每个 Die
都有自己的分布,让您可以轻松添加游戏所需的任何骰子。
// Die.cpp
#include "Die.hpp"
#include <random>
// static member initialization
std::mt19937 Die::m_roller{std::random_device{}()};
Die::Die() : m_values(1, m_sides) {}
Die::Die(int sides) : m_sides(sides), m_values(1, m_sides) {}
int Die::roll() { return m_values(m_roller); }
Player
class 能够与它现在正在玩的任何游戏互动。
#ifndef PLAYER_HPP
#define PLAYER_HPP
#include <string>
class DiceGame;
class Player {
public:
Player(std::string name);
int get_score() const;
void set_score(int newScore);
void set_game(DiceGame* game);
void take_turn();
private:
std::string m_name;
int m_score = 0;
DiceGame* m_game = nullptr;
};
#endif
// Player.cpp
#include "Player.hpp"
#include <iostream>
#include <string>
#include "DiceGame.hpp"
Player::Player(std::string name) : m_name(name) {}
int Player::get_score() const { return m_score; }
void Player::set_score(int newScore) { m_score = newScore; }
void Player::set_game(DiceGame* game) { m_game = game; }
void Player::take_turn() {
std::cout << m_name << "'s turn:\n";
m_game->roll();
std::cout << '\n';
}
我评论中提出的最后一点是您可能需要一个基础 class 因为您说过您计划展示不同的骰子游戏以供用户选择。
这为您的所有骰子游戏创建了一个标准接口,这将使它们的实现更加直接。
我们还为我们的 Player
注册了他们现在要玩的游戏。这允许您的 Player
直接与他们当前正在玩的任何游戏互动。
#ifndef DICEGAME_HPP
#define DICEGAME_HPP
#include <vector>
#include "Die.hpp"
#include "Player.hpp"
class DiceGame {
public:
DiceGame();
virtual ~DiceGame();
void set_up();
void register_player(Player& player);
void roll();
protected:
std::vector<Die> m_dice;
std::vector<Player*> m_players;
virtual void v_set_up() = 0;
};
#endif
// DiceGame.cpp
#include "DiceGame.hpp"
#include <iostream>
DiceGame::DiceGame() = default;
DiceGame::~DiceGame() = default;
void DiceGame::set_up() { v_set_up(); }
void DiceGame::register_player(Player& player) {
m_players.push_back(&player);
player.set_game(this);
}
void DiceGame::roll() {
for (auto& die : m_dice) {
std::cout << die.roll() << " ";
}
std::cout << '\n';
}
最后,我创建了一个 super-basic 测试游戏,以确保所有棋子都能很好地相互配合。游戏给自己 6 d6s。
#ifndef TESTGAME_HPP
#define TESTGAME_HPP
#include "DiceGame.hpp"
class TestGame : public DiceGame {
public:
TestGame();
private:
void v_set_up() override;
};
#endif
#include "TestGame.hpp"
#include "DiceGame.hpp"
TestGame::TestGame() = default;
void TestGame::v_set_up() {
for (int i = 0; i < 6; ++i) {
m_dice.emplace_back(6);
}
}
输出:
Gus's turn:
2 2 2 6 3 5
Pat's turn:
3 3 3 5 1 3
遗漏了很多内容,例如适当的游戏循环机制和 clean-up 步骤,但足以说明如何在 Player
和游戏之间建立关系已经选择玩
如何为 class 的私有成员对象调用函数?我对我的 C++ 有点生疏,所以我很确定我开始重新思考这个问题并把自己挖进了一个洞。
为了复习我的 C++,我正在创建一个程序,列出一些不同的骰子游戏,用户选择他们想玩的游戏和玩家数量。每个游戏都有不同面数和不同规则的骰子,所以我认为最好的方法是为每个游戏创建一个 class,然后将玩家和骰子对象存储在 class 中,但我不能弄清楚我应该如何访问播放器或对象函数。这是一些示例代码。
Farkle.h
#ifndef FARKLE_FARKLE_H
#define FARKLE_FARKLE_H
#include "Player.h"
#include "Die.h"
class Farkle {
public:
Farkle();
void setPlayers(int t_numPlayers);
std::string getPlayers(int t_playerNum); // probably don't need
private:
std::vector<Player> m_playerList;
Die m_die;
};
#endif //FARKLE_FARKLE_H
Farkle.cpp
#include "Farkle.h"
#include "Die.h"
Farkle::Farkle() {
m_die = Die(6);
}
void Farkle::setPlayers(int t_numPlayers) {
int i;
for (i = 0; i < t_numPlayers; i++){
m_playerList.emplace_back(Player(("Player " + std::to_string(i + 1))));
}
}
std::string Farkle::getPlayers(int t_num){
return m_playerList[t_num].GetPlayerName();
}
Player.h
#ifndef FARKLE_PLAYER_H
#define FARKLE_PLAYER_H
#include <string>
#include <vector>
class Player {
public:
// constructor
Player(std::string t_playerName);
// setters
void SetPlayerName(std::string t_playerName);
void SetPlayerScore(int t_score);
// getters
std::string GetPlayerName();
int GetPlayerScore();
private:
std::string m_playerName;
int m_score;
};
#endif //FARKLE_PLAYER_H
Player.cpp
#include "Player.h"
Player::Player(std::string t_playerName){
m_playerName = t_playerName;
m_score = 0;
}
// set the players name
void Player::SetPlayerName(std::string t_playerName){
m_playerName = t_playerName;
}
// set the players score
void Player:: SetPlayerScore(int t_score){
m_score = t_score;
}
// get the name of the player
std::string Player::GetPlayerName() {
return m_playerName;
}
// get the players score
int Player::GetPlayerScore() {
return m_score;
}
Die.h
#ifndef FARKLE_DIE_H
#define FARKLE_DIE_H
class Die {
public:
explicit Die(int = 6);
void roll();
int getSides();
int getValue();
private:
int m_sides;
int m_value;
};
#endif //FARKLE_DIE_H
Die.cpp
#include "Die.h"
#include <cstdlib>
#include <ctime>
Die::Die(int t_numSides){
unsigned int seed = time(0);
srand(seed);
m_sides = t_numSides;
roll();
}
void Die::roll(){
const int MIN_VALUE = 1;
m_value = (rand() % (m_sides - MIN_VALUE +1)) + MIN_VALUE;
}
int Die::getSides() {
return m_sides;
}
int Die::getValue() {
return m_value;
}
只是想指出,这些 class 还没有接近完成,我只是想在投入更多工作之前想出我的方法。
目前我知道有两种方法可以解决这个问题,但它们似乎都不对。我可能又想多了。
我知道我可以在 Farkle 中创建一个函数来调用其中一个对象的函数,就像我在 Farkle.cpp 中使用 getPlayers 函数来获取玩家名称一样。对我来说,这种方法感觉有点傻,因为我只是使用该函数调用另一个 classes 函数。
我知道我也可以使对象成为 class 的 public 成员。例如,我可以移动“Die m_die;”到 Farkle 的 public 部分,然后稍后使用“game.die.getValue()”调用一个函数。我觉得这种方法虽然不是最佳实践,但应该避免。我找不到太多关于它的信息,所以我可能是不正确的。
哪种方法是最佳做法?
还有其他我可能会遗漏的方法吗?
与其为每种游戏类型创建 class,不如使用名称空间(不太熟悉)或函数之类的东西会更好吗?
好的,这将是 long-ish,但这里的代码用于演示我在评论中提到的内容。
不过我们将从 main.cpp
开始:
#include <iostream>
#include <memory>
#include "DiceGame.hpp"
#include "Player.hpp"
#include "TestGame.hpp"
int main() {
Player one("Gus");
Player two("Pat");
std::unique_ptr<DiceGame> game(new TestGame);
game->set_up();
game->register_player(one);
game->register_player(two);
// Game loop would go here instead
one.take_turn();
two.take_turn();
// When a game finishes, you'd clean up so you can play another
}
我们的玩家可以玩很多游戏,所以他们存在于此。选择游戏后,我们会对其进行设置并将我们的玩家与之相关联。然后我让每个玩家转一圈,但是你可以在这里放置一个游戏循环,或者让游戏负责它的循环,或者有一个 GameMaster
类型 class 来确保它被玩。
接下来,我说我不喜欢 Die
class 因为它的随机数。这是不同的看法:
#ifndef DIE_HPP
#define DIE_HPP
#include <random>
class Die {
public:
explicit Die();
explicit Die(int sides);
int roll();
private:
int m_sides = 6;
std::uniform_int_distribution<int> m_values;
static std::mt19937 m_roller;
};
#endif
static
成员作为一个实体存在于 Die
的所有成员中。换句话说,所有 Die
个对象共享这个单一资源。每个 Die
都有自己的分布,让您可以轻松添加游戏所需的任何骰子。
// Die.cpp
#include "Die.hpp"
#include <random>
// static member initialization
std::mt19937 Die::m_roller{std::random_device{}()};
Die::Die() : m_values(1, m_sides) {}
Die::Die(int sides) : m_sides(sides), m_values(1, m_sides) {}
int Die::roll() { return m_values(m_roller); }
Player
class 能够与它现在正在玩的任何游戏互动。
#ifndef PLAYER_HPP
#define PLAYER_HPP
#include <string>
class DiceGame;
class Player {
public:
Player(std::string name);
int get_score() const;
void set_score(int newScore);
void set_game(DiceGame* game);
void take_turn();
private:
std::string m_name;
int m_score = 0;
DiceGame* m_game = nullptr;
};
#endif
// Player.cpp
#include "Player.hpp"
#include <iostream>
#include <string>
#include "DiceGame.hpp"
Player::Player(std::string name) : m_name(name) {}
int Player::get_score() const { return m_score; }
void Player::set_score(int newScore) { m_score = newScore; }
void Player::set_game(DiceGame* game) { m_game = game; }
void Player::take_turn() {
std::cout << m_name << "'s turn:\n";
m_game->roll();
std::cout << '\n';
}
我评论中提出的最后一点是您可能需要一个基础 class 因为您说过您计划展示不同的骰子游戏以供用户选择。
这为您的所有骰子游戏创建了一个标准接口,这将使它们的实现更加直接。
我们还为我们的 Player
注册了他们现在要玩的游戏。这允许您的 Player
直接与他们当前正在玩的任何游戏互动。
#ifndef DICEGAME_HPP
#define DICEGAME_HPP
#include <vector>
#include "Die.hpp"
#include "Player.hpp"
class DiceGame {
public:
DiceGame();
virtual ~DiceGame();
void set_up();
void register_player(Player& player);
void roll();
protected:
std::vector<Die> m_dice;
std::vector<Player*> m_players;
virtual void v_set_up() = 0;
};
#endif
// DiceGame.cpp
#include "DiceGame.hpp"
#include <iostream>
DiceGame::DiceGame() = default;
DiceGame::~DiceGame() = default;
void DiceGame::set_up() { v_set_up(); }
void DiceGame::register_player(Player& player) {
m_players.push_back(&player);
player.set_game(this);
}
void DiceGame::roll() {
for (auto& die : m_dice) {
std::cout << die.roll() << " ";
}
std::cout << '\n';
}
最后,我创建了一个 super-basic 测试游戏,以确保所有棋子都能很好地相互配合。游戏给自己 6 d6s。
#ifndef TESTGAME_HPP
#define TESTGAME_HPP
#include "DiceGame.hpp"
class TestGame : public DiceGame {
public:
TestGame();
private:
void v_set_up() override;
};
#endif
#include "TestGame.hpp"
#include "DiceGame.hpp"
TestGame::TestGame() = default;
void TestGame::v_set_up() {
for (int i = 0; i < 6; ++i) {
m_dice.emplace_back(6);
}
}
输出:
Gus's turn:
2 2 2 6 3 5
Pat's turn:
3 3 3 5 1 3
遗漏了很多内容,例如适当的游戏循环机制和 clean-up 步骤,但足以说明如何在 Player
和游戏之间建立关系已经选择玩