"no match for "operator []"

white shadow 3

New member
"no match for "operator []"

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

מה שעשיתי במהלך התוכנית זה הגדרתי איטרטור מהסוג של הוקטור שנתון לי כדי לרוץ על האיברים שלו.

חלק די גדול מהשגיאות שקיבלתי זה כאשר ניסיתי לגשת לתכולה של התא עליו מצביע האיטרטור ע"י
zz my vec [iter] zz
והשגיאות שקיבלתי על זה הן כפי שכתוב בכותרת
zz "no match for "operator []" zz
מה הכוונה? אני צריך להגדיר במחלקה שלי אופרטור [] ? (כמו שמגדירים אופרטור השמה לדוגמא)?

רוב תודות
 

BravoMan

Active member
חסרים קצת פרטים בשאלה שלך:

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

&nbsp
בנוסף, התיאור אינו ברור. למה שלא תדביק את קטע הקוד הרלוונטי ואת טקסט השגיאה המלא, כפי שאתה מקבל אותו מהקומפיילר.
&nbsp
אגב, אתה לא באמת שם רווח בשם משתנה כמו בדוגמה שכתבת כאן, נכון? my vec?
 

white shadow 3

New member
חלילה וחס לשים רווח!


זה יצא בטעות כי תיקנתי שם משהו באמצע וכנראה מתוך הרגל מגונה לחצתי על רווח

שפת התכנות Cּּּ++

מצרף בתמונה 2 אפשרויות שניסיתי ואת 2 השגיאות שקיבלתי (מציין כי השגיאה השניה כנראה ממשיכה ימינה, אבל אין לי אפשרות לגלול את המסך ימינה ולראות את ההמשך
)

