5 / 5 / 0
Регистрация: 24.08.2011
Сообщений: 191
1

Как избежать циклического включения заголовочных файлов?

12.12.2014, 12:41. Показов 11133. Ответов 10
Метки нет (Все метки)

Author24 — интернет-сервис помощи студентам
Сделал в проге кучу классов и кучу указателей на них. В итоге при компиляции выдает кипу ошибок:
1>------ Build started: Project: DoorToAwake, Configuration: Debug Win32 ------
1> Object.cpp
1>d:\programming\visualstudio\projects\doortoawake\doortoawake\act.h(9): error C2143: syntax error : missing ';' before '*'
1>d:\programming\visualstudio\projects\doortoawake\doortoawake\act.h(9): error C4430: missing type specifier - int assumed. Note: C++ does not support default-int
1>d:\programming\visualstudio\projects\doortoawake\doortoawake\location.h(11): error C2061: syntax error : identifier 'Actor'
1>d:\programming\visualstudio\projects\doortoawake\doortoawake\location.h(16): error C2061: syntax error : identifier 'Actor'
1>d:\programming\visualstudio\projects\doortoawake\doortoawake\location.h(21): error C2061: syntax error : identifier 'Object'
1>d:\programming\visualstudio\projects\doortoawake\doortoawake\location.h(32): error C2065: 'Object' : undeclared identifier
1>d:\programming\visualstudio\projects\doortoawake\doortoawake\location.h(32): error C2059: syntax error : '>'
1>d:\programming\visualstudio\projects\doortoawake\doortoawake\location.h(32): error C2976: 'std::deque' : too few template arguments
1> c:\program files (x86)\microsoft visual studio 12.0\vc\include\deque(920) : see declaration of 'std::deque'
1>d:\programming\visualstudio\projects\doortoawake\doortoawake\actor.h(57): error C2143: syntax error : missing ';' before '*'
1>d:\programming\visualstudio\projects\doortoawake\doortoawake\actor.h(57): error C4430: missing type specifier - int assumed. Note: C++ does not support default-int
1>d:\programming\visualstudio\projects\doortoawake\doortoawake\actor.h(58): error C2065: 'Object' : undeclared identifier
1>d:\programming\visualstudio\projects\doortoawake\doortoawake\actor.h(58): error C2059: syntax error : '>'
1>d:\programming\visualstudio\projects\doortoawake\doortoawake\actor.h(58): error C2976: 'std::deque' : too few template arguments
1> c:\program files (x86)\microsoft visual studio 12.0\vc\include\deque(920) : see declaration of 'std::deque'
1>d:\programming\visualstudio\projects\doortoawake\doortoawake\actor.h(11): error C2512: 'std::deque' : no appropriate default constructor available
1>d:\programming\visualstudio\projects\doortoawake\doortoawake\actor.h(13): error C2065: 'currentObject' : undeclared identifier
1>d:\programming\visualstudio\projects\doortoawake\doortoawake\actor.h(33): error C2065: 'currentObject' : undeclared identifier
1>d:\programming\visualstudio\projects\doortoawake\doortoawake\actor.h(41): error C2065: 'currentObject' : undeclared identifier
1> DoorToAwake.cpp
1>d:\programming\visualstudio\projects\doortoawake\doortoawake\act.h(9): error C2143: syntax error : missing ';' before '*'
1>d:\programming\visualstudio\projects\doortoawake\doortoawake\act.h(9): error C4430: missing type specifier - int assumed. Note: C++ does not support default-int
1>d:\programming\visualstudio\projects\doortoawake\doortoawake\actor.h(28): error C2061: syntax error : identifier 'Location'
1>d:\programming\visualstudio\projects\doortoawake\doortoawake\actor.h(56): error C2143: syntax error : missing ';' before '*'
1>d:\programming\visualstudio\projects\doortoawake\doortoawake\actor.h(56): error C4430: missing type specifier - int assumed. Note: C++ does not support default-int
1>d:\programming\visualstudio\projects\doortoawake\doortoawake\actor.h(57): error C2143: syntax error : missing ';' before '*'
1>d:\programming\visualstudio\projects\doortoawake\doortoawake\actor.h(57): error C4430: missing type specifier - int assumed. Note: C++ does not support default-int
1>d:\programming\visualstudio\projects\doortoawake\doortoawake\actor.h(58): error C2065: 'Object' : undeclared identifier
1>d:\programming\visualstudio\projects\doortoawake\doortoawake\actor.h(58): error C2059: syntax error : '>'
1>d:\programming\visualstudio\projects\doortoawake\doortoawake\actor.h(58): error C2976: 'std::deque' : too few template arguments
1> c:\program files (x86)\microsoft visual studio 12.0\vc\include\deque(920) : see declaration of 'std::deque'
1>d:\programming\visualstudio\projects\doortoawake\doortoawake\actor.h(11): error C2512: 'std::deque' : no appropriate default constructor available
1>d:\programming\visualstudio\projects\doortoawake\doortoawake\actor.h(12): error C2065: 'currentLocation' : undeclared identifier
1>d:\programming\visualstudio\projects\doortoawake\doortoawake\actor.h(13): error C2065: 'currentObject' : undeclared identifier
1>d:\programming\visualstudio\projects\doortoawake\doortoawake\actor.h(33): error C2065: 'currentObject' : undeclared identifier
1>d:\programming\visualstudio\projects\doortoawake\doortoawake\actor.h(41): error C2065: 'currentObject' : undeclared identifier
1>d:\programming\visualstudio\projects\doortoawake\doortoawake\menu.h(57): warning C4060: switch statement contains no 'case' or 'default' labels
1>d:\programming\visualstudio\projects\doortoawake\doortoawake\menu.h(84): warning C4060: switch statement contains no 'case' or 'default' labels
Почти все или все они связаны с классом обжект. Раньше он у меня был только в .h файле. Перенес описание функций в .cpp файл, но ничего не помогло. Вот пример класса и файла, где выдает ошибку:

