שליחת מצביע ב c++

חן119

New member
שליחת מצביע ב c++

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

למה אני מקבלת טעות כשאני מנסה לשלוח אל הפונקציה head (שורה 11 ב main) ?...

ואז במימוש הפונקציות כשאני מנסה להקצות זיכרון לאותו המצביע?
לא כך ולחים מצביע ב c++?
 

פרסאוס

New member
עצה לריבוי קבצים

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

חן119

New member
אני לא יודעת אם לזה התכוונת

אבל אני מניחה שלא, כי יצא ג'יברישי-משהו
 

BravoMan

Active member
כן, זה בדיוק מה שפרסאוס התכוון ועכשיו הרבה

יותר קל להבין מה עשית (כי בקבצי DOC אני לא נוגע, יותר מידי צרות איתם!).

הבעיה היא, שהגדרת את הפונקציה CreateNode לקבל "רפרנס" למשתנה מסוג MyPoint ולא מצביע.

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

ואת לא שולחת מצביע בתוך main אלא את המשתנה עצמו עליו head אמור להצביע (רק שהוא NULL, כלומר את מנסה לשלוח משהו שלא קיים).

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

ולסיום: את מנסה בתוך הפונקציה לבנות אובייקט מסוג MyPoint ללא פרמטרים, אבל למחלקה MyPoint אין בנאי כזה.
את חייבת להשתמש בבנאי שמקבל פרמטרים! (לצורך העניין a ו-b).

הנה דוגמה איך מעבירים מצביע לפונקציה:

void function(int *number);

