בעיה עם שני הידרים שמכילים אחד את השני - C++ - תכנות - HWzone פורומים
עבור לתוכן
  • צור חשבון

בעיה עם שני הידרים שמכילים אחד את השני - C++


e-r-a-n

Recommended Posts

אני כרגע מתחזק שני קלאסים נפרדים (שאינם יורשים זה מזה) ובכל קלאס אני פעולות גם על אוביקט מהסוג השני.

כלומר כל קלאס עושה אינקלוד לשני.

נוצרת לי פה בעיה של forwarding, ואני קצת מתקשה להתמודד איתה.

האינקלודים שלי ממוקמים בקבצי הידר, ובכל אחד מהם וגם בקבצי סי.פי.פי (לא רוצה שהטקסט יתבלגן, לכן בעברית..) הוספתי גם את ההצהרה class X,

כאשר מדובר בקלאס השני.

לאחר ביצוע הפעולה הנ"ל, הכל עובד כשורה.

יש לי בנאי שמקבל מהסוג השני, ועובד, ובצורה דומה גם בקלאס האחר שלי.

בקלאס א' יצרתי אופרטור שמקבל כפרמטר אוביקט מקלאס ב', וזה עובד מעולה.

כעת אני רוצה לבצע את אותו האופרטור בקלאס ב'.

לצורך העניין האופרטור הוא +=.

בקלאס ב', היכן שאני כעת מעוניין לבצע זאת, הפעולה נכשלת ואני מקבל בתגובה את ההערה הבאה:


in member function "......:operator+=(...):
error:invalid use of incomplete type 'const struct A'
error: forward declaration of 'const struct A'

כך חוזר על עצמו עוד פעמיים, בכל מקום בו ביצעתי קריאה לאיזו מתודה של הצד השני.

אזכיר שנית כי בשני ההידרים קיים אינקלוד להידר האחר, וגם ההצהרה "קלאס ....".

קישור לתוכן
שתף באתרים אחרים

forward declaration עובד רק כשאתה מעביר מצביעים, ולא את הקלאס עצמו. כלומר, כזה דבר יעבוד:

class A;

class B {
void f(A* obj);
}

בעוד שכזה דבר לא יעבוד:

class A;

class B {
void f(A obj);
}

הסיבה היא שבמקרה הראשון, הקומפיילר לא צריך לדעת איך בדיוק נראית המחלקה A (כי הוא רק מעביר מצביע), ובמקרה השני הוא כן צריך לדעת.

אגב, יש סיכוי שזה דווקא כן יעבוד:

class A;

class B {
void f(A& obj);
}

שווה לך לבדוק. בכלל, בד"כ כשמעבירים אובייקטים לפונקציה מומלץ להעביר אותו באמצעות &, כדי למנוע שכפול מיותר.

נ.ב. להבא, תעלה דוגמת קוד. לא תמיד ברור מהניסוח מה בדיוק עשית, ובקלות יכול להיות שהשגיאה נובעת מטעות תחבירית אחרת.

קישור לתוכן
שתף באתרים אחרים

אני מעביר רפרנס (כדי למנוע שכפול מיותר)

כלומר אני נמצא באופציה 3 שהצעת.

ככה ביצעתי גם את הפונקציה המקבילה בקלאס האחר שלי, ושם זה עובד.

לצורך המחשה יותר עמוקה, המבנה הוא כזה:


/// B.h

#include ...
#include ...
#include "A.h"
class A;
class B {
private:
...

public:
....
B& operator+=(const A& other)
{
...
...
return *this;
}
...
};


/// A.h

#include ...
#include ...
#include "B.h"
class B;
class A {
private:
...

public:
....
A& operator+=(const B& other)
{
...
...
return *this;
}
...
};

בהידר של 'איי' זה עובד היטב, כפי שכבר ציינתי, אך בהידר של 'בי', אני מקבל הערה על ה

forward decleration of const struct A

ובמתודה עצמה של האופרטור חיבור שציינתי, אני מקבל 3 הערות על כל מקום בו ביצעתי קריאה דרך האובייקט פרמטר למתודה של הקלאס האחר - מתודות GET פשוטות ולא נסיון לכתיבה עליהם.

קישור לתוכן
שתף באתרים אחרים

אז תעלה את הקוד המלא (או לפחות את החלקים הרלוונטיים) ואת הודעת השגיאה המדוייקת. כולל מספרי שורות.