C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
#pragma once
#include "stdafx.h"
#include <string>
#include <deque>
#include "Actor.h"
#include "Act.h"
#ifndef MY_SYMBOL_H
#define MY_SYMBOL_H
 
//namespace lala {
 
    class Object {
    public:
 
        Object() {
 
        }
 
        //Call functions that realize interaction with this object
        virtual void Interact(Actor* actor) = 0;
 
        //Give to the location my description
        virtual std::string GetDescription();
 
        //Give to the location my action
        virtual std::deque<std::string> GetActions();
 
        //Add to object description and interact description
        void AddDescriptions(std::string objDescr, std::string actDescr);
 
    private:
 
        std::string objectDescription, actionDescription;
 
    };
//}
#endif //MY_SYMBOL_H
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#include "stdafx.h"
#include "Object.h"
 
 
//Give to the location my description
std::string Object::GetDescription() {
 
}
 
//Give to the location my action
std::deque<std::string> Object::GetActions() {
 
}
 
//Add to object description and interact description
void Object::AddDescriptions(std::string objDescr, std::string actDescr) {
 
}
В этом файле ругается на строку с объявлением указателя на обжект:
C++
1
2
3
4
5
6
7
8
9
10
11
#pragma once
#include <string>
#include "Object.h"
#ifndef MY_SYMBOL_HR
#define MY_SYMBOL_HR
 
struct act {
    std::string actionDescription;
    Object* interactiveObject;
};
#endif //MY_SYMBOL_HR
Пробовал библиотеки из .h переносить в .cpp - только новые ошибки появляются. Пробовал засунуть всё в неймспейс - пишет, что оно не существует. Я вообще не понимаю, что делать. Подскажете?
0
Лучшие ответы (1)
Programming
Эксперт
94731 / 64177 / 26122
Регистрация: 12.04.2006
Сообщений: 116,782
12.12.2014, 12:41
Ответы с готовыми решениями:

