פורסם 2009 באוקטובר 416 שנים שלוםאני עושה את המשחק דיגר, יש לי שני אובייקטים לוח ואוייב(בהמשך יוסף גם האובייקט של הדיגר)בכל אופן ללוח יש שדה של אוייב והאובייקט של הלוח דואג לשם אותו עליו.עכשיו באובייקט של האוייב יש את הפונקציה של התזוזה, עכשיו כדי הוא יוכל לזוז הוא צריך שתיהיה לו אינדיקציה לגבי מצב הלוחהאם יש לו קירות, יהלומים (וגם את הדיגר) עכשיו הבעיה שלי היא כזאתבקובץ board.h יש לי אינקלוד לenemy.h אך מצד שני בקובץ enemy.h יש לי אינקלוד לboard.hboard.h#ifndef BOARDH#define BOARDH#include "enemy.h"class Board{public: Board();private: Enemies *digger_Enemies ; int numEnemies;};#endifenemy.h#ifndef ENEMYH#define ENEMYH#include "board.h"class Enemy{public: Enemy(Board currB);private: int EnemyRow , EnemyCol; Board currentBoard;};#endifתודה לעוזרים
פורסם 2009 באוקטובר 416 שנים Forward declaration to the rescue!קודם כל, לא אמרת מה השגיאה שלך. חוץ מזה, לא הגדרת מה זה Enemies (אני מניח שהתכוונת ל-Enemy?)הפתרון הוא:א. לדאוג ש-Enemy יחזיק מצביע ל-Board.ב. במקום לעשות include, לעשות forward decleration, כלומר ב-enemy.h, לפני שאתה מגדיר את class Enemy, להוסיף את השורה:class Board;זה אומר לקומפיילר "קיים קלאס בשם Board, אבל אני עדיין לא אומר לך מהו", ואז זה מאפשר לקלאס להכיל מצביע אליו.
פורסם 2009 באוקטובר 416 שנים מחבר 1) לגבי הEnemies זה אכן אמור להיות Enemy2) אני לא יכול שיהיה לי שדה "רגיל" של לוח בEnemy , ואז בקריאה של הבנאי של ENEMY (זה יבוצע מאובייקט של הלוח) לשלוח לו את *this או שבגלל שזה אובייקט(והוא יחסית גדול בהמשך יהיו עוד שדות) אני צריך מצביע3) אם אני יוסיף את השורה איך הקומפילר ידע איפה קלאס הBoard נמצא?תודה על התגובה המהירה
פורסם 2009 באוקטובר 416 שנים 2. לא רק שאתה לא יכול (מבחינת הקומפיילר), אלא גם עדיף שלא (מבחינה לוגית). אם Enemy מכיל בתוכו משתנה מטיפוס Board, אז הוא צריך להכיל עותק של הלוח. כלומר כל אובייקט מטיפוס Enemy יכיל עותק משלו של הלוח, וזה לא הגיוני בשום צורה. צריך להיות רק אובייקט אחד מטיפוס Board, וכל האובייקטים מטיפוס Enemy יחזיקו מצביע אליו. 3. סמוך עליו כשאתה משתמש ב-forward declaration, אז בזמן הקומפילציה הקומפילר לא באמת צריך לדעת איך נראה הקלאס Board, הוא רק צריך לדעת שהוא קיים. הוא למעשה מסמן אותו באופן מיוחד, שאומר "אני יודע שקיים סימבול שנקרא Board, אבל אני לא יודע מהו עדיין". אחרי הקומפילציה מתרחש שלב הקישור (link), שם הקומפיילר מקשר בין כל הסימבולים השונים - הוא מחפש בין כל הקבצים המקומפלים סימבול שנקרא Board, ושם אותו במקום המתאים. אם הוא לא מוצא כזה (נניח ששכחת לכלול את ההגדרה של Board בפרוייקט שלך) אז הוא יצעק.
פורסם 2009 באוקטובר 416 שנים מחבר הסתדרתי אם הבעיה הראשונה אבל עכשיו יש לי בעיה חדשהכשאני רוצה להריץ מתוך הenemy את הפונקציה של BOARD ע"י המצביע הוא כותב לי error c2027 ממה שהבנתי יש בעיה בהגדרה של הקלאסככה הגדרתי את השדה בBoard בתוך הEnemyBoard *boardPtrובקובץ enemy.cpp אני מנסה להריץ את השורהboardPtr->draw()
פורסם 2009 באוקטובר 416 שנים מחבר אמרת לי שבמקום האינקלוד כותבים class Board; בכל אופן כרגע הקבצים נראים כךboard.h#ifndef BOARDH#define BOARDH#include "enemy.h"class Board{public: Board(); void draw();private: Enemies *digger_Enemies ; int numEnemies;};#endifהמימוש של draw זה הדפסות וזה עבד מקודם ככה שנראה לי שזה לא הבעיה בפונקציה הזו(אם יהיה צורך אני יעלה גם אותה)enemy.h#ifndef ENEMYH#define ENEMYHclass Board;class Enemy{public: Enemy(Board *currB); void f();private: int EnemyRow , EnemyCol; Board *currentBoard;};#endif[b]enemy.cpp[/b]#include "enemy.h"Enemy::Enemy(Board *currB){ currentBoard = currB;}void f(){currentBoard->draw(); //יש בעיה בשורה הזו בעיה שאובייקט הלוח לא מוגדר טוב}
פורסם 2009 באוקטובר 416 שנים בקובץ h אתה שם את class Board במקום האינקלוד.בקובץ cpp עדיין צריך את האינקלוד.שים לב להפרדה - בקובץ h, אתה מגדיר את Enemy כך שהוא רק צריך לדעת על קיומו של קלאס בשם Board, בלי לדעת איך הוא נראה ומה אפשר לעשות איתו. בקובץ cpp, אתה ממש משתמש בפונקציות של Board, ולכן אתה כן צריך לדעת איך הוא מתנהג, ולכן צריך לעשות שם אינקלוד.בכל מקרה, עדיף להימנע כמה שאפשר מלעשות אינקלודים בקבצי h ולשים אותם בקבצי ה-cpp במקום (כמובן זה עניין של שיקול דעת).
פורסם 2009 באוקטובר 416 שנים מחבר השתגעתי קצת מכל האינקלודים, עשיתי אינקלוד לבורד בקובץ הH של enemy וזה אכן מתקמפל מה שלא ברור לי עדיין בשביל מה אני צריך לעשות class Board; אם גם ככה אני יכתוב בקובץ ההדר או בקובץ הCPPאינקלוד לboard.h
פורסם 2009 באוקטובר 416 שנים זה הכל עניין של סדר. בוא נניח שיש לנו שני קלאסים, A ו-B, שכל אחד מהם מכיל בתוכו מצביע לאחר. A.h: #ifndef A_H#define A_Hclass A { public: A(); void f(); private: B* b;};#endif B.h: #ifndef B_H#define B_Hclass B { public: B(); void f(); private: A* a;};#endif ונניח שמוגדרים הקבצים הבאים שמממשים את הפונקציות של הקלאסים: A.cpp: #include "A.h"#include "B.h"A::A() { b = new B();}void A::f() { b->f();} B.cpp: #include "B.h"#include "A.h"B::B() { a = new A();}void B::f() { // do something} עכשיו, אתה אומר לקומפיילר לקמפל. שים לב שקבצי ה-h לא מתקמפלים, אלא רק קבצי ה-cpp (קבצי ה-h מתקמפלים דרכם, בעקיפין, באמצעות ה-include). הקומפיילר ניגש לקמפל את הקובץ A.cpp. קודם כל, הוא עושה include, כלומר הוא "מדביק" את כל הקוד שמופיע בקבצי h בתוך הקובץ. יוצא משהו כזה: class A { public: A(); void f(); private: B* b;};class B { public: B(); void f(); private: A* a;};A::A() { b = new B();}void A::f() { b->f();} כיוון שב-A.cpp עשיתי קודם כל אינקלוד ל-A.h ואז ל-B.h, אז ההגדרה של A מופיעה קודם. הקומפיילר מתחיל לקמפל לפי הסדר. הוא מגיע לקלאס A, ושם הוא מגיע לשורה: B* b; בעיה - הוא לא מכיר את הטיפוס B, ולכן לא יודע מה לעשות כאן, ולכן יזרוק שגיאה. מה הפתרון? להוסיף בתחילת A.h את השורה הבאה: #include "B.h" ואז ההגדרה של B תבוא לפני ההגדרה של A. אבל רגע.... אז הקוד (אחרי הכנסת האינקלודים) ייראה ככה: class B { public: B(); void f(); private: A* a;};class A { public: A(); void f(); private: B* b;};A::A() { b = new B();}void A::f() { b->f();} ושוב, נתקענו - הקומפיילר מגיע לשורה A* a; ושוב לא יודע מה לעשות. אם נשים ב-B.h אינקלוד ל-A.h רק נחזור למצב הראשון. לכן, הפתרון הוא forward declaration - לדאוג ש-A רק יידע שקיים קלאס בשם B (ולהיפך), ואז הבעיה תיפתר. נוסיף את השורה class A בתחילת B.h, ואת השורה class B בתחילת A.h, ואז אחרי האינקלודים הקוד ייראה ככה: class B;class A { public: A(); void f(); private: B* b;};class A;class B { public: B(); void f(); private: A* a;};A::A() { b = new B();}void A::f() { b->f();} ועכשיו הכל טוב ויפה - כשהקומפיילר מגיע לקמפל את הקלאס A, הוא כבר יודע שקיים טיפוס שנקרא B, ולהיפך. עכשיו, למה היינו צריכים לעשות ב-A.cpp אינקלוד גם ל-A.h וגם ל-B.h? נניח שהיינו עושים אינקלוד רק ל-A.h. אז הקוד היה נראה ככה: class B;class A { public: A(); void f(); private: B* b;};A::A() { b = new B();}void A::f() { b->f();} יש כאן בעיה - הקומפיילר יודע שקיים קלאס שנקרא B, אבל הוא לא יודע איזה פונקציות יש לו. הקומפיילר לא מסוגל לקרוא ל-b->f, כי הוא לא יודע שבכלל ל-b יש פונקציה שנקראת f. לכן צריך לעשות אינקלוד ל-B.h בכל מקום שבו משתמשים בפונקציות של B. נ.ב. מצטער על ההסבר הארוך, מקווה שעכשיו זה מובן יותר
פורסם 2009 באוקטובר 1116 שנים מחבר #ifndef BOARDH#define BOARDH#include "enemy.h"class Board{public: Board(); void draw();private: Enemies *digger_Enemies ; int numEnemies;};#endifיש לי עוד שאלה קצרהאיך אני מאתחל את השדה digger_Enemies* כאשר הבנאי של האוייב אמור לקבל מצביע ללוחהבנאי של האוייבEnemy(Board *currB); ניסיתי אם הרבה צורות אבל שום דבר לא עובדהגודל של המערך digger_Enemies אמור להיות בגודל numEnemies
פורסם 2009 באוקטובר 1116 שנים אפשר ליצור מערך דינמי רק של טיפוסים שיש להם בנאי דיפולטי (כזה שלא מקבל פרמטרים).בשביל זה יש לך שתי אופציות:תוסיף ל-Enemy בנאי דיפולטי, ופונקציית setBoard (שתקרא לה אחרי שתיצור את כל האובייקטים מטיפוס Enemy)או שתשתמש במערך של מצביעים ל-Enemy, ואז תאתחל כל אחד מהם בנפרד ע"י new (וכמובן תדאג אחר כך למחוק כל אחד בנפרד ע"י delete, בנוסף למחיקת המערך).(יש עוד פתרונות)
פורסם 2009 באוקטובר 1116 שנים מחבר בהנחה שאני אשתמש בפתרון הראשון זה אמור להיות משהו כזה:Board::Board(){ digger_Enemies = new Enemy()[numEnemies]; for(int i=0;i<numEnemies;i++) digger_Enemies[i].setBoard(this);}לא הבנתי כל כך את הפתרון השני אני מגדיר מערך של מצביעים לאובייקט מסוג אוייב Enemy **digger_Enemies ואז פשוט אני רושםBoard::Board(){ digger_Enemies = new Enemy*[numEnemies]; for(int i=0;i<numEnemies;i++) { digger_Enemies[i] = new Enemy(); digger_Enemies[i].setBoard(this); }}בנוגע להערה האחרונה שלך אתה מתכוון לדסטראקטור של הenemy, לשכתב אותו בגלל ההקצאות?ואיזה עוד פתרונות יכולים לפתור את הבעיה?
פורסם 2009 באוקטובר 1116 שנים הפתרון הראשון שלך כמעט נכון (לא צריך סוגריים אחרי Enemy, וכשיוצרים אובייקט ב-new עם בנאי דיפולטי אז לא שמים סוגריים).בחלק השני בלבלת את שני הפתרונות ביחד - אם אתה משתמש במצביעים, לא צריך בנאי דיפולטי ו-setBoard.במקרה השני, איפה שאתה עושה delete[] digger_Enemies, אתה צריך לדאוג קודם לעשות delete לכל אחד מהאיברים של המערך בנפרד.
ארכיון
דיון זה הועבר לארכיון ולא ניתן להוסיף בו תגובות חדשות.