אציין כי הוקטור עצמו מכיל פויינטרים לאובייקטים של המחלקה השניה ואני רוצה לקרוא לפונקציה של המחלקהB שנקראית (IsDead() על אובייקט של המחלקה B..

 

white shadow 3

New member
כרגע השגיאה שמופיעה אצלי (פעמיים) בקוד

היא השגיאה (1)
request for member "......" in ..."
&nbsp
שאני לא כ"כ מבין מה היא אומרת
 

BravoMan

Active member
הקומפיילר חושב שאתה מנסה להפעיל על אובייקט מסוג

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

BravoMan

Active member
אוקיי, אני חושב שהבאנתי מה ניסית לעשות.

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

כדי לגשת לתוכן התא דרך איטרטור, פשוט עושים לו dereference.

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

אם הבנתי נכון את המצב, הקוד הבא אמור לעבוד כפי שהתכוונת:
void A::RemoveDead(){ for(std::vector<A*>::iterator iter=myvec.begin(); iter!=myvec.end();iter++){
if(((*iter)->B::IsDead())==true){
delete[] *iter; myvec.erase(iter); } }}

שים לב לסוגריים סביב iter* - קודם עושים dereference, ועז מפעילים את הפונקציה על מצביע שקיבלנו.
אחרת, זה יצא הפוך - הקומפיילר ינסה קודם להפעיל את הפונקציה דרך <-, ואז לעשות dereference לתוצאה.

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

ועוד נקודה: האם בכל תא בווקטור שמור מערך אובייקטים? או רק מצביע לאובייקט בודד?
כי אם זה לא מערך לא צריך סוגריים מרובעות ב-dlete!
 

white shadow 3

New member
אכן בכל תא שמור מצביע אחד בודד ולכן הורדתי משם את הסוגריים

ואכן הוספת הסוגרים לפני *iter פתרה את הבעיה
&nbsp
תודה!
 

white shadow 3

New member
שאלת הבהרה קטנה על שימוש במשתנים של אובייקט בתוך פונקציה

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

האם כשאני בתוך הפונקציה עצמה, אני יכול להשתמש ב"תא האב" כדי להפעיל עליו פונקציות נוספות (לדוגמא, לקרוא לconstructor שהוגדר מראש (עם רשימת פרמטרים שהוא צריך לקבל) עם הארגומנטים של "תא האב")?
אציין כי הmemebers של "האב" הם intים ולא דברים מורכבים.

אם לא -אני מריח שנדרש כאן אולי copy-constructor
א. האם יש אפשרות לעשות את השכפול הזה בלי להשתמש בו?
ב. במידה ואני צריך לממש אחד, אז כשאני קורא לו דרך הפונקציה - אני צריך לשלוח אליו את *this (הפוינטר לאובייקט שעליו רצה במקור הפונקציה = "תא האב")?

(still on c++
)
 

פרסאוס

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

בדיוק בגלל זה הוא שם.
 

BravoMan

Active member
בוא נעשה קצת סדר:

1. זה שאתה משתמש ב-vector או באיטרטורים שלו, לא משנה את דרך העבודה עם האובייקט עצמו.
ברגע ששלפת אותו (ע"י dereference לאיטרטור או שימוש ב-[]) אין זה כלל משנה שהאובייקט (או המצביע אליו) מאוחסן ב-vector.
&nbsp
2. מתוך מתודה (לא סטטית) של אובייקט אתה יכול לקרוא לכל מתודה אחרת, פרטית או ציבורית, וגם לגשת ישירות לכל האיברים של אותו אובייקט עליו הפעלת את המתודה.
שים לב, שבנאים הם יוצאי דופן מהבחינה שהם נקראים רק פעם אחת ביצירת אובייקט, כלומר, אינך יכול להפעיל בנאי על אובייקט קיים כפי שאתה מפעיל את isReadyToClone, אבל אין שום בעיה לקרוא ל-new או ליצור משתנים מסוג המחלקה (הקצאה סטטית) בתוך המתודה.
&nbsp
3. כשאתה קורא למתודה אחת של האובייקט ממתודה אחרת שלו אין צורך להוסיף את שם המחלקה לשם המתודה.
אתה נמצא ב-scope של המחלקה והשם יזוהה לבד.
כלומר, אינך חייב לכתוב Bacteria::IsReadyToClone אלא מספיק IsReadyToClone.
&nbsp
4. לאור כל האמור, שני המימושים שלך יעבדו (אם תיישם בנאים מתאימים) ושניהם יעשו את העבודה תקין.
יש אפילו אפשרות למימוש נוסף שבוא יופעל בנאי ברירת מחדל ללא פרמטרים ואז תעתיק "ידנית" את ערכי השדות מאובייקט "האב" לאובייקט "הבן".
&nbsp
השאלה האמתית כאן היא, איזה מימוש יותר קריא וקל לתחזוקה.
זה אומנם רק תרגיל אבל לומדים כדי לפתח הרגלי תכנות נכונים לעבודה אמתית.
&nbsp
היות ומה שאתה עושה כאן, הוא ליצור עותק זהה לאובייקט, כדאי לעשות זאת עם בנאי העתקה.
בצורה כזו, אם אי פעם יהיה צורך לשנות את הדרך בה אובייקט מועתק, יהיה רק מקום אחד בקוד בו יש לבצע את השינוי.
אם הפונקציה Clone תשכפל אובייקט בצורה שונה, יהיו לך מספר מקומות בהם מתבצע שכפול, ולך תזכור היכן תיקנת והיכן לא.
&nbsp
זה כאמור ההסבר הארוך יותר לתשובה הקצרה והפשוטה של פרסאוס: "תממש בנאי העתקה".
&nbsp
בהצלחה!
 

white shadow 3

New member
תודה רבה!

היה לי ולעוד מישהו איזשהיא סוגיה לגבי מחיקת האובייקטים מסוג BACTERIA (אלו שהוקצו דינמית והפוינטר אליהם הושתל בתוך הוקטור)..אנחנו רוצים למחוק אובייקטים מסויימים של בקטריה וגם את התא במערך (או כל המערך) שהכיל את הפוינטר אליהם.
&nbsp
לגבי מחיקת אובייקט מסויים ואת התא הספציפי שלו במערך:
הרצנו איטרטור על המערך, הגענו לתא שבו יש אובייקטים שאנו רוצים למחוק. ביצענו
zz delete (*iter) zz ולאחר מכן zz erase (iter) zz.
(אני אדבר קודם כל הבעיה המוכללת לבעיה הזאת ולאחר מכן אתייחס לעניין של zz delete (*iter) zz שמשום מה לא עובדת)
&nbsp
לגבי מחיקת כל האובייקטים וכל המערך:
באופן דומה - איטרטור שרץ על פני כל התאים, לכל אחד מהם עושה zz delete (*iter) zz ובסיום הריצה מבצע clear לוקטור.
(אגב, הטענה שלו הייתה שאם נבצע רק clear אז כל המערך נמחק וכתוצאה מזה מוחק גם את האובייקטים של BACTERIA אבל אין כאן שימוש ב-delete להקצאה הדינמית אז לא נשמע לי סביר שבדרך קסם זה מוחק את האובייקטים של BACTERIA, ואולי הוקטור נעלם אבל נראה שהאובייקטים האלה עדיין "מרחפים" להם איפשהוא ולא משתחררים)
&nbsp
תיאור הבעיה:
רציתי לוודא שכאשר אני מריץ zz delete (*iter) zz זה באמת מוחק את האובייקטים, אז ניסיתי רק לבצע את פעולת ה-delete לפוינטר בלי לגעת בתא שבו הם נמצאים ולהדפיס את המצב הקיים:
&nbsp
* יצרתי כמה אובייקטים חדשים מסוג BACTERIA.
* הדפסתי (ע"י פונקציה שבניתי) את ערכי המשתנים שלהם ויוצא על המסך שאכן ישנה אוכלוסייה שערכיה תואמים את הנתונים שהזנתי עבור כל אובייקט.
* הרצתי את הפונקציה שמבצעת את ה-delete (*iter( וביצעתי שוב הדפסה של הנתונים.
ציפיתי שלא יודפס כלום כי התאים במערך אמורים להיות ריקים אבל משום מה אני מקבל את אותה תמונת מצב כמו לפני הרצת הפונקציה.
&nbsp
(אציין כי לא מיממשתי לאף אחת מהמחלקות destructor מיוחד כלשהוא כי סה"כ המשתנים שיש בהם הם לא מורכבים - לאחד יש מערך (שמכיל פוינטרים, אבל מערך) ולשני יש intים)
&nbsp
א. האם יש אולי סיבה שפספסנו ש-delete לא מבצע את המחיקה?
&nbsp
ב. עקרונית לגבי שינויים בגדול המערך תוך כדי ריצת איטרטור עליו - אנחנו רצינו שהאיטרטור יעבור איבר איבר, ואם ימצא אובייקט שמתאים למחיקה ימחק אותו ולאחר מכן (בתוך אותה לולאה) ימחק את התא וימשיך הלאה. האם הפעולה של "ימשיך הלאה" (כלומר ++iter בסוף ה-for) היא פעולה חוקית/נכונה/אפשרית אחרי שכיבול כבר מחקנו את התא הקודם שהאיטרטור היה עליו או שצריך קודם לעבור על המערך כולו ולמחוק את הפוינטרים ואז לעבור עליו שוב ולמחוק את התאים? (הדילמה הזאת קשורה גם לצד המשלים של הפעולה - והיא פעולת הוספת התאים, כלומר אם הגעתי לתא שבו יש אובייקט שאני רוצה לשכפל אז אני משכפל אותו ולאחר מכן עושה PUSHֹ_BACK לוקטור ושם שם את האיטרטור ואז מקדם את ה-iter++)
אישית ולוגית, המצב הזה נשמע לי מאוד בעייתי בשביל שיעבוד...
&nbsp
סוף שבוע מעולה!
&nbsp
&nbsp
&nbsp
 

white shadow 3

New member
אוקי, אני חושב שהגעתי לאיזשהיא מסקנה

אם אני עושה
delete על הפוינטר במערך אז כעת נראה שהוא כן מוחק את האובייקט אבל ככל הנראה שם בתא הזה (לפני שאני מוחק אותו) פוינטר לאיזשהוא זבל...(אם לדוגמא בהתחלה הכנסתי אובייקט משם 1 וגיל 5 אז אחרי ה-delete אני מדפיס את הנתונים של התא ורואה שם 0 וגיל 53015346134...

שיניתי את פונקציית המחיקה כך שהיא מקבלת פוינטר לבקטריה, מחזירה פוינטר לבקטריה ובסיום ה-delete עושה return NULL;.
(אם כי אני לא ממש בטוח בתקינות של הדרישה הזאת..)

חיפשתי קצת באינטרנט וראיתי שיש פונקציה remove שנראה שעושה את מה שאני מבקש לעשות - לעבור על הוקטור (בסיום ריצת האיטרטור ומחיקת הבקטריות) ולמחוק את התאים שלהם

;()zz vec.erase(std::remove(vec.begin(), vec.end(), NULL), vec.end

הבעיה היא שאני מקבל שגיאה על כך שאני משווה בין פוינטר לבין integer (?)
ישנה התאמה שאני יכול לעשות לנוסחא הזאת כדי שהיא תחפש פוינטרים לבקטריה מסוג NULL (בתקווה שהחזרה של ה-NULL תעבוד לי כי כרגע לא עושה רושם שהיא עובדת)
 

white shadow 3

New member
נמצא (ככל הנראה) הפתרון


תודה (מקווה שזאת הייתה שאלה אחרונה
)
#בלי נדר...
 

BravoMan

Active member
כתבת הרבה...

לא ממש הצלחתי לעקוב אחרי הכל, אבל ממה שהבנתי, יש לך בלבול קל לגבי מה עושה delete.
&nbsp
חשוב להבהיר: delete מבצע 2 פעולות בלבד:
1. מפעיל את ה-destructor (אם יש כזה) של האובייקט.
2. מסמן את הזיכרון ששיך לאובייקט כ-"פנוי לשימוש".
&nbsp
מה delete לא עושה?
הוא לא מאפס את הזיכרון או משנה את הערכים שלו בשום צורה! הוא גם לא מאפס או משנה אף משתנה שמחזיק מצביע לאותו זיכרון, כולל המשתנה עליו קראת ל-delete.
&nbsp
אם אתה עושה זאת מיד, אתה יכול להשתמש במצביע שכרגע עשית לו delete ולהדפיס את ערכי האובייקט ויש סיכוי טוב שהם יהיו תקינים.
יש גם סיכוי שלא, תלוי מה עוד הולך בתוכנה ובמחשב שלך.
&nbsp
הערכים האלה ידרסו רק בפעם הבאה שהזיכרון הזה יקצה לטובת אובייקט כלשהו, ומישהו יכתוב לשם ערכים.
אין לנצל התנהגות זו בתוכנית כי היא "לא צפויה". אינך יכול לדעת מתי ואלו חלקים של זיכרון ששחררת ידרסו.
&nbsp
אם אתה רוצה מסיבה כלשהי לאפס את הזיכרון תרם שחרורו, עליך לבנות destructor שעושה זאת, אם כי אין סיבה אמתית לטרוח רק כדי לאפס כמה איברים מסוג int.
&nbsp
הכלל בשפת ++C הוא שיש לאתחל כל משתנה, איבר, או בכלל כל זיכרון מיד אחרי הקצעה, ע"מ לוודא שלא עובדים עם "ערכים אקראיים". בפועל, הם אינם אקראיים, אלא הערך האחרון שנכתב לזיכרון בשימוש הקודם, לפני ששוחרר והוקצע מחדש.
 

white shadow 3

New member
הדרישה הייתה ש-

יש לשחרר את הזיכרון שהוקצה דינמית ברגע שכבר אין צורך בו, ולכן ברגע שהגעתי לתא במערך וגיליתי שהאובייקט שהפוינטר מצביע עליו כבר לא רלוונטי ("מת") הרצתי עליו delete כדי לשחררו
&nbsp
אני חושב שהשימוש ב-delete בעיתוי הזה (שבו גיליתי בצורה כזאת או אחרת כי האובייקט כבר לא רלוונטי) עונה בצורה מספיקה על הדרישה, נכון?
&nbsp
מבחינת תכנות נכון / אפשרי - האם ניתן להגדיר #define פעם אחת בקובץ .h שממוכל ב-include בשאר הקבצים בתוכנית , כדי ששאר הקבצים יוכלו לעשות בו שימוש?
(לדוגמא, אני רוצה להגיד שאם קורה מצב X אז המשתנה y=0.
עכשיו ההשמה "0=" מופיעה בכמה מקומות שונים בקוד (לאו דווקא עבור אותו משתנה y) וחשבתי פשוט להגדיר, נגיד:
zz #define original 0 zz
ובכל מקום שיש בו "0=" לשים "original="
&nbsp
&nbsp
&nbsp
 

BravoMan

Active member
לשחרר זיכרון, פירושו בדיוק לעשות delete

בהנחה שהזיכרון הוקצע ע"י new.

לעשות delete זה כל מה שדורשים ממך.

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

מצד שני, לעשות דבר כזה:
#define original 0

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

לו היה מדובר בערך מיוחד, מה שקרוי "magic number", שיכול להיות 1, 3, או 10- (או כל דבר אחר), והיה לו שם משמעותי, למשל HUNGRY_BACTERIA אז היה הגיוני להחליפו ב-define כדי שהקוד יהיה מובן יותר.

לדוגמה:
#define YONG 1#define ADULT 2
#define OLD 3
Bacteria a;a.setAge(YONG);


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

white shadow 3

New member
תודה רבה

שאלה קטנה רק על מנת לוודא שמה שאני עושה חוקי
&nbsp
אם במחלקה הראשונה שלי יש 2 חברים - *מערך של פוינטרים* (נניח נקרא לו vec1) למחלקה השניה ועוד משתנה int כלשהוא.
&nbsp
אם אני רוצה לבנות copy-constructor למחלקה הזאת, האם השורה:
zz vec1 (org_A.vec1) zz)
&nbsp
או השורה:
vec1 =another.vec1; (במימוש של אופרטור = )
&nbsp
נכונות (שתיהן עברו קומפילציה אבל זה לא אומר שהן נכונות
) ובאמת מבצעות את מה ששהפונקציה הזאת צריכה לעשות?
&nbsp
המשך שבוע טוב!
 

BravoMan

Active member
השאלה היא מה הוא הטיפוס האמתי של vec1

אם הוא מערך סטייל C, כלומר מוגדר כך:
Bacteria* vec1[10];

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

אם vec הוא משהו מורכב יותר, למשל vector, צריך לקרוא בתיעוד של המחלקה הרלוונטית איך היא מתנהגת בהפעלת אופרטור = (אם היא דורסת אותו בכלל).
http://www.cplusplus.com/reference/vector/vector/operator=/
כפי שתוכל לראות, מחלקה vector הסטנדרטית של ++C ממשת אופרטור השמה בצורה שהוא מעתיק את כל האיברים מ-vector אחד לשני, כך שנוצר עותק של ממש ואין בעיה להשמיד את המקור.
 

white shadow 3

New member
הוא מוגדר תחת מחלקה 1 כוקטור כך:

zz std::vector<Bacteria*> vec1 zz
(כאמור Bacteria היא מחלקה 2 לצורך העניין)
&nbsp
זה אמור לענות על החלק השני של התגובה, ואם הבנתי נכון אז הפעולה שתתבצע ב-2 השורות שכתובות בcopy constructor ו-אופרטור "=" אמורות לבצע את מה שהתכוון המשורר, לא?
 

BravoMan

Active member
ושוב - התיעוד הוא ידידך הטוב!

++C היא שפה מורכבת והספריות הסטנדרטיות שלה עוד יותר מורכבות.
אי אפשר להחזיק הכל בראש, אבל למזלנו זו גם שפה מתועדת להפליא, עם ספרייה סטנדרטית מתועדת להפליא.
&nbsp
אם אתה תוהה כיצד תתנהג פונקציה מסוימת של מחלקה מסוימת, כל שעליך לעשות הוא לקרוא בתיעוד.
http://www.cplusplus.com/reference/vector/vector/vector/
כפי שאתה יכול לראות, ל-vector יש גם copy constructor, אז גם התשובה לשאלה ראשונה שלך היא "כן".
&nbsp
אבל יכולת לקבל אותה בצורה מהירה וודאית יותר אם היית פשוט קורא בתיעוד.
(לא שאני חלילה מנסה להניע אותך מלשאול שאלות בפורום, תשאל כמה שאתה רוצה, פשוט ללמוד לקרוא תיעוד זה מאוד שימושי ונוח, וחשוב לי להדגיש את זה)
 
למעלה