int main() {
int *p_number;
p_number = new int;

function(p_number);

return 0;
{


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

חן119

New member
אהה...אתה מעלה פה כמה נקודות טובות

אבל עדיין טיפה לא ברורה לי נקודה אחת (וסליחה שאני מלאה אותך):

אם אני כן רוצה לשמר את ההגדרה של האבטיפוס ככזאת: MyPoint * CreateNode(MyPoint &head,double a,double b
וב main אני מגדירה מצביע ל Mypoint שאקרא לו head

אז כשאני קוראת למתודה, אני צריכה לעשות כמוך וקודם להקצות מקום ל head?
אני לא יכולה לשלוח מצביע למתודה הזאת? לא כדאי לי לאתחל אותו בכלל?
להגדיר פרמטר של מתודה באבטיפוס ככזה: x& ,לא שקולה ללהגדיר אותו כך: x* ?

בקיצור מה שעדיין לא מובן לי זה: איך אני יכולה לשלוח את head למתודה שנמצאת במימושים?
 

BravoMan

Active member
השאלה האמתית היא, מה הפונקציה הזו

אמורה לעשות בעצם?

וספציפית, למה היא צריכה את head?

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

(שימי לב כי מצביע גם יכול להצביע לכתובת NULL - שהיא כתובת "שום מקום", כלומר יש משתנה מסוג מצביע, אבל עדיין אין לו על מה להצביע)

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

כלומר, אם יש לך פונקציה שנראית כך:

voidn func(int &x) {
x = 5;
}

ואז תקראי לה מ-main כך:
int main() {
int a;
funct(a);

return 0;
{


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

x אינו הכתובת של a ולא משתמשים באופרטור * (כוכבית) כדי להגיע לתוכן.
x פשוט הופך להיות לשם נרדף ל-a ואת משתמשת בו כאילו היה a בעצמו.

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

חן119

New member
אה! הבנתי אותך! עכשיו מובן לי ההבדל!

יש לי רק עוד בעיה אחרונה:
לפי התרגיל שקיבלנו (צירפתי אותו, הוא בע"מ 3) אני אמורה למחוק את הרשימה בסופו של דבר, אבל קשה לי לתפוס איך אני אמורה ליישם את זה
1. לפי איך שאני בניתי את המחלקה, אין לי head, ולכן אני לא יודעת מה אני אמורה למחוק בעזרת ההורס
2. אסור לי לעשות שינויים ב"שלד" שהמרצה צירפה, אני אמורה להשלים את התוכנית לפיו.
3. האם אחרי השימוש בהורס, יש צורך בנוסף גם למחוק את המצביע שהגדרתי ב Main (למרות שהוא ראש הרשימה שאותה אני מוחקת בהורס)?

עניין ההורס באופן כללי עדיין לא הכי מובן לי: מתי ההורס יופעל? רק אחרי שיוצאים מה main?
 

BravoMan

Active member
אם יש משהו גרוע יותר מ-DOC, זה PPT.

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

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

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

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

תאורטית, אפשר לעשות את מה שאת עשית: כשמשחררים איבר ברשימה הוא מנסה למחוק את האיבר הבא וכך כל הרשימה נמחקת רקורסיבית, אבל זה ממש לא מומלץ ולא כך מטפלים ברשימות מקושרות.

(אגב, תוסיפי שם בדיקה ש-Next אינו NULL)

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

אם זה אובייקט שהוקצה דינמית (ע"י new) חייבים לעשות לו delete.

כאמור ה-dtor נקרא כאשר עושים delete או כאשר הפונקציה מסתיימת.

בצורה שבנית כעת את המחלקה, את יכולה לעשות delete ל-head וזה יגרום לתגובת שרשרת שתמחק את כל הרשימה.

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

חן119

New member
אה, אוקי סליחה, אז אני צרף את זה כאן:

"כתוב תכנית שיוצרת ומדפיסה רשימה מקושרת של נקודות.
התכנית משתמשת במחלקת MyPoint. הגדר את המחלקה ואת השיטות שלה – הבנאי, שיטת הצגת נקודה ושיטת חישוב מרחק.
בנוסף, מחוץ למחלקת MyPoint, כתוב פונקציה שמקבלת מצביע לראש הרשימה המקושרת ושיעורים של נקודה. הפונקציה יוצרת אובייקט של מחלקת MyPoint, מקשרת את הנקודה החדשה לרשימה ומחזירה את ראש הרשימה המעודכנת.
ה-prototype של הפונקציה: MyPoint* CreateNode(MyPoint &head, double a, double b)
התכנית יוצרת רשימה מקושרת של 4 נקודות בעזרת הפונקציה CreateNode ומדפיסה את איברי הרשימה
בסוף התכנית נמחקת הרשימה מהזיכרון תוך הצגת הצומת הנמחק."

צריך היה להשלים את ה"שלד" הבא:

#include “lab11_12_2_header.h"
using namespace std;

void main(){
MyPoint *head=…. , *temp;
for(int i=1; i<4; i++){
double a, b;
cout<<"\nInsert 2 coordinates:"; cin>>a>>b;
head=CreateNode(…..);
}
temp=head;
……
for(int i=0;i<4;i++){
cout<<"the deleted point was "; ……; cout<<endl;
…….
{
{



זה ההדר
#include <iostream>
#include<math.h>
using namespace std;

class MyPoint{
double x,y;
public:
…..
};


MyPoint* CreateNode(MyPoint &head, double a, double b);
 

BravoMan

Active member
כדאי שתצרי קשר עם המורה \ מרצה שכתב את זה

ותבדקי, כי נראה שנפלה כאן טעות: המלל של התרגיל לא תואם את הגדרת הפונקציה.

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

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

יכול מאוד להיות שזו טעות הקלדה או משהו (הבדל של תו אחד - * במקום &), כדאי לבדוק לפני שמסתבכים.
 

חן119

New member
אוקי, אני באמת אשאל אותה בשיעור :)

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

פרסאוס

New member
כמה הערות

ראשית לסימון & יש משמעות מעבר ליחוס והיא "כתובתו של".
יתכן ופה מקור הטעות בהגהה.
כמובן, כמו שציין BravoMan, הטעות היא טעות לאורך כל הדרך והיא ממש לא הגיונית.
(כדי לייצר ייחוס שכזה למשתנה שהוא מצביע צריך לבטל את ההצבעה. למה זה שימושי?)
בנוסף, אם הרשימה נמחקת, איזה צומת היא רוצה להציג?
שלישית, אלא אם יש סיבה להכניס את הרשומה החדשה בראש הרשימה, הרי שראש הרשימה נשאר קבוע.
אם יש צורך במיון הרשימה לפי פרמטרים מסויימים, הם לא מוזכרים בשאלה.
מבלי להכיר את המתרגלת, התרגיל מבולבל וחסר.
 

חן119

New member
חחחחח...הערנו לה בנושא כמה פעמים אבל זה

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