Про добавление заголовочных файлов в заголовочных файлах
В который раз эта вещь засовывает мозги в блендер! Я про то, что не могу однозначно запомнить...

Как избежать многократного включения модуля с помощью #include в проект?
Я пытаюсь реализовать консольный графический движок, разбитый на файлы по классам. Получается...

Как посмотреть содержимое заголовочных файлов в Си?
доброго. как посмотреть содержимое заголовочных файлов в си я про #include &lt;intrin.h&gt; те...

Как компилятору указать директорию заголовочных файлов.
Мне надо на Visual Studio 2010 Ultimate. Нужно указать директорию заголовочных файлов DirectX'а.

10
Эксперт PHP
3106 / 2591 / 1219
Регистрация: 14.05.2014
Сообщений: 7,236
Записей в блоге: 1
12.12.2014, 12:48 2
Defake,
Цитата Сообщение от Defake Посмотреть сообщение
act.h
Цитата Сообщение от Defake Посмотреть сообщение
location.h
Цитата Сообщение от Defake Посмотреть сообщение
actor.h
Цитата Сообщение от Defake Посмотреть сообщение
menu.h
Все эти файлы сюда. Для экономии места желательно под спойлеры исходный текст запихнуть.
0
18829 / 9832 / 2403
Регистрация: 30.01.2014
Сообщений: 17,270
12.12.2014, 14:37 3
Defake, у тебя циклическое включение заголовочных файлов (Object.h включает Act.h, а Act.h включает Object.h). Убери из Object.h включение Act.h.
0
5 / 5 / 0
Регистрация: 24.08.2011
Сообщений: 191
12.12.2014, 19:36  [ТС] 4
DrOffset, для этого и существует #pragma once. А если не включать Act.h в Object, то как его использовать? Но, собственно, убрал уже, т.к. понял, что там я акт не использую)) Но цикличности много еще кроме этого

Kerry_Jr, ну, файл Act.h уже в первом сообщении (самый последний)

Далее, файл Actor.h:

Кликните здесь для просмотра всего текста
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
#pragma once
#include "stdafx.h"
#include <map>
#include "Location.h"
#include "Object.h"
#include "Act.h"
 
class Actor {
public: 
 
    Actor() {
        currentLocation = NULL;
        currentObject = NULL;
    }
    
    /* Add some item to inventory */
    bool AddItem(std::string item) {
 
        return false;
    }
 
    /* Add a new memo to the memo list */
    bool AddMemo(std::string memo) {
 
        return false;
    }
 
    void ChangeLocation(Location* nextLoca) {
 
    }
 
    void InteractAct(int cursor) {
        if (!currentObject) {
            //Change the state of interctive object
        } else {
            //Call Interact function from an object
        }
    }
 
    void LookAround(std::string* description, std::deque<std::string>* actsDescr) {
        if (!currentObject) {
            //Ask object for description
        } else {
            //Ask current location for description and actions
            //Then, write action classes down the actionsArray
        }
    }
 
    void endInteraction() {
 
    }
 
private:
 
    int health;
    Location* currentLocation;
    Object* currentObject;
    std::deque<Object*> actionsArray;
    std::map<std::string, bool> inventory;
    std::map<std::string, bool> memos;
};


Файл Location.h:
Кликните здесь для просмотра всего текста
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
#pragma once
#include "Object.h"
#include "Act.h"
#include <deque>
#include <string>
 
class Location {
public:
    
    /* Get descriptions from all own objects and send it to actor */
    std::string GetDescription(Actor* actor) {
 
    }
 
    /* Get actions (objects) and send it back to the actor */
    std::deque<act> GetActions(Actor* actor) {
 
        //In the end the location say to all needed objects that one turn has passed
    }
 
    void Include(Object* object) {
        
    }
 
    void AddDescription(std::string descr) {
 
    }
 
protected:
 
    std::string description;
    std::deque<Object*> interactiveObjects;
    
 
    
 
};


