שאלה לגבי קומפילציה של 2 קודים קצרים שונים

שאלה לגבי קומפילציה של 2 קודים קצרים שונים

1) מדוע בקוד המצ"ב לא מתקיימת דריסה של הפונקציה print ובמקום זאת יש שגיאת קומפליציה? לא הבנתי למה הגדרת ההרשאה כ-private מונעת פניה של אובייקט מסוג C לפונקציה print.

קוד:
#include <iostream>
using namespace std;
 
class A
{
public:
    void print() { cout << "A::print()"; }
};
 
class B : private A
{
public:
    void print() { cout << "B::print()"; }
};
 
class C : public B
{
public:
    void print() { A::print(); }
};
 
int main()
{
    C b;
    b.print();
}

2) לגבי הקוד הבא
קוד:
#include<iostream>
using namespace std;
 
class Base 
{
public:
    int fun()      { cout << "Base::fun() called"; }
    int fun(int i) { cout << "Base::fun(int i) called"; }
};
 
class Derived: public Base 
{
public:
    int fun(char x)   { cout << "Derived::fun(char ) called"; }
};
 
int main() 
{
    Derived d;
    d.fun();
    return 0;
}
לא הבנתי למה השורה
d.fun();
אינה חוקית כאן. הרי על פניו ההרשאה כאן אמורה לאפשר את זה.
 

BravoMan

Active member
פרטי זה פרטי!

1) המחלקה C אינה יורשת את A, ולא יכולה להשתמש במתודות שלך.

היא אומנם יורשת מ-B, אבל B יורשת מ-A בצורה פרטית, מה שאומר שרק B יודעת שהיא יורשת מ-A.
כל ה-"צאצאים" של B, לא יהיו מודעים ל-A, כפי שהם לא יהיו מודעים למתודות ואיברים פרטיים של B.

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

קוד:
class Derived: public Base 
{
public:
    using Base::fun;
    int fun1(char x)   { cout << "Derived::fun(char ) called"; }
};
 
ושוב תודה לך האחד והיחיד תרתי משמע


 
מתן ערכי ברירת מחדל לארגומנטים של templates - מתי כן ומתי לא

מדוע הקוד הבא כן מתקמפל
קוד:
#include<iostream>
using namespace std;
 
template<class T, class U = char>
class A  {
public:
    T x;
    U y;
    A() {   cout<<"Constructor Called"<<endl;   }
};
 
int main()  {
   A<char> a;  // This will call A<char, char>   
   return 0;
}

ואילו הקוד הבא לא מתקמפל
קוד:
#include<iostream>
using namespace std;
 
template<class T = char, class U, class V = int> class A  // Error
{ 
   // members of A
};
 
int main()
{
   
}
 

BravoMan

Active member
כי הקומפיילר לא יודע מה אתה רוצה מהחיים שלו!

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

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

תעצור ותחשוב על זה רגע - אם אתה כותב:
A<int, char> a;


איך הקומפיילר אמור לדעת אלו פרמטרים בדיוק אתה רוצה למלות?
האם זה T ו-U?
או אולי U ו-V?
 
גדול, תודה! שאלה נוספת:

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

BravoMan

Active member
לא.

השפה לא עובדת כך, ולמעשה גם לא שפות OOP אחרות שאני מכיר.
&nbsp
אין לך דרך לייצר מחלקה בלי לעבור ב-contructor כלשהו.
&nbsp
אתה לא חייב שה-constructor יאתחל איברים, אבל הוא בהכרח יופעל גם אם אינו עושה כלום.
 
ונניח שיש לי מחלקה A, ומחלקה B שלא חברה של מחלקה A

וגם לא מכילה אותה. האם אפשר בתוך מחלקה B לפנות מפורשות לתכונה של מחלקה A ע"י מחלקה A בעצמה?
 

BravoMan

Active member
לא מבין מה אתה שואל...

מה זה "לפנות מפורשות לתכונה של מחלקה A ע"י מחלקה A בעצמה"?
 

BravoMan

Active member
אתה רוצה משהו כזה:

קוד:
#include <iostream>

class A {
	public:
		static int answer; //must be static!
};

int A::answer; //memory allocated here.

class B {
	public:
		void setAnswer(int answer) {
			A::answer = answer; //use anywhere
		}
};

int main(int argc, char **argv) {
	B b;
	b.setAnswer(42);
	
	//                                              | this prints 'end of line'
	//use outside a class:                          V 
	std::cout << "The answer is: " << A::answer << std::endl;
	
	return 0;
}

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

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

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

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

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