ועוד הערה קטנה - עדיף להימנע מאינקלודים מיותרים. אם כזה עשית forward declaration, אז אתה כנראה לא צריך את ה-include בכלל.

קישור לתוכן
שתף באתרים אחרים

החלקים החשובים בהידר הבעייתי:


#ifndef SPARSEMATRIX_H_
#define SPARSEMATRIX_H_
#include <cstdlib>
#include <vector>
#include <cassert>
#include "SparsePoint.h"
#include "Number.h"
#include "RegMatrix.h"
using namespace std;
class RegMatrix;

class SparseMatrix {
private:
vector<SparsePoint> _matrix;
void MergeClones();
int _numRow, _numCol;
public:
int GetColumns() const;
int GetRows() const;
const vector<SparsePoint>& GetMatrix() const;
SparseMatrix(int numRow, int numCol);
SparseMatrix(int numRow, int numCol, double fill[]);
SparseMatrix(const SparseMatrix &other);
SparseMatrix(const RegMatrix &other);
virtual ~SparseMatrix();
....
....
SparseMatrix& operator+=(const RegMatrix& other)
{
assert((_numCol == other.GetColumns()) && (_numRow == other.GetRows()));
int pos = 0;
for (int i = 0; i < _numRow; ++i)
for (int j = 0; j < _numCol; ++j)
{
SparsePoint p(i,j,(other.GetMatrix()).at(pos));
_matrix.push_back(p);
++pos;

}
MergeClones();
return *this;
}

...
...
};
#endif

רוב המתודות דיי ברורות מטבען, וניתן להבין מההגיון,

P הוא בעצם אובייקט שמחזיק מיקום ואוביקט מסוג מספר, שהוא פשוט מספר עם עוד כמה תכונות אחרות שאני צריך.

כל שאר המתודות במחלקה הזו פועלות.

השגיאה המתקבלת היא:


../SparseMatrix.h: In member function ‘SparseMatrix& SparseMatrix::operator+=(const RegMatrix&)’:
../SparseMatrix.h:47: error: invalid use of incomplete type ‘const struct RegMatrix’
../SparseMatrix.h:11: error: forward declaration of ‘const struct RegMatrix’
../SparseMatrix.h:47: error: invalid use of incomplete type ‘const struct RegMatrix’
../SparseMatrix.h:11: error: forward declaration of ‘const struct RegMatrix’
../SparseMatrix.h:52: error: invalid use of incomplete type ‘const struct RegMatrix’
../SparseMatrix.h:11: error: forward declaration of ‘const struct RegMatrix’

כאשר שורה 47: ה assert

שורה 11: ההצהרה class RegMatrix

שורה 52: האיתחול של P.

קישור לתוכן
שתף באתרים אחרים

