类与头文件的关系

Relationship between classes and header files

我正在尝试学习如何在 C++ 中使用 类。这是我的文件结构。

main.cpp (sueca.cpp)

#include "Card.h"
#include "Deck.h"
#include "Player.h"
#include "Turn.h"
#include "Game.h"

int main ()
{
    std::cout << "\t\t\t.:: Sueca Game ::." << std::endl;

    Game sueca;
    //sueca.startGame();
    
    return 0;
}

Card.h:

#ifndef CARD_H
#define CARD_H

#include <iostream>             // std::cout
#include <string>               // std::string

class Card {
    public:
        Card();
        Card(int&, int&);
        ~Card();
        int getSuit() const;
        int getRank() const;
        int getPoints() const;
        void printCard () const;
        void setSuit(int&); 
        void setRank(int&);
        void setCard(int&, int&);

    private:
        int suit;
        int rank;
};

#endif

Card.cpp

#include "Card.h"

Card::Card ()
{
    suit = -1;
    rank = -1;
}

Card::Card (int &isuit, int &irank)
{
    suit = isuit;
    rank = irank;
}

Card::~Card ()
{

}

int Card::getSuit () const
{
    return suit;
}

int Card::getRank () const
{
    return rank;
}

void Card::setSuit (int &isuit) 
{
    suit = isuit;
    return;
}

void Card::setRank (int &irank) 
{
    rank = irank;
    return;
}

void Card::setCard (int &isuit, int &irank)
{
    suit = isuit;
    rank = irank;
}


int Card::getPoints () const
{
    if (rank <= 4)
        return 0;
    else if (rank <= 7)
        return rank-3;
    else
        return rank+2;
}

void Card::printCard () const
{
    std::string naipe, nome;
    switch (suit) 
    {
        case 0: 
            naipe = "HEARTS";
            break;
        case 1:
            naipe = "DIAMONDS";
            break;
        case 2:
            naipe = "CLUBS";
            break;
        case 3:
            naipe = "SPADES";
            break;
    }
    switch (rank)
    {
        case 0: 
            nome = "TWO";
            break;
        case 1:
            nome = "THREE";
            break;
        case 2:
            nome = "FOUR";
            break;
        case 3:
            nome = "FIVE";
            break;
        case 4:
            nome = "SIX";
            break;
        case 5:
            nome = "QUEEN";
            break;
        case 6:
            nome = "JACK";
            break;
        case 7:
            nome = "KING";
            break;
        case 8:
            nome = "SEVEN";
            break;
        case 9:
            nome = "ACE";
            break;
    }

    std::cout << nome << " of " << naipe;
    return;
}

Deck.h:

#ifndef DECK_H
#define DECK_H

#include <iostream>             // std::cout
#include <algorithm>            // std::shuffle
#include <array>                // std::array
#include <string>               // std::string
#include <random>               // std::default_random_engine
#include <chrono>               // std::chrono::system_clock

#include "Card.h"

class Deck : public Card{
    public:
        Deck();
        ~Deck();
        void shuffleDeck(int&); 
        void printDeck();   

    private:
        std::array<Card,40> element;         
};

#endif

Deck.cpp:

#include "Deck.h"

Deck::Deck ()
{
    auto i = 0;
    for (auto s = 0; s < 4; ++s)
    {
        for (auto r = 0; r < 10; ++r)
        {
            element[i].setCard(s, r);
            ++i;
        }
    }
}

void Deck::shuffleDeck (int &times)
{
    for (auto i = 0; i < times; ++i)
    {
        unsigned seed = std::chrono::system_clock::now().time_since_epoch().count();
        std::shuffle(element.begin(), element.end(), std::default_random_engine(seed));
    }
    return;
}

void Deck::printDeck ()
{
    for (auto c = 0; c < element.size(); ++c)
    {
        std::cout << "card\t(" << c << ")\t";
        element[c].printCard();
    }
    return;
}

Player.h:

