Ограничение включает в C ++ - PullRequest
0 голосов
/ 09 марта 2012

У меня возникают всевозможные проблемы с include-overload в моем новом проекте C ++, но я не уверен, как этого избежать.

Как избежать проблемы с необходимостью включения десятков классов, например, в сценарий загрузки карты:

Вот тривиальный пример класса Map, который будет загружать игровую карту из файла:

// CMap.h
#ifndef _CMAP_H_
#define _CMAP_H_
class CMap {
    public:
        CMap();
        void OnLoad();
};
#endif

// CMap.cpp
#include "CMap.h"
CMap::CMap() {
}

void CMap::OnLoad() {
    // read a big file with all the map definitions in it here
}

Теперь предположим, что у меня есть целое множество монстров для загрузки в мою карту, поэтому у меня может быть список или какая-то другая структура, чтобы хранить все мои определения монстров на карте

std::list<CMonster*> MonsterList;

Тогда я мог бы просто объявить «CMonster» в моем CMap.h и добавить в этот список столько монстров, сколько мне нравится

// CMap.h
class CMonster;

// CMap.cpp
void CMap::OnLoad() {
    // read a big file with all the map definitions in it here
    // ...
    // read in a bunch of mobs
    CMonster* monster;
    MonsterList.push_back(monster);
}

Но что, если у меня много разных типов монстров? Как мне создать много разных типов монстров, не включая каждый CMonster_XXX.h? А также использовать методы на тех?

// CMap.cpp
void CMap::OnLoad() {
    // read a big file with all the map definitions in it here
    // ...
    // read in a bunch of mobs
    CMonster_Kitten* kitty;
    kitty->OnLoad();
    MonsterList.push_back(kitty);

    CMonster_Puppy *puppy;
    puppy->OnLoad();
    puppy->SetPrey(kitty);
    MonsterList.push_back(puppy);

    CMonster_TRex *awesome;
    awesome->OnLoad();
    awesome->SetPrey(puppy);
    MonsterList.push_back(awesome);
}

Ответы [ 5 ]

3 голосов
/ 09 марта 2012

Вот правило, которое я использую для включения вещей.

  • Вперед объявляйте столько, сколько можете в заголовочных файлах.
  • включите любой .h вам нужно в .cpp
  • не включайте .h в другие .h, если не нужно.
  • Если ваш проект собирается без необходимости включать .h, у вас все в порядке. (в основном, если ваш компилятор достаточно совместим)

Редактировать: Кроме того, вы можете прочитать Крупномасштабный дизайн программного обеспечения C ++ . В нем говорится об управлении физическими файловыми зависимостями.

2 голосов
/ 09 марта 2012

Вы можете создать файл myMonstersInclude.h как

#include "myMonster1.h"
#include "myMonster2.h"
....

Вашему основному коду потребуется только выполнить `#include" myMonstersInclude.h ".

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

0 голосов
/ 09 марта 2012

Вы можете использовать заводскую функцию.В сочетании с глобальными статическими объектами для регистрации типов.Примерно так:

// in some main file...
typedef CMonster*(*create_ptr)();

std::map<std::string, create_ptr> &get_map() {
    // so we can make sure this exists...
    // NOTE: we return a reference to this static object
    static std::map<std::string, create_ptr> map;
    return map;
}

мы добавили немного кода для регистрации функции создания ...

// in each type of monster class (ex: CMonsterA)
CMonster *create_monster_a() {
    return new CMonsterA;
}

static struct monsterA_Registrar {
    monsterA_Registrar() {
        get_map().insert(std::make_pair("MonsterA", create_monster_a));
    }
} register_monsterA;

наконец, вернувшись в основной файл, вы можете создать монстримя его типа ...

std::map<std::string, create_ptr>::iterator it = get_map().find("MonsterA");
if(it != get_map().end()) {
    return (it->second)();
}

throw "invalid monster type requested";

Вот что происходит:

Когда программа запускается до main, она запускает все конструкторы глобальных объектовв данном случае register_monsterA является одним из них.

Конструктор этого объекта получит get_map() (это не может быть просто глобальный static, потому что у нас нет способа узнать, в каком порядке объекты инициализируютсяесли мы это сделаем, значит, это функция).

Затем он добавит к ней элемент, который является «функцией создания», в основном функцией, способной создать новый CMonster.

Наконец, чтобы сделать монстра, мы просто смотрим на ту же карту, получаем функцию создания и запускаем ее (если она была).


РЕДАКТИРОВАТЬ: Вот полный рабочий пример ... (с некоторой макро-магией, чтобы сделать его чище)

CMonster.h

class CMonster {
public:
    virtual ~CMonster() {
    }

    virtual void roar() = 0;
};

typedef CMonster*(*create_ptr)();

std::map<std::string, create_ptr> &get_map();

#define MONSTER_REGISTRAR(name) \
CMonster *create_monster_##name() { \
    return new C##name; \
}\
 \
static struct monster##name##_Registrar {\
    monster##name##_Registrar() { \
        get_map().insert(std::make_pair(#name, create_monster_##name));\
    } \
} register_monster##name;

CMonster.cc

std::map<std::string, create_ptr> &get_map() {
    // so we can make sure this exists...
    // NOTE: we return a reference to this static object
    static std::map<std::string, create_ptr> map;
    return map;
}

CMonsterA.cc

#include "CMonster.h"
class CMonsterA : public CMonster {
public:
    CMonsterA() {
        std::cout << "HERE - A" << std::endl;
    }
    virtual void roar() {
        std::cout << "A" << std::endl;
    }
};

MONSTER_REGISTRAR(MonsterA)

CMonsterB.cc

#include "CMonster.h"
class CMonsterB : public CMonster {
public:

    CMonsterB() {
        std::cout << "HERE - B" << std::endl;
    }
    virtual void roar() {
        std::cout << "B" << std::endl;
    }
};

MONSTER_REGISTRAR(MonsterB)

main.cc

#include "CMonster.h"

CMonster *get_monster(const std::string &name) {
    std::map<std::string, create_ptr>::iterator it = get_map().find(name);
    if(it != get_map().end()) {
        return (it->second)();
    }

    throw "invalid monster type requested";
}

int main() {

    CMonster *monster = get_monster("MonsterB");
    monster->roar();
    delete monster;
}
0 голосов
/ 09 марта 2012

Вопрос в том, должна ли ваша карта действительно знать обо всех отдельных типах монстров?Вероятно, нет - просто зная, что они получены из CMonster , должно быть достаточно, насколько это касается карты.Все методы, используемые вашим классом карты, должны работать с виртуальными функциями на монстрах, поэтому каждый тип монстров определяет свое специализированное поведение, а не карту.

Я подозреваю, что ваша проблема включения будет значительно уменьшенаправильно используя наследство здесь.

0 голосов
/ 09 марта 2012

Короткий ответ: Вы не можете.

Немного длиннее: вы можете создать файл заголовка, который будет #include другими, и включить новый файл заголовка в ваши файлы .cpp. Это по-прежнему эффективно, включая все заголовки, у вас просто нет дублированного списка включений, поэтому я сказал, что краткий ответ: нельзя.

Новый заголовок будет выглядеть примерно так:

#include CMonster_cat
#include CMonster_puppy
...
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...