סליח לי :-[

ללא ספק, הבעיה פה נודדת לה מקצה לקצה.

כעת הסרתי אינקלוד לספרס בהידר של רגולר, מאחר ועשיתי שם את ההצהרה על class SparseMatrix

אז כעת הקומפיילר מעיר לי על בעיות ב RegMatrix, עבור קונסטרקטור שמקבל Sparse.

ההערה תואמת לקודמתה.

חסכתי את הגזירה-הדבקה של כל מיני דברים ארעיים, וכדי למנוע עוד יותר בלבול העברתי לך בפרטי לינק לקבצים הספציפיים, לעיון נוח וקריא יותר..

כעת ראיתי את התגובה הנוספת:

א - טופל

ב - כן

ג - טופל ולכן העדכון שבתחילת ההודעה

קישור לתוכן
שתף באתרים אחרים

אומר שוב: איפה שאתה עושה forward declaration, אל תעשה אינקלוד.

חוץ מזה, למה יש לך מימושים של פונקציות בקובץ h? תממש רק בקובץ cpp (אלא אם אתה כותב פונקציות inline או template, ואז זה קצת יותר מורכב).

קישור לתוכן
שתף באתרים אחרים

את העניין עם ה forward declaration כבר סידרתי, כלומר כעת הוא לא מופיע יחד עם אינקלוד

לגבי איפה מימשתי מה, גם לי מעט תמוה, אבל ככה אני ועוד כמה מסביבי שמענו את המרצה אומר במהלך השיעור - שאת האופרטורים כדאי לממש בהידר.

יותר הגיוני שהכוונה הייתה רק לאלו שמוגדרים כ friend, לכן אני אתקן את זה כעת.

[br]פורסם בתאריך: 22.09.2010 בשעה 16:01:35


אוקיי, תיקנתי את מרבית אי-הסדר שהיה,

אך עדיין נותרתי עם אותה בעיה כמקודם..:


../RegMatrix.cpp: In constructor ‘RegMatrix::RegMatrix(const SparseMatrix&)’:
../RegMatrix.cpp:46: error: invalid use of incomplete type ‘const struct SparseMatrix’
../RegMatrix.h:9: error: forward declaration of ‘const struct SparseMatrix’
../RegMatrix.cpp:46: error: invalid use of incomplete type ‘const struct SparseMatrix’
../RegMatrix.h:9: error: forward declaration of ‘const struct SparseMatrix’
../RegMatrix.cpp:49: error: invalid use of incomplete type ‘const struct SparseMatrix’
../RegMatrix.h:9: error: forward declaration of ‘const struct SparseMatrix’
../RegMatrix.cpp:49: error: invalid use of incomplete type ‘const struct SparseMatrix’
../RegMatrix.h:9: error: forward declaration of ‘const struct SparseMatrix’
../RegMatrix.cpp: In member function ‘RegMatrix& RegMatrix::operator+=(const SparseMatrix&)’:
../RegMatrix.cpp:156: error: invalid use of incomplete type ‘const struct SparseMatrix’
../RegMatrix.h:9: error: forward declaration of ‘const struct SparseMatrix’
../RegMatrix.cpp:156: error: invalid use of incomplete type ‘const struct SparseMatrix’
../RegMatrix.h:9: error: forward declaration of ‘const struct SparseMatrix’
../RegMatrix.cpp:159: error: invalid use of incomplete type ‘const struct SparseMatrix’
../RegMatrix.h:9: error: forward declaration of ‘const struct SparseMatrix’
../RegMatrix.cpp:159: error: invalid use of incomplete type ‘const struct SparseMatrix’
../RegMatrix.h:9: error: forward declaration of ‘const struct SparseMatrix’

קישור לתוכן
שתף באתרים אחרים

:yelclap::xyxthumbs:

לא אוכל לתאר עד כמה אני מודה לך.

טכנית, הבנתי - ההצהרה על הקלאס בהידר, והאינקלוד ב .cpp, אבל לא מבחינה תיאורטית.

אני מגשש ברשת כדי לקבל הסבר רחב יותר, אבל בכל הקשור ל C\C++, מלבד cplusplus.com /cppreference לא מצאתי יותר מדי, וגם אלו יותר דומים ל API מאשר סוג של tutorial, כמו שקיים בג'אווה למשל.

קישור לתוכן
שתף באתרים אחרים

כשאתה עושה INCLUDE לקובץ H, אתה בעצם מביא את כל המחלקות שמוגדרות בו, את כל המתודות שמוגדרות בו ובפרט את כל המשתנים שמוגדרים במחלקה.

ברגע שהורדת את הinclude לקובץ הH, הקומפיילר, למעשה, לא מודע לכל הDATATYPES שמוגדרים במחלקה, ולכן הוא לא יודע כמה הוא צריך להקצות לך על המחסנית כשאתה שולח את האובייקט לפונקציה. אם אתה שולח מצביע, אז הוא מודע לגודל של המצביע (4 בתים בדרך כלל, תלוי במחשב שלך), ןלכן אין בעיה להעביר אותו.

קישור לתוכן
שתף באתרים אחרים

את כל מה שאמרת אני מבין, אבל לא התייחסת לעניין ההצהרה "class X"

האם כלל האצבע הוא כזה - בעת שיש לי שני הידרים שהאחד תלוי בשני וההפך, מצב שביצוע אינקלוד יצור סתירה כלשהיא, אז אני בעצם רק מצהיר על class X, וזה מחליף לי את האינקלוד, כלומר נותן לי גישה לשיטות וכד'?

מה שלא מסתדר לי עד הסוף הוא שאם אני בקובץ CPP אינקלוד ל H שלו, שהוא כבר הצהיר על class X, מדוע זה "לא תופס" ?

קישור לתוכן
שתף באתרים אחרים

ארכיון

דיון זה הועבר לארכיון ולא ניתן להוסיף בו תגובות חדשות.

×
  • צור חדש...