Επιστροφή στο Forum : Σχετικα με template class vc++
Γεια και χαρα και Χρηστος ανεστη κλπ κλπ..
Αναφερομε σε Visual Studio C++ 2008 EE,Win32 project;
Προσπαθω να μετατρεψω μια εφαρμογη κρυπτογραφησης απο .net 2 σε win api app(ή οπως λεγετε τελος παντων).
Επειδη εχω 6 class ( η μια ειναι base , οι αλλες ειναι διαφοροι τυποι κρυπτογραφησης) μου χρειαζεται μια κλαση η οποια θα ειναι κατι σαν buffer που δεχετε πολλους τυπους. Εψαξα στο ιντερνετ και βρηκα πως μπορω να το κανω με template. Εδω ειναι η προσπαθεια μου
#include "stdafx.h"
template <class T>
class MemoryStream{
public:
T* buffer;
long Size;
void Write(T* buffer_,long bufferSize)
{
Size = bufferSize;
AllocBuffer();
buffer =buffer_;
}
T* Read(long index,long count)
{
T temp[count];
long tempIndex=0;
for(;index<(index+count);index++,tempIndex++)
temp[tempIndex]=buffer[index];
return temp;
}
void Clean(void)
{
free(buffer);
}
private:
void AllocBuffer(void)
{
buffer = (T*) malloc(sizeof(T*)*Size);
}
};
int _tmain(int argc, _TCHAR* argv[])
{
char somestream[]="lalalala";
MemoryStream<char> *ms;
ms->Write(somestream,sizeof(somestream));
printf("%s",ms->Read(1,4));
return 0;
}
Μπορει να μου πει κανεις τι στο καλο εχω κανει λαθος?:closed:
Χρόνια πολλά και σε σένα φίλε μου.
Κατ' αρχάς, τι εννοείς λάθος; Ότι δεν κομπαϊλάρει ή ότι δε σου βγάζει τα αποτελέσματα που θέλεις;
Όταν το δοκιμάζω σε VS2008 μου βγάζει τα εξής λάθη:
Error 1 error C2057: expected constant expression main.cpp 17
Error 3 error C2133: 'temp' : unknown size main.cpp 17
Error 2 error C2466: cannot allocate an array of constant size 0 main.cpp 17
Warning 4 warning C4172: returning address of local variable or temporary main.cpp 21
(πρόσθεσα στην αρχή τις γραμμές:
#include <stdio.h>
#include <memory>
και αφαίρεσα την
#include "stdafx.h"
για να μου περάσει το build, οπότε οι αριθμοί γραμμών που φαίνονται μάλλον θα είναι λίγο διαφορετικοί).
Τα λάθη που μου έβγαλε είναι λογικά και η εξήγησή τους είναι:
Για τη γραμμή:
T temp[count];
το Error 1 σημαίνει ότι στη C++ δεν μπορείς να έχεις ένα array με μεταβλητό πλήθος. Πρέπει να κάνεις new. Δηλαδή να γίνει:
T* temp = new T[count];
Αυτό θα διορθώσει και τα άλλα λάθη, ΑΛΛΑ θα πρέπει να κάνεις delete[] το αποτέλεσμα της MemoryStream::Read για να μην έχεις memory leaks. Καλύτερα λοιπόν να περάσεις το T* σαν παράμετρο στη Read και να αρχικοποιηθεί απ' έξω (και εκεί να διαγραφεί κιόλας). Γενικά είναι καλή τακτική να γίνεται η αρχικοποίηση και η καταστροφή στο ίδιο μπλοκ κώδικα (ή ακόμα καλύτερα να χρησιμοποιείς την std::auto_ptr ή την std::shared_ptr)
Το warning εξαφανίστηκε επίσης και είχε να κάνει με το ότι η Read επέστρεφε pointer σε τοπική μεταβλητή που μετά το return καταστρέφεται και άρα θα σου επέστρεφε σκουπίδια.
Επίσης στο τέλος, στη main, χρησιμοποιείς τη μεταβλητή ms χωρίς να έχει γίνει new προηγουμένως. Άρα, η εφαρμογή θα σκάσει.
Σε αυτό το context μάλιστα, δεν χρειάζεται καν να είναι pointer η ms. Μπορεί να είναι απλά μία μεταβλητή στο stack:
MemoryStream<char> ms;
και να χρησιμοποιηθεί με . και όχι με ->
Δεν ξέρω αν εννοούσες αυτά τα λάθη, στο compilation δηλαδή. Αν εννοείς κάτι άλλο, το ξανασυζητάμε...
Σημερα αρχησα C++ (πριν χρονια ειχα δοκιμαση αλλα παπαλα, βλεπεις MFC ..)
Τεσπα το ελυσα το προβλημα μου.
template <class T>
class MemoryStream{
public:
long Size;
void Write(T* buffer_,long bufferSize)
{
Size = bufferSize;
AllocBuffer();
buffer =buffer_;
}
void Read(long index,long count,T* buffer_)
{
long step=0;
while(step<count)
{
buffer_[step]=buffer[index+step];
step++;
}
}
void Clean(void)
{
free(buffer);
}
private:
void AllocBuffer(void)
{
buffer = (T*) malloc(sizeof(T*)*Size);
}
T* buffer;
};
Test mode
template <class T>
class MemoryStream{
public:
long Size;
void Write(T* buffer_,long bufferSize)
{
Size = bufferSize;
AllocBuffer();
buffer =buffer_;
}
void Read(long index,long count,T* buffer_)
{
long step=0;
while(step<count)
{
buffer_[step]=buffer[index+step];
step++;
}
}
void Clean(void)
{
free(buffer);
}
private:
void AllocBuffer(void)
{
buffer = (T*) malloc(sizeof(T*)*Size);
}
T* buffer;
};
int _tmain(int argc, _TCHAR* argv[])
{
char c[]="1234567890";
int i[] = {1,2,3,4,5,6,7,8,9,0};
MemoryStream<int> ms;
MemoryStream<char> ms1;
ms.Write(i,10);//int
ms1.Write(c,10);//char
int res_i[4];
char res_c[4];
ms.Read(3,4,res_i);//int
ms1.Read(3,4,res_c);//char
for(int u=0;u<4;u++)
printf("%d %c\n",res_i[u],res_c[u]);
printf("%d",sizeof(res_c));
getchar();
return 0;
}
Τωρα θελω να σε ρωτησω αυτο περι το memory leak.
Βλεπεις ασχολουμε με c# και εχουμε το ωραιοτατο Object που μας λυνει τα χερια :respekt:.
Η παραπανω τεμπλαϊτ την εχω μεσα override συναρτηση που ανικει σε μια base class η οποια εχει 5 driver class ... καταλαβαινεις πως ειναι πολυ σημαντικο κοματι του προγραμματος μου.
delete ? τι ειναι και τι κανει:hmm:?
A και κατι αλλο, αυτο που ειπες stak?
Ποια η διαφορα
classA a;
με
classA *a=new classA();
Sorry φίλε μου. Δεν ήξερα ότι τώρα ξεκινάς με C++, οπότε δεν ξέρω αν ήμουνα πολύ αναλυτικός. Χαίρομαι πάντως που έλυσες το πρόβλημά σου.
Το delete στη C++ χρησιμοποιείται για να καταργήσει μία μεταβλητή που έχει γίνει initialize με new. Έναν pointer δηλαδή. Όταν λοιπόν γράφεις:
classA* a = new classA();
πρέπει οπωσδήποτε να διαγράψεις τη μεταβλητή a όταν σταματήσεις να τη χρησιμοποιείς με τη δήλωση:
delete a;
Από κει και πέρα, η μνήμη που κατείχε η a θα ελευθερωθεί και δεν θα μπορέσεις να τη χρησιμοποιήσεις. (Για την ακρίβεια θα μπορείς να τη χρησιμοποιήσεις, αλλά αν το κάνεις η εφαρμογή θα σκάσει ή θα έχει απροσδιόριστη συμπεριφορά).
Αν λοιπόν δεν κάνεις delete, η μνήμη που κατέχει η a δεν θα ελευθερωθεί ποτέ και δεν θα αποδοθεί ποτέ πίσω στην εφαρμογή. Αυτό σημαίνει ότι θα καταλαμβάνεται μνήμη που δεν χρησιμοποιείται. Αυτό λέγεται memory leak.
Τα σύγχρονα λειτουργικά, όταν κλείνουν μία εφαρμογή ελευθερώνουν τη μνήμη που καταλαμβάνουν. Αυτό σημαίνει ότι όταν βγαίνεις από την εφαρμογή, η μνήμη ελευθερώνεται ούτως η άλλως (είτε κάνεις delete είτε όχι), αλλά όσο είσαι μέσα στην εφαρμογή θα βλέπεις τη μνήμη να μεγαλώνει συνεχώς χωρίς να χρησιμοποιείται.
Με δυο λόγια, αν κάνεις new, πρέπει να κάνεις και delete.
Τώρα, το stack είναι μια περιοχή της μνήμης όπου αποθηκεύονται οι τοπικές μεταβλητές που δεν είναι pointers. Έχει περιορισμένο μέγεθος (εξαρτάται από κάποιο setting στον compiler) οπότε δεν είναι καλό να αποθηκεύεις εκεί μεγάλες μεταβλητές.
Το heap είναι μία άλλη περιοχή της μνήμης που είναι κοινή για όλη την εφαρμογή. Εκεί αποθηκεύονται οι pointers. Το heap μπορεί να μεγαλώσει απεριόριστα, όσο είναι ορισμένο από το λειτουργικό σύστημα.
Έτσι, στη δήλωση:
classA a;
θα ανατεθεί μνήμη από την stack που θα ελευθερωθεί φεύγοντας από την function, ενώ στη δήλωση:
classA* a = new classA();
θα ανατεθεί μνήμη από την heap που θα ελευθερωθεί μόνο με το delete.
Κατ' αντιστοιχία, στη C#, στο stack αποθηκεύονται πάντα τα structs και στο heap οι classes.
Για να γλυτώσεις όλα τα παραπάνω, μπορείς να χρησιμοποιήσεις το std::auto_ptr<> ή το boost::shared_ptr<> (ή τώρα πλέον tr1::shared_ptr<>) αλλά αυτό είναι άλλη κουβέντα, για smart pointers.
Η C# προσπαθεί να λύσει όλα τα προβλήματα καταργώντας τους pointers, όπως και η Java. Χρησιμοποιεί δηλαδή implicitly κάτι σαν το std::auto_ptr<> που ανέφερα παραπάνω.
Ελπίζω να ξεκαθάρισα αυτά που ήθελες.
Ωραια .. καλα κανω που εχω τη τεμπλαϊτ ως στακ εφοσον ο μπαφερ της "ειναι" malloc.
Κατι αλλο, εχω String (define char*) και το εχω μονοχα για file paths, ειναι αναγκη να το κανω malloc εφοσον εχει pointer?
Καταρχάς, το malloc και το new είναι (σχεδόν) το ίδιο. Το malloc κληρονομήθηκε από τον κόσμο της C. Άρα, ό,τι συμβαίνει με τη new, το ίδιο συμβαίνει και με την malloc. Αν χρησιμοποιήσεις malloc, η μνήμη δεσμεύεται από την heap.
Όμως, στη δήλωση:
MemoryStream<int> ms;
η ms είναι φυσικά στην stack, ανεξάρτητα αν ο buffer της είναι στη heap ή όχι...
Επειδή ασχολείσαι με C# που δεν έχει pointers, είναι λίγο δύσκολο να τους καταλάβεις με μία συζήτηση σε ένα forum. Οι pointers είναι ίσως το πιο δύσκολο κομάτι στην κατανόηση σε μία γλώσσα σαν τη C++ ή κάποια άλλη που τους χρησιμοποιεί. Θα κάνω όμως μια προσπάθεια να σου εξηγήσω για την char*, απλά δεν νομίζω ότι μπορώ να το εξηγήσω καλά σε μερικές γραμμές...
Λοιπόν, στη C++, οι δηλώσεις
char*
και
char[]
είναι ισοδύναμες (όπου char οποιοσδήποτε τύπος).
Η ανάθεση σε heap ή σε stack έχει να κάνει με την αρχικοποίηση της μεταβλητής, όχι με τον τύπο.
Αν η αρχικοποίηση της μεταβλητής γίνει με new (ή malloc), τότε η δέσμευση μνήμης γίνεται από το heap. Το * δείχνει απλά ότι η μεταβλητή είναι pointer (δηλαδή περιέχει μία διεύθυνση μνήμης - είτε στο heap είτε στο stack)
Τώρα, αν έχεις για παράδειγμα τον κώδικα:
int a = 5;
int* pa = &a;
τότε η pa θα δείχνει σε μία διεύθυνση στο stack γιατί η αρχικοποίησή της μεταβλητής έγινε στο stack (όχι με new).
Άρα, για την char* που χρησιμοποιείς, έχει σχέση με το πώς έχει αρχικοποιηθεί και πώς χρησιμοποιείται.
Για να αποφύγεις τέτοιου είδους προβλήματα, καλύτερα να χρησιμοποιήσεις την std::string (ή αν χρησιμοποιείς MFC την CString), τουλάχιστον τώρα για την αρχή. Αυτές κάνουν το handling των strings μόνες τους χωρίς προβλήματα.
Πάντως, μπορείς να δηλώσεις τη μεταβλητή σου ως:
char myFile[_MAX_PATH];
οπότε δεν χρειάζεται new /delete
ή ως
char* myFile = new char[_MAX_PATH];
οπότε χρειάζεται delete.
Και στις δύο περιπτώσεις μπορείς να χρησιμοποιήσεις τις standard functions strXXX της C++ για να τις χειριστείς.
Δεν ξέρω αν σου έδωσα να καταλάβεις, είναι δύσκολο μέσα σε λίγες γραμμές να καταλάβει κάποιος τους pointers και τη διαχείριση μνήμης σε C++...
Επειδή ασχολείσαι με C# που δεν έχει pointers
http://en.csharp-online.net/ECMA-334:_27.2_Pointer_types
Κάποιος να φτιάξει ενα "common misconceptions" για την C# :p
Η C# *έχει* pointers.
http://en.csharp-online.net/ECMA-334:_27.2_Pointer_types
Κάποιος να φτιάξει ενα "common misconceptions" για την C# :p
Η C# *έχει* pointers.
ναι μωρε για unsafe mode... το ξερουμε, εαν γραφεις δονετ δε τους θελεις...
σαν γλωσσα προγραμματισμου η C# απλα δε πιανετε, ουτε C++ ουτε c j ουτε τιποτα...
Εαν εχει δουλεψει καποιος με .net 3 θα καταλαβει αμεσος τι εννοω
Φοβερό! Παρόλο που ασχολούμαι και με C# (και έχω διαβάσει και αρκετά βιβλία) δεν ήξερα ότι η C# έχει pointers.
Είναι το unsafe προφανώς που κάνει τη διαφορά (unmanaged code?)
Αν τώρα μάθω ότι έχει και η Java pointers...
οκ.. αυτο εδω κανει ακριβος αυτο που εκανε και πριν?
template <class T>
class MemoryStream{
public:
long Size;
void Write(T* buffer_,long bufferSize)
{
Size = bufferSize;
AllocBuffer();
buffer =buffer_;
}
void Read(long index,long count,T* buffer_)
{
long step=0;
while(step<count)
{
buffer_[step]=buffer[index+step];
step++;
}
}
void Clean(void)
{
delete[] buffer;//free(buffer);
}
private:
void AllocBuffer(void)
{
buffer =new T[Size]; //(T*) malloc(sizeof(T*)*Size);
}
T* buffer;
};
Καλα καταλαβα, βαζουμε addess μονο σε buffers :cool:
Ναι, είναι ακριβώς το ίδιο.
Παρεμπιπτόντως, βλέπεις ότι το buffer είναι δηλωμένο ως T* και γίνεται access από την Read ως array. Είναι αυτό που έλεγα πριν για το char* και το char[] ότι ουσιαστικά είναι το ίδιο.
Χρονια πολλα!!!
το ξερουμε, εαν γραφεις δονετ δε τους θελεις...
τι αλλο μπορεις να γραφεις??? τι εννοεις ...δεν σε πιανω..:p
Χρονια πολλα!!!
τι αλλο μπορεις να γραφεις??? τι εννοεις ...δεν σε πιανω..:p
XNA
Δες ενα project:
http://www.youtube.com/watch?v=TgChURF5fQE
@ ADSLgr.com All rights reserved.