עבור לתוכן

עבודה עם טמפלט וקבצים בC++

Featured Replies

פורסם

היי,

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

לדוגמא כאן:

template<class T> 
T CFile<class T>::read() const
{

}

איך אני אומר למשתנה fstream שלי לקרוא T בודד? אם זה INT, STRING או אפילו CLASS שמישהו כתב ועכשיו רוצה להשתמש בREAD שלי...

תודה.

פורסם

לא כל כך הבנתי מה אתה רוצה לעשות. אתה רוצה לממש את הפונקציה read, או שאתה רוצה להשתמש בה?

ניסית להשתמש באופרטור >> ?

פורסם
  • מחבר

לממש..

המחלקה נראת ככה:

template<class T> 
class CFile{
private:
char *_uri;
fstream _file;
int _begin;
int _end;

public:
CFile(const char *uri)
{
strcpy(_uri,uri);

_file.open(_uri , ios::in | ios::out | ios::app | ios::beg);

if(_file.is_open())
{
_begin = _file.tellg();
_file.seekg(0,ios::end);
_end = _file.tellg();
}
}

~CFile()
{
_file.close();
}

/*reads from the file a single T*/
T read() const;

};

אם אני אעשה << איך ה _file ידע לקרוא את האובייקט שאני אתן לו ולהוציא ממנו את המידע הרלוונטי.

ואותו דבר ההפך, איך fstream ידע לטפל ב>> לתוך מחלקה שהוא לא מכיר, כלומר איך הוא ידע כמה לקרוא.

לדוגמא אם זה INT אז הוא יודע שהוא צריך לקרוא עד הרווח הבא. ואם זה STRING אז זה עד סוף השורה.

או האם אני טועה. האם כל GET מביא את המידע עד הרווח הבא?

פורסם

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

T x;
_file >> x;
return x;

כמובן, זה מטיל דרישות על T - לטיפוס חייבים להיות מוגדרים בנאי ברירת מחדל, פונקציית <<operator מתאימה (כמו שמגדירים >>operator עבור הדפסה), ו-copy constructor על מנת שתוכל להחזיר אותו מהפונקציה.

נ.ב. יש לך באג בבנאי - אתה מעתיק מ-uri ל-uri_, בלי שאתה מאתחל את uri_ להצביע לשום מקום. בכלל, למה לך להשתמש ב-*char ולא פשוט ב-string?

פורסם
  • מחבר

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

בכל אופן אני נתקל בשגיאה

Error	1	error C2649: 'typename' : is not a 'class'	

למרות שכמו שאפשר לראות כאן אני כן עושה template<class T> בתחילת הגדרת הפונקציה

template<class T> 
T CFile<class T>::Read() const
{
T obj;

if(_file.is_open())
{
_file >> obj;
}

if(_file.fail())
{
cout << "error while reading file." << endl;
return null;
}
else
{
return obj;
}
}

פורסם

בטמפלייט משתמשים ב-typename ולא ב-class

חוץ מזה, איך אתה יכול להחזיר null בתור T? רק מצביע יכול להיות null.

פורסם
  • מחבר

איזה מוזר בכל המקומות שקראתי על טמפלייט הם כותבים עם CLASS.

זה לא כמו C# ? ;D

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

עריכה:

ומה עם כתיבה לקובץ.

כשאני עושה

obj >> _file;

המימוש של << לא מתבצע אצל FSTREAM?

פורסם

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

fstream עובד בדיוק כמו cin/cout. בדיוק כמו שאתה מדפיס ל-cout, ככה מדפיסים לקובץ (במקום cout תכתוב file_). כמו שציינתי קודם, זה דורש שיהיה >>operator מוגדר עבור הקלאס שלך.

