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

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


SweeT_EviL

Recommended Posts

היי,

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

לדוגמא כאן:

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

}

איך אני אומר למשתנה fstream שלי לקרוא T בודד? אם זה INT, STRING או אפילו CLASS שמישהו כתב ועכשיו רוצה להשתמש ב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;
}
}

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

איזה מוזר בכל המקומות שקראתי על טמפלייט הם כותבים עם 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).

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

אין קשר לחריגה אבל כן יכולה להיות לך בעיה עם הקובץ שאתה קורא כתלות במימוש של המלקה שבפועל קוראת ממנו (ה 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, כמו הבנאי.

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

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

ארכיון

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

×
  • צור חדש...