#ifndef PLAYER_H
#define PLAYER_H

#include <iostream>             // std::cout
#include <string>               // std::string
#include <array>                // std::array

#include "Card.h"

class Player : public Card {
    public:
        Player();
        Player(int&, std::string&);
        ~Player();
        int getID() const;
        std::string getName() const;
        int getPoints() const;
        int printHand() const;
        void setID(int&);
        void setName(std::string&);
        void setPoints(int&);

    private:
        int id;
        std::string name;
        int points = 0;
        std::array<Card,10> hand;         
};

#endif

Player.cpp

#include "Player.h"

Player::Player ()
{
    id = -1;
    points = 0;
}

Player::Player (int &iid, std::string &iname)
{
    id = iid;
    name = iname;
    points = 0;
}

Player::~Player ()
{
    
}

int Player::getID () const
{
    return id;
}

std::string getName () const
{
    return name;
}

int Player::getPoints () const
{
    return points;
}

int Player::printHand () const
{
    if (hand.size() == 0)
    {
        std::cout << "\t\t\tERROR: Player " << name << " has no cards (" << id << ")" << std::endl;
        return -1;
    }

    std::cout << "(" << id << ") " << name << "'s cards:" << std::endl;
    for (auto i = 0; i < 10; ++i)
        hand[i].printCard();

    return 0;
}

void Player::setID(int &iid)
{
    id = iid;
    return;
}

void Player::setName(std::string &iname)
{
    name = iname;
    return;
}

void Player::setPoints(int &ipoints)
{
    points = ipoints;
    return;
}

Turn.h:

#ifndef TURN_H
#define TURN_H

#include <iostream>             // std::cout
#include <array>                // std::array

#include "Player.h"
#include "Card.h"

// NOTE: I'd like to access the variable "trump" from the class Game, making it the parent of this class (one game has many turns)

class Turn {
    public:
        Turn();
        Turn(int&, std::array<Player,4>&);
        ~Turn();
        int getID() const;
        int playTurn();
        int getTurnWinner();
        
    private:
        int id;
        //int trump; --> use the trump variable from the parent Game instead
        std::array<Player,4> gamer;
        std::array<Card,4> move;    
};

#endif

Turn.cpp:

#include "Turn.h"

Turn::Turn ()
{
    id = -1;
}

Turn::Turn (int &roundNO, std::array<Player,4> &players)
{
    id = roundNO;
    gamer = players;
}

int Turn::getID () const
{
    return id;
}

int Turn::playTurn ()
{   
    for (auto p = 0; p < 4; ++p)
    {
        std::cout << "\t(" << p << ") " << gamer[p].getName() << "'s turn!" << std::endl;

        if (gamer[p].printHand() == -1)
            return -1;
        
        std::cout << "Pick the number of the card you want to play: ";

        int playcard;
        std::cin >> playcard;

        while (playcard > 10)
        {
            std::cout << "Invalid selection!\nPick the number of the card you want to play: ";
            std::cin >> playcard;
        }
        
        //move[p] = gamer[p].hand[playcard];
    }    

    return 0;
}

int Turn::getTurnWinner ()
{
    Card max, maxtrunfo;
    int winner, winnertrunfo, points = 0;
    bool trunfo = false;

    if (move.size() < 4)
    {   
        std::cout << "\t\t\tERROR: Round " << id << " has" << move.size() << " cards." << std::endl;
        return -1;
    }
    if (gamer.size() < 4)
    {   
        std::cout << "\t\t\tERROR: Round " << id << " has" << gamer.size() << " cards." << std::endl;
        return -2;
    }

    for (auto i = 0; i < 4; ++i)
    {
        if (move[i].getRank() > max.getRank())
        {
            max = move[i];
            winner = i;
        }
            
        if ((move[i].getSuit() == trump) && (move[i].getRank() > maxtrunfo.getRank()))                                                ///VER ISTO
        {
            maxtrunfo = move[i];
            trunfo = true;
            winnertrunfo = i;
        }

        points += move[i].getPoints();
    }

    if (trunfo)
    {
        std::cout << "\t(" << winnertrunfo << ") " << gamer[winnertrunfo].getName() << " wins!" << std::endl;
        //gamer[winnertrunfo].getPoints() = points;
    }
    else
    {
        std::cout << "\t(" << winner << ") " << gamer[winner].getName() << " wins!" << std::endl;
        //gamer[winner].getPoints() = points;
    }

    return 0;
}