כשמתרחשת שגיאה, הדבר הנכון לעשות הוא לזרוק חריגה (excpetion). מי שקורא לפונקציה read צריך לעטוף אותה ב-try/catch ולטפל בחריגה בהתאם. מבחינת תכנות מונחה עצמים, הקלאס שלך לא צריך להיות אחראי להדפיס הודעת שגיאה - הוא רק צריך לדווח על שגיאה, באמצעות חריגה. מי שתופס את החריגה צריך להחליט אם הוא רוצה להדפיס הודעת שגיאה מתאימה, ואם כן אז לאיפה (למסך, לקובץ וכד').

אם אתה בכל זאת רוצה להיות מסוגל להחזיר ערך ברירת מחדל במקרה של שגיאה, הדבר הנכון לעשות הוא ש-read תחזיר *T ולא T. אבל אז תצטרך להקצות אותו דינמית (new), ומי שמשתמש בו יהיה אחראי לשחרר אותו (delete).

פורסם
  • מחבר

הבינותי.

תודה. :xyxthumbs:

פורסם
  • מחבר

עוד דבר קטן

T Read() const throw(CError);

הCONST בשורה אומר שאני לא משנה שום אובייקט במחלקה נכון?

הוא לא קשור לTHROW או משהו נכון?

פורסם

אין קשר לחריגה אבל כן יכולה להיות לך בעיה עם הקובץ שאתה קורא כתלות במימוש של המלקה שבפועל קוראת ממנו (ה type של _file במקרה שלך).

ברגע שאתה קורא מקובץ אתה משנה את האוביקט שעוטף לך את הקובץ. מכיוון ש CFile שלך מחזיק _file אז למעשה אתה עשוי כבר לא להיות const.

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

_file.is_open()

הוא const אז אתה בבעיה. (חרא של דוגמה כי is open היא בערך הפונ' היחידה שהיא כן const לפחות ב std::fstream).

אם אתה שולט בהגדרות של הפונ' אז אין לך בעיה אבל אם אתה משתמש בספריות כתובות (כגון namespace::std) אז אתה יכול להיות בבעיה.

פתרון אפשרי אחד במידה ויש תלונות מצד המהדר הוא להגדיר כאלו משתנים כמו _file כ mutable. אל תשתמש יותר מדי כי אז זה אומר שתה מפספס את הפואנטה של const.

פורסם
  • מחבר

נשמע הגיוני.

עכשיו אני מקבל שגיאה אחרת:

Error	1	error LNK2019: unresolved external symbol "public: void __thiscall CFile<class CElement>::Write(class CElement const &)" (?Write@?$CFile@VCElement@@@@QAEXABVCElement@@@Z) referenced in function _main	main.obj
Error 2 fatal error LNK1120: 1 unresolved externals

שכאן אין ממש ברירה אלא להוסיף כמעט את כל הקוד:

main.cpp

void main()
{
try
{
CElement e1(3);
CFile<CElement> file("d:/s.txt");

file.Write(e1);
}
catch (CError &ex)
{
cout << ex.getError();
}
}

cfile.cpp

/*write into the file a single T*/
template<typename T>
void CFile<typename T>::Write(const T& t) throw(CError)
{
if(apBuf == null)
{
throw CError("undefined object - null object.");
}

if(_file.is_open())
{
_file << t;
if(_file.fail())
{
throw CError("error while reading file.");
}
}
else
{
throw CError("error file not open.");
}
}

cfile.h

template<typename T> 
class CFile{
private:
string _uri;
fstream _file;

public:
CFile(const string uri) throw(CError)
{
_uri = uri;

_file.open(_uri.c_str() , ios::in | ios::out | ios::app | ios::beg);

if(!_file.is_open())
{
throw CError("error openning file.");
}
}

~CFile()
{
_file.close();
}

/*write into the file a single T*/
void Write(const T& t) throw(CError);
};

celement.h

class CElement
{
private:
int _number;

public:
CElement(int number)
{
_number = number;
}

CElement(const CElement& element)
{
_number = element._number;
}

/*set the number to the getting number*/
void Set(const int number);

/*return the number*/
int Get() const;

friend ostream& operator<<(ostream& out, const CElement& a);
friend istream& operator>>(istream& in, CElement& a);
};

celement.cpp


ostream& operator<<(ostream& out, const CElement& a)
{
out << a._number;
return out;
}

istream& operator>>(istream& in, CElement& a)
{
in >> a._number;
return in;
}

אולי הוא לא רואה את הCASTING טוב מהfstream לostream

פורסם

לא קשור casting, כי אם כן אז הוא היה נותן לך שגיאה בשלב ה-compile, ולא בשלב ה-link.

השגיאה אומרת שהוא לא הצליח למצוא את המימוש של Write. נסה להעביר אותו לתוך הקובץ h, כמו הבנאי.

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

פורסם
  • מחבר

עובד! תודה.

מעניין למה זה ככה ???

ארכיון

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

דיונים חדשים