Так, ну и Menu.h:
Кликните здесь для просмотра всего текста
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
#pragma once
#include "Location.h"
#include <deque>
#include <conio.h>
#include "Actor.h"
#include "Map.h"
 
#define UP 72
#define DOWN 80
#define RIGHT 77
#define LEFT 75
#define ENTER 13
#define ESCAPE 27
 
class Menu {
public:
 
    void startSession() {
        Update();
    }
 
    Menu() {
        gameActive = false;
        menuActive = true;
        station = 0;
        cursor = 0;
    }
 
private:
 
    bool gameActive; //Has game run at all?
    bool menuActive; //Menu turn on or off?
    int cursor, station, maxCursor;
    std::deque<std::string> actionsArray;
    Actor* actor;
    Map* map;   
 
    void Update() {
        char key = 0;
        while (gameActive || menuActive) {
            system("cls");
 
            //Change smth due to pressed key
            switch (key) {
            case ESCAPE:
                (menuActive && !gameActive) ? quitOutGame() :
                    (menuActive && gameActive) ? menuActive = false : menuActive = true;
                break;
            case UP:
                break;
            case DOWN:
                break;
            case ENTER:
                if (menuActive) {
                    switch (station) {
                        chooseMenuAction();
                    }
                } else {
                    //Actor interact with smth
 
                    //Get from actor new descriptions
                }
                break;
            default:
                printf("Fatal error with menu X-)");
                exit(0);
                break;
            }
 
            /* Draw certain points of menu due to the station or to the actor state
            (Send array of actions to OutputMaster) */
            
            key = _getch();
        }
    }
    
 
    void chooseMenuAction() {
        if (!cursor) {
            //exit out of the Menu
        }
        switch (station) {
 
        }
    }
 
    void cursorUp(int max) {
 
    }
 
    void cursorDown() {
 
    }
 
    void Save(char* fileName) {
 
    }
 
    /* Read init states from .sav file and creates map */
    void Load(char* fileName) {
 
    }
 
    /* Load game from base .sav file, creates start map and actor */
    void newGame() {
        
    }
    
    void quitOutGame() {
 
    }
};
0
18829 / 9832 / 2403
Регистрация: 30.01.2014
Сообщений: 17,270
12.12.2014, 20:07 5
Defake, эта тема наверное уже в миллионный раз здесь возникает.

Цитата Сообщение от Defake Посмотреть сообщение
для этого и существует #pragma once.
Нет, она для предотвращения включения дважды и более одного и того же файла в одну и ту же единицу трансляции. Магическим образом твои циклические включения разруливаться от этого не будут.

Цитата Сообщение от Defake Посмотреть сообщение
А если не включать Act.h в Object, то как его использовать?
Нужно понять что есть заголовочный файл и как его правильно использовать.
И что такое единица трансляции в С++.
А еще что такое предварительное объявление.

А пока что, я покажу простой пример на одном из твоих файлов, дальше, очень надеюсь, ты справишься сам
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
#pragma once
#include "stdafx.h"
#include <map>
//#include "Location.h"
//#include "Object.h"
//#include "Act.h"
 
class Location; // предварительные объявления
class Object;
 
class Actor {
public: 
 
    Actor() {
        currentLocation = NULL;
        currentObject = NULL;
    }
    
    /* Add some item to inventory */
    bool AddItem(std::string item) {
 
        return false;
    }
 
    /* Add a new memo to the memo list */
    bool AddMemo(std::string memo) {
 
        return false;
    }
 
    void ChangeLocation(Location* nextLoca) {
 
    }
 
    void InteractAct(int cursor) {
        if (!currentObject) {
            //Change the state of interctive object
        } else {
            //Call Interact function from an object
        }
    }
 
    void LookAround(std::string* description, std::deque<std::string>* actsDescr) {
        if (!currentObject) {
            //Ask object for description
        } else {
            //Ask current location for description and actions
            //Then, write action classes down the actionsArray
        }
    }
 