Game.h:

#ifndef GAME_H
#define GAME_H

#include <iostream>             // std::cout
#include <algorithm>            // std::shuffle
#include <array>                // std::array
#include <string>               // std::string
#include <random>               // std::default_random_engine
#include <chrono>               // std::chrono::system_clock

#include "Turn.h"
#include "Player.h"

class Game {
    public:
        //Game();
        //~Game();
        int startGame();
        int setTrump();

    private: 
        int trunfo;
        int duration;
        Deck gamedeck;
        std::array<Turn,10> round;
        std::array<Player,4> gamer;
};

#endif

Game.cpp

#include "Game.h"

/*Game::Game ()
{
    trunfo = -1;
    duration = 0;
}

Game::~Game ()
{

}*/

int Game::setTrump ()
{
    unsigned seed = std::chrono::system_clock::now().time_since_epoch().count();
    std::default_random_engine generator(seed);
    std::uniform_int_distribution<int> gamer(0,3);
    int player = gamer(generator);
    
    return player;
}

int Game::startGame ()
{
    int shuffle = 1;

    std::cout << "How many times do you want to shuffle the deck? ";
    std::cin >> shuffle;

    while (shuffle < 1)
    {
        std::cout << "Please insert an integer bigger than 0.\nHow many times do you want to shuffle the deck? ";
        std::cin >> shuffle;
    }

    gamedeck.printDeck();
    gamedeck.shuffleDeck(shuffle);

    std::cout << "Deck shuffled " << shuffle << " times!" << std::endl;
    std::cout << "\t * The player pairings will be (1 & 3) and (2 & 4). Let's proceed to name each player." << std::endl;

    std::string playername;
    for (auto i = 0; i < 4; ++i)
    {
        std::cout << "Type the name of Player " << i+1 << ": ";
        std::cin >> playername;

        while (playername.size() == 0)
        {
            std::cout << "Please insert a readable name.\nType the name of Player " << i+1 << ": ";
            std::cin >> playername;
        }

        gamer[i].id = i;
        gamer[i].name = playername;
        
        for (auto c = 0; c < 10; c++)
            gamer[i].hand[c] = gamedeck.element[c+i*10];                                                                        /// APRENDER A PASSAR O ENDEREÇO DA CARTA
    }

    // Set trump card & suit
    int trump_player, trump_card;
    trump_player = setTrump();

    std::cout << "The trump card is (" << trump_player << ") "<< gamer[trump_player].name << "'s ";
    gamer[trump_player].hand[0].print_card();
    trunfo = gamer[trump_player].hand[0].suit;

    return 0;
}

终端错误:

