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

שאלה על מחלקות בשפת C++


binyamin1001

Recommended Posts

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

אני פטרתי את זה ב"נו, הסביבה של Raphsody היא לא לפי התקן", ושכחתי מזה.

אחד מהחבר'ה בקורס, העלה את השאלה הזאת לרשימת התפוצה, ותוך כדי שיחה המליץ לי לראות שזה עובד גם בסביבה רגילה (VisualStudio, למשל).

הרצתי, והזדעזעתי.

יש למישהו הסבר?


#include<iostream>
using namespace std;
class A
{
int a;
public:
void print();
};
void A::print()
{
cout<<"Testing, for Science"<<endl;
}
int main()
{
A* p;
p->print();

system("pause");
}

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

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

איך נזהרים?

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

בשפות מתוחכמות יותר כמו ג'אווה ו-#C, כל משתנה חייב להיות מאותחל - ככה שאי אפשר שמצביע יהיה לא תקני, הוא רק יכול להצביע לאובייקט אמיתי או להיות NULL. בשפות כאלה, אם אתה מנסה להשתמש במצביע כזה אז תיזרק לך חריגה (exception) - בג'אווה זה נקרא NullPointerException וב-#C זה נקרא NullReferenceException.

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

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

את זה שזה עובד, אני יודע.

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

השאלה שלי היא - איך זה אפשרי?

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

עד כמה הרעיון הזה מדוייק?

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

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

כל דבר יכול לקרות. מאחר והפונקציה print אינה משתמשת ב-this ואין dereferencing ל-null pointer אז התוצאה היא כמצופה (או שלא...)

בכל מקרה אתה מבין שזה לא טוב לכתוב קוד כזה.

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

אני מניח שלא הובהרתי את עצמי.

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

אני אצמצם את השאלה:

האם זה קורה כי השורה

p->print();

מתורגמת לשורה:

print(&p);

?

(בגלל שהפונקציה עצמה מתורגמת מ:

A::print(){...}

ל:

print(A* this){...}

את החלק הזה אני די יודע, לא משער.

השאלה היא על החלק הראשון של התגובה)

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

ננסה להסביר בדרך טיפה שונה

אחד ההבדלים בין ++C ל #C וJava זה שבשאר השפות כל הפונקציות הן וירטואלית, ולכן לכל אובייקט אוטומטית נוצרת טבלת פונקציות (RTTI).

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

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

http://stackoverflow.com/questions/669742/accessing-class-members-on-a-null-pointer

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

ננסה להסביר בדרך טיפה שונה

אחד ההבדלים בין ++C ל #C וJava זה שבשאר השפות כל הפונקציות הן וירטואלית, ולכן לכל אובייקט אוטומטית נוצרת טבלת פונקציות (RTTI).

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

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

http://stackoverflow.com/questions/669742/accessing-class-members-on-a-null-pointer

תודה רבה על התשובה.

זה בדיוק מה שרציתי לשמוע.

שוב- תודה!

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

ארכיון

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

×
  • צור חדש...