    void endInteraction() {
 
    }
 
private:
 
    int health;
    Location* currentLocation;
    Object* currentObject;
    std::deque<Object*> actionsArray;
    std::map<std::string, bool> inventory;
    std::map<std::string, bool> memos;
};
Если в каком-то из методов этого класса потребуется обращаться к методам Object или Location, то реализацию этих методов переносим в Actor.cpp и подключаем Location.h и Object.h туда.

Вот одна из последних тем, там есть пример в развернутом виде, который показывает что происходит при циклических include`ах.
1
5 / 5 / 0
Регистрация: 24.08.2011
Сообщений: 191
12.12.2014, 21:30  [ТС] 6
DrOffset, да, сейчас путем коммента всего и потом раскомментирования по кусочкам сам это увидел, вы были правы)

И еще пару вопросов:

1) В примере моего кода вы оставили:
C++
1
2
3
4
private:
    Location* currentLocation;
    Object* currentObject;
    std::deque<Object*> actionsArray;
Но файлы включения удалили, оставили только объявления классов. Т.е. это решает проблему?
C++
1
2
3
std::deque<Object*> InteractAct(int cursor) {
       
    }
Значит можно будет делать и так, как выше?

И если я буду использовать методы, нельзя вообще никак обойтись без .cpp? Просто неудобно очень)
0
18829 / 9832 / 2403
Регистрация: 30.01.2014
Сообщений: 17,270
12.12.2014, 21:56 7
Лучший ответ Сообщение было отмечено Defake как решение

Решение

Цитата Сообщение от Defake Посмотреть сообщение
И если я буду использовать методы, нельзя вообще никак обойтись без .cpp?
Можно, но это уже будет нетривиально и зависеть от конкретной ситуации.
Разносить определения и объявления функций в классе в любом случае придется.

Цитата Сообщение от Defake Посмотреть сообщение
Т.е. это решает проблему?
Смотри. Чтобы использовать тип, он должен быть объявлен. Мы это обеспечили через forward declaration. Избавились от зависимостей в виде заголовочных файлов в данном месте. Но. Совсем от них избавиться нельзя, потому что чтобы производить операции с объектами типа нужна знать его полное определение. Поэтому в месте, где нужно полное определение типа (создание объекта, вызов методов), все-таки их надо включить. Если два класса используют друг друга (что в принципе само по себе не очень, но это вопрос отдельный), то единственный способ разорвать рекурсию, это разнести использование друг друга по разным единицам трансляции, т.е. разным cpp.

Цитата Сообщение от Defake Посмотреть сообщение
Просто неудобно очень)
Это удобно. Есть такое довольно известное правило проектирования классов. Заключается оно в том, что по началу все методы реализуются в cpp, а в h остается только интерфейс. И только по результатам профилирования можно некоторые функции перенести в качестве inline в h. Это позволяет сделать код более локальным (без лишних зависимостей). Более локальный код всегда проще отлаживать.

На самом деле ты можешь не переносить все методы в cpp. Если задача позволяет избавиться от перекрестных включений без этого.

Добавлено через 11 минут
Цитата Сообщение от Defake Посмотреть сообщение
Но файлы включения удалили, оставили только объявления классов. Т.е. это решает проблему?
Я не увидел у тебя в коде использования объектов по этим указателям
C++
1
2
    Location* currentLocation;
    Object* currentObject;
Поэтому все так. Если оно планируется, то необходимо будет эти методы перенести в свой cpp и подключить туда заголовочные файлы с полными определениями этих классов (Object и Location). Это необходимо делать в том случае, если Object или Location тоже в, свою очередь, используют класс Actor. Т.е. если связь двунаправленная. Если Object и Location класс Actor не используют, то, очевидно, можно обходиться без такого разнесения для Actor. Хотя, я бы в этом случае все равно бы его сделал.
1
5 / 5 / 0
Регистрация: 24.08.2011
Сообщений: 191
12.12.2014, 22:21  [ТС] 8
DrOffset, просто первый язык программирования мой был ActionScript, это было в те времена, когда я еще почти ничего не знал о программинге, так что привычки и привязанности появились именно там. И там было всё как раз в одном файле.

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

Про неправильную структуру кстати да.. Буду думать, как исправить) Какое огромное облегчение, что необязательно все методы засовывать в cpp, а только те, которые используют другие классы) Спасибо за помощь!
0
18829 / 9832 / 2403
Регистрация: 30.01.2014
Сообщений: 17,270
12.12.2014, 23:50 9
Цитата Сообщение от Defake Посмотреть сообщение
Про переменные могли не объяснять второй раз, я просто уточнил, можно ли использовать их как возвращаемое значение методов.
А, я видимо просто вопроса не понял. Если это указатель или ссылка (или контейнер указателей), то можно. Правило в общем простое, forward declaration достаточно всегда, если не требуется знание о размере объекта этого типа. В случае с указателями и ссылками, размер не требуется (указатель на любые данные в С++ имеет размер sizeof(void*)), поэтому это работает. Если ты возвращаешь объект по значению, то тут нужен размер, а также знание о конструкторах и деструкторах, в этом случае потребуется уже полное определение типа, forward declaration недостаточно.
0
212 / 131 / 28
Регистрация: 20.03.2009
Сообщений: 1,123
Записей в блоге: 16
13.12.2014, 00:04 10
Для лучшего понимания объясню применимость предварительного объявления и инклюдов.
Для чего оба они нужны - в общем-то понятно. Когда компилятор видит в текстовом файле исходника слово MyClass, он должен понять, что это именно класс. Для этого он предварительно должен увидеть объявление этого класса - либо предварительное
C++
1
class MyClass;
либо полное
C++
1
2
3
4
class MyClass
{
//...
};
В некоторых случаях ему достаточно предварительного, в некоторых - обязательно нужно полное. Поэтому если соответствующий класс имеет полное объявление в другом файле, то вы должны либо заинклюдить этот файл (вторая ситуация), либо дать предварительно объявление класса(первая ситуация).

Когда требуется полное объявление? Обычно - тогда, когда мы создаем переменную этого класса (то же самое - передача аргумента функции по значение, возврат из функции по значению). В этих случаях нам необходимо знать размер такой переменной, чтобы выделить подходящее количество памяти. Простое предварительное объявление такой информации не содержит.

В полном же объявлении указаны все переменные-члены, все функции-члены, а также другие атрибуты, которые могут влиять на размер переменной данного типа (например, наличие виртуальных методов требует добавления в переменную указателя на vtable, который тоже занимает определенное количество памяти). Поэтому полное объявление, напротив, предоставляет всю необходимую информацию, чтобы высчитать sizeof для объектов данного класса.

Другой случай, когда предварительным объявлением не обойтись - когда вы вызываете функцию-член данного класса. Понятно, что компилятор должен вообще убедиться, что у класса есть функция-член с подходящей сигнатурой.

А вот когда нам вполне достаточно предварительного объявления. В сущности, только в одном случае - когда мы объявляем указатель или ссылку на объект данного класса. Размер указателя один и тот же, независимо от типа объекта, на который он ссылается, компилятору в этот момент достаточно информации о том, что это "указатель на что-то". Ровно то же справедливо и для ссылок. Однако, если вы вызываете по указателю/ссылке функцию-член, или обращаетесь по нему/ней к переменной-члену, то в силу вступает предыдущий абзац, и вам уже снова будет нужно полное объявление.

Небольшой пример на тему:
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
// Actor.h
#pragma once
 
class Scene;
 
class Actor
{
//...
Scene* Parent;  // Используем указатель на сцену - достаточно предварительного объявления
};
 
// Actor.cpp
#include "Actor.h"
#include "Scene.h"
 
Actor::Actor()  // Определяем функцию-член - обязательно нужно полное определение - инклюдим Actor.h
{
}
//...
void Actor::interact()
{
//...
if (/*в результате взаимодействия объект будет убран со сцены*/)
  Parent -> removeActor(this); // Вызываем функцию-член класса сцены - обязательно нужно полное определение
}
 
// Scene.h
#pragma once
 
#include "Actor.h"
 
class Scene
{
//...
void removeActor(Actor* actor);  // В принципе здесь нам бы хватило и предварительного объявления...
Actor getActorById(int id);  // Но эта функция возвращает полновесный объект класса Actor - мы должны знать его размер, соответственно, требуется полное объявление
//...
std::list<Actor*> Actors; // Здесь тоже хватило бы предварительного объявления
};
По поводу стражей включения - если есть вижуал-студийная #pragma once - то комбинация #ifndef/#define/#endif не нужна - она в точности дублирует эту прагму. Другое дело, что если вы не используете прагму (например, если работаете в другой IDE), то стражи включения должны обязательно быть в самом начале файла, весь код должен быть строго внутри них:
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
#ifndef MY_SYMBOL_HR
#define MY_SYMBOL_HR
 
#include <string>
 
class Object;
 
struct act {
    std::string actionDescription;
    Object* interactiveObject;
};
 
#endif //MY_SYMBOL_HR
Цитата Сообщение от Defake Посмотреть сообщение
Какое огромное облегчение, что необязательно все методы засовывать в cpp, а только те, которые используют другие классы
По-хорошему, нужно в обязательном порядке засовывать реализацию всех методов в cpp, кроме случаев действительной необходимости. Кроме указанных выше проблем с производительностью, когда по результатам замеров профайлером некие функции являются узким местом, и требуется сделать их инлайновыми, я сейчас даже не придумаю ситуации, когда бы стоило реализовывать функции прямо в заголовочном файле.
Ну кроме, конечно, шаблонов, но с ними совершенно отдельный разговор - по-другому просто не получится.

Цитата Сообщение от Defake Посмотреть сообщение
просто первый язык программирования мой был ActionScript, это было в те времена, когда я еще почти ничего не знал о программинге, так что привычки и привязанности появились именно там. И там было всё как раз в одном файле.
К сожалению, разные языки требуют разных подходов. Одна из особенностей C++ - разделение класса по крайней мере на два файла - *.h и *.cpp. В некоторых случаях возможно поместить все в один файл, но это будет плохой код на C++. В C# или в Pascal/Delphi, например, код тоже не делится на два файла. Это особенность этих языков.

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

P.S. Ну вот, опередили)
2
DrOffset
13.12.2014, 00:13     Как избежать циклического включения заголовочных файлов?
  #11

Не по теме:

Цитата Сообщение от Гром Посмотреть сообщение
P.S. Ну вот, опередили)
Ничего страшного. Я тебе плюс поставлю за развернутый ответ :)

0
IT_Exp
Эксперт
87844 / 49110 / 22898
Регистрация: 17.06.2006
Сообщений: 92,604
13.12.2014, 00:13

Раздельная компиляция (нюансы использования заголовочных файлов и файлов реализации)
Почему во многих учебниках функции-члены класса внутри класса только объявляют, но определяют вне...

не разберусь как переработать программу с учетом использования заголовочных файлов, модулей и пользовательских функций
#include &lt;iostream.h&gt; #include &lt;stdio.h&gt; #include &lt;conio.h&gt; #include &lt;stdlib.h&gt; #include...

Универсальный способ подключения заголовочных файлов, или как мне не ловить цикличное обьявление каждую компиляцию?
Я сейчас на пути создания игры, которая заполонит весь мир - в нее будут играть все, делать...

Подключение заголовочных файлов и файлов реализации
Здравствуйте,при создании класса в visual studio руками не компилится проект,при создании через add...


Искать еще темы с ответами

Или воспользуйтесь поиском по форуму:
11
Ответ Создать тему
Опции темы

КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin
Copyright ©2000 - 2024, CyberForum.ru