Порядок определений классов в C++

У меня тут небольшая проблема. Я пытаюсь определить несколько классов, некоторые из которых являются игроками, а некоторые — пешками, принадлежащими игрокам. Исходя из Python, я привык иметь удобный доступ к игроку, владеющему пешкой, через пешку, а также к пешкам игрока через игрока. Поправьте меня, если я ошибаюсь, но в C++ это кажется невозможным.

В настоящее время я сначала определяю Player, и предполагается, что один из его элементов данных m_Pawns должен быть vector<Pawn>. Я объявляю элемент данных, но не присваиваю ему никакого значения. Я также определяю функцию-член, которая предназначена для назначения вектора пешек m_Pawns, но я не вызываю ее нигде рядом с конструктором. Поскольку на самом деле я не вызываю конструктор для Pawn в конструкторе для Player, кажется, все должно быть в порядке.

Вот мой Player класс. Класс Board определяется заранее, тогда как класс Pawn определяется позже (класс Pawn содержит указатели на владельца класса Player, поэтому его переключение не очень помогает).

class Player
{
public:
    Player(sf::Color color, const string& name);
    sf::Color GetColor();
    string GetName();
    void CreatePawns(Board& board, int which);
protected:
    vector<Pawn> m_Pawns;
    sf::Color m_Color;
    string m_Name;
};

Player::Player(sf::Color color, const string& name):
    m_Color(color),
    m_Name(name)
{}

sf::Color Player::GetColor()
{
    return m_Color;
}

string Player::GetName()
{
    return m_Name;
}

void Player::CreatePawns(Board& board, int which)
{
    switch(which)
    {
    case 1:
        for(int i = 0; i < 4; ++i)
        {
            m_Pawns.push_back(Pawn((*board).Cluster1[i], this*, m_Color));
        }
        break;
    case 2:
        for(int i = 0; i < 4; ++i)
        {
            m_Pawns.push_back(Pawn((*board).Cluster2[i], this*, m_Color));
        }
        break;
    case 3:
        for(int i = 0; i < 4; ++i)
        {
            m_Pawns.push_back(Pawn((*board).Cluster3[i], this*, m_Color));
        }
        break;
    default:
        cout << "Invalid player ID!\n\n";
        break;
    }
}

person GarrickW    schedule 16.06.2011    source источник
comment
использовать предварительное объявление ... см. эту ссылку www-subatech.in2p3.fr/~photons/subatech/soft/carnac/   -  person jRJ    schedule 16.06.2011


Ответы (4)


Если class Player идет первым, а class Pawn — позже, вы можете объявить указатель или ссылку только на более поздний класс (здесь Pawn). Вы не можете иметь объекты более позднего класса, например.

class Player {
  Pawn* p; // allowed
  Pawn& r; // allowed
  vector<Pawn*> p; // allowed
  vector<Pawn&> vr; // not allowed (reference are not copyable)
  vector<Pawn> o; // error !
};

class Pawn {};

Вы никак не можете преодолеть эту ситуацию, так как в C++ для класса non-template необходимо показать полное определение для объявления объектов.

Единственный выход — переформатировать код или использовать указатель/ссылку (с форвардным объявлением).

person iammilind    schedule 16.06.2011
comment
В вашем примере не отображаются ни указатели, ни ссылки, ни значение. Он объявляет вектор-к-... этого. Вы можете очень хорошо использовать предварительно объявленные имена типов в качестве аргументов шаблона. Например. один из способов реализации pimpl-типов: class FooImpl; class Foo { boost::shared_ptr<FooImpl> impl_; } - person Sebastian Mach; 16.06.2011
comment
Также: вектор ссылок невозможен. Ссылки не являются копируемыми типами. - person Sebastian Mach; 16.06.2011
comment
Я попытался перевернуть все так, чтобы Pawns определялись первыми и требовали только указателя на Player (как начальный член данных, а также в конструкторе), но я все еще получаю то же сообщение об ошибке. Должен ли я использовать опережающее объявление несмотря ни на что, даже с указателями? - person GarrickW; 16.06.2011
comment
@GarricW, да, предварительное объявление обязательно для использования указателя или ссылок в таком случае. - person iammilind; 16.06.2011
comment
Ладно, думаю, у меня все работает. Осталась только одна небольшая ошибка, которую я не могу исправить. Что не так с этой строкой? board является ссылкой, this* должен быть указателем на экземпляр Player, использующий функцию. m_Pawns.push_back(Pawn(board.Cluster2[i], this*, m_Color));. Я получаю expected primary-expression before ',' token и ту же ошибку с '('. - person GarrickW; 16.06.2011
comment
Я также пробовал альтернативу с идентичными результатами: Pawn newPawn(board.Cluster1[i], this*, m_Color); m_Pawns.push_back(newPawn); - person GarrickW; 16.06.2011
comment
Ответ по-прежнему неверен. vector<Pawn> отлично работает с предварительно объявленным Pawn. - person Konrad Rudolph; 17.06.2011

Класс Pawn все еще должен быть определен, чтобы скомпилированный мог создавать экземпляр вектора. Вы можете обойтись без хранения ссылок или указателей на объекты Pawn в вашем векторе вместо значений; вектор, например. В этом случае будет достаточно предварительного объявления класса Pawn.

person cababunga    schedule 16.06.2011

Вы можете переключить его, потому что вы можете перенаправить объявление Player, а затем Pawn может получить указатель на него.

Вы можете взять указатель на неполный тип. Вы не можете хранить значения этого типа.

person Puppy    schedule 17.06.2011

Вы можете сделать что-то вроде этого:

class Pawn;

class Player {
}

class Pawn {
}
person Amm Sokun    schedule 17.06.2011
comment
Также забыл упомянуть, что это должно быть сделано. Это называется предварительным объявлением. parashift.com содержит исчерпывающую ссылку на C++. - person Amm Sokun; 17.06.2011