g++ sueca.cpp -o sueca
/usr/bin/ld: /tmp/ccmCL0jl.o: in function `std::array<Turn, 10ul>::array()':
sueca.cpp:(.text._ZNSt5arrayI4TurnLm10EEC2Ev[_ZNSt5arrayI4TurnLm10EEC5Ev]+0x27): undefined reference to `Turn::Turn()'
/usr/bin/ld: sueca.cpp:(.text._ZNSt5arrayI4TurnLm10EEC2Ev[_ZNSt5arrayI4TurnLm10EEC5Ev]+0x7d): undefined reference to `Turn::~Turn()'
/usr/bin/ld: /tmp/ccmCL0jl.o: in function `std::array<Turn, 10ul>::~array()':
sueca.cpp:(.text._ZNSt5arrayI4TurnLm10EED2Ev[_ZNSt5arrayI4TurnLm10EED5Ev]+0x39): undefined reference to `Turn::~Turn()'
/usr/bin/ld: /tmp/ccmCL0jl.o: in function `std::array<Player, 4ul>::array()':
sueca.cpp:(.text._ZNSt5arrayI6PlayerLm4EEC2Ev[_ZNSt5arrayI6PlayerLm4EEC5Ev]+0x27): undefined reference to `Player::Player()'
/usr/bin/ld: sueca.cpp:(.text._ZNSt5arrayI6PlayerLm4EEC2Ev[_ZNSt5arrayI6PlayerLm4EEC5Ev]+0x76): undefined reference to `Player::~Player()'
/usr/bin/ld: /tmp/ccmCL0jl.o: in function `std::array<Player, 4ul>::~array()':
sueca.cpp:(.text._ZNSt5arrayI6PlayerLm4EED2Ev[_ZNSt5arrayI6PlayerLm4EED5Ev]+0x39): undefined reference to `Player::~Player()'
/usr/bin/ld: /tmp/ccmCL0jl.o: in function `Game::Game()':
sueca.cpp:(.text._ZN4GameC2Ev[_ZN4GameC5Ev]+0x1d): undefined reference to `Deck::Deck()'
/usr/bin/ld: sueca.cpp:(.text._ZN4GameC2Ev[_ZN4GameC5Ev]+0x75): undefined reference to `Deck::~Deck()'
/usr/bin/ld: /tmp/ccmCL0jl.o: in function `Game::~Game()':
sueca.cpp:(.text._ZN4GameD2Ev[_ZN4GameD5Ev]+0x40): undefined reference to `Deck::~Deck()'
collect2: error: ld returned 1 exit status

此外,我知道这些都是非常基本的问题,我一直在阅读“C++ 之旅”,但它没有详细说明某些事情是如何完成的,所以我浏览网页寻找解决方案我的代码中出现的问题。尝试利用一些 C 知识自学 C++。如果你能推荐我一些进步的方法,我会很感激(也许我应该买另一本书?)。这个程序是我尝试编写一个在我的国家流行的纸牌游戏。

您可能需要稍微回顾一下您的书,但我会稍微解释一下。

包含 (.h) 文件通常存在,因此您可以拥有 10 个使用声明的 class 的 .cpp 文件。这是你放置 class Foo { ... } 东西的地方。然后任何想使用 Foo 的人都可以包含 .h 文件。

.cpp 文件是您实现 class 的地方。我想你明白这一点。

编译时,先编译每个 .cpp,然后 link 将它们一起编译。或者你可以这样做:

g++ --std=c++17 A.cpp B.cpp C.cpp -o MyProgram

这将对 MyProgram 进行编程(您可以随意调用它)并包含这三个 .cpp 文件中的所有代码。

当然,情况可能比这复杂得多。但是如果你做一个类似于我给你看的 g++ 行,列出你所有的 .cpp 文件,那么只要你已经实现了你定义的所有代码,它应该构建。

在旧的 c++ 时代,您必须定义 .cpp 和 .h。在现代 C++ 中,这不再是严格的要求。即使你有静态 C++ 有内联静态,你可以在 header.

中初始化静态

我建议坚持使用 17 或以上,并将所有内容保留在各自的 header。

如果你想使用 .cpp 或 .cc 或 .c 是因为你需要隐藏一个实现细节但是这样做你必须记住在你的项目中包含 .cc 例如使用命名空间保留在.cc 或包含第三方库。

知道如何制作库和链接、dll 也很好,但是这会变成更多的琐事,只需 header 就可以完成。但并不是说 headers 是灵丹妙药,静态库、dll 也不是。他们更喜欢你可以发布的不同口味。你也有 ipc 东西,比如共享内存和网络。但坚持亲吻(保持简单愚蠢)并在必要时进行优化。

此外,如果您定义了没有实现的方法,您将得到未定义的引用