CDBVariant in CArray

Intrebari legate de programarea cu biblioteci precum MFC, ATL, WTL si GDI+.
mesajflaviu
Membru++
Membru++
Posts: 687
Joined: 10 Sep 2008, 21:40
Judet: Cluj

Re: CDBVariant in CArray

Post by mesajflaviu » 27 Apr 2012, 10:55

nedo wrote:Daca nu ma insel, aici ar trebui sa fie cam la fel de rapide. O clasa care nu are un membru de copiere va avea atribuit de catre compilator unul, care va face un shalow copy, va copia doar adresele membrilor si le va atribui noilor membri.
Mai pe romaneste:
fool.&m_pstring = foo2.&m_pstring;
si barl.&m_string = bar2.&m_string;
Bun, dar CString nu are un operator de copiere ?



nedo
Junior
Junior
Posts: 32
Joined: 14 Oct 2011, 20:18
Judet: Bucureşti

Re: CDBVariant in CArray

Post by nedo » 27 Apr 2012, 11:06

Da, s-ar putea sa ai dreptate.

User avatar
Ovidiu Cucu
Fondator
Fondator
Posts: 3778
Joined: 11 Jul 2007, 16:10
Judet: Iaşi
Location: Iasi
Contact:

Re: CDBVariant in CArray

Post by Ovidiu Cucu » 27 Apr 2012, 11:28

nedo wrote:O clasa care nu are un membru de copiere va avea atribuit de catre compilator unul, care va face un shalow copy, va copia doar adresele membrilor si le va atribui noilor membri.
Mai pe romaneste:
fool.&m_pstring = foo2.&m_pstring;
si barl.&m_string = bar2.&m_string;
Nu.
Shallow copy (sau bitwise copy, copiere bit cu bit) inseamna ca se ia pur si simplu de la adresa A si se copie bit cu bit (mai bine zis, byte cu byte) la adresa B.
Exemplu:

Code: Select all

  memcpy(&foo2, &foo1, sizeof(CFoo));
Copierea implicita nu face bitwise copy ci memberwise copy, adica membru cu membru, adica:

- daca membrul este un tip scalar se copie folosind operatorul de atribuire built-in.
- daca membrul este un array atunci se copie array-ul element cu element.
- daca membrul este un obiect atunci se copie obiectul (se apeleaza constuctorul de copiere sau operatorul de atribuire al membrului);

// Si mitul cu shallow copy / bitwise copy este destul de raspandit, asa ca ar trebui sa-l trimitem lui nenea Adam Savage. ;)

User avatar
bu7ch3r
Membru++
Membru++
Posts: 326
Joined: 17 May 2011, 15:17
Judet: Iaşi
Location: Sofia
Contact:

Re: CDBVariant in CArray

Post by bu7ch3r » 27 Apr 2012, 11:30

Vedeti ca scrie acolo(link-ul trimis de mesajflaviu) ceva despre reference count :D
Cu stima,
Lupu Claudiu

User avatar
Ovidiu Cucu
Fondator
Fondator
Posts: 3778
Joined: 11 Jul 2007, 16:10
Judet: Iaşi
Location: Iasi
Contact:

Re: CDBVariant in CArray

Post by Ovidiu Cucu » 27 Apr 2012, 12:27

mesajflaviu wrote:Pai la fel, foo2 cu al sau foo2.m_pstring contine doar adresa stringului str, pe cand bar2 are in bar2.m_string chiar obiectul str cu tot zidu' ....
Probabil asa o sa zica nea Adam la inceput. Nu si la sfarsit, daca ceteste mai cu atentie aici: http://www.codexpert.ro/forum/viewtopic ... 121#p13656
Obiectul tip CString NU contine tot zidu' ci doar un pointer care arata unde-i zidu', undeva la mama darkului, prin heap. ;)


Revin cu intrebarea:

Code: Select all

   CFoo foo2 = foo1; // #1
   CBar bar2 = bar1; // #2
[/i]
Q: Este mai optima copierea in linia #1 datorita faptului ca, CFoo contine pointer la CString si nu obiect tip CString? De ce?

Tip: vezi pontul lui Claudiu. ;)

mesajflaviu
Membru++
Membru++
Posts: 687
Joined: 10 Sep 2008, 21:40
Judet: Cluj

Re: CDBVariant in CArray

Post by mesajflaviu » 27 Apr 2012, 20:33

Banui ca in primul caz, se incrementeaza doar referinta membrului m_pchData, iar in cazul 2 se creeaza un nou obiect CString identinc cu bar1 ... din ce am citit aici si aici ...

P.S. Parca aud oale si strachini sparte :?:

User avatar
Ovidiu Cucu
Fondator
Fondator
Posts: 3778
Joined: 11 Jul 2007, 16:10
Judet: Iaşi
Location: Iasi
Contact:

Re: CDBVariant in CArray

Post by Ovidiu Cucu » 28 Apr 2012, 10:45

mesajflaviu wrote:[...]
P.S. Parca aud oale si strachini sparte :?:
"Bang, dong, zang!..". Nu va speriati, e un botez la chiezii de la zece. :biggrin:
Ok, sa incercam sa lamurim chestia cu reference count.

Sa zicem ca am urmat un curs finantat din fonduri UE, numit "C++ pentru profesorii de C cu cin si cout". :geek:
Pentru verificarea cunostintelor si certificare, ni se cere sa implementam clasa String.
Deci, la treaba!

Code: Select all

class String
{
   char* m_pBuffer;
public:
   String();
   String(const char* p);
   String(const String& r);
// ... s.a.m.d.
   ~String();
};

Code: Select all

String::String() : m_pBuffer(0)
{
}

String::String(const char* p)
{
   m_pBuffer = new char[strlen(p) + 1];
   strcpy(m_pBuffer, p);
}

String::String(const String& r)
{
   if(r.m_pBuffer == 0)
   {
      m_pBuffer = 0;
   }
   else
   {
      m_pBuffer = new char[strlen(r.m_pBuffer) + 1];
      strcpy(m_pBuffer, r.m_pBuffer);
   }
}

String::~String()
{
   delete []m_pBuffer;
}
Merje? Mai sunt cateva mici corecturi de facut, insa in principiu, merge. Se poate si mai bine? De ce, cum?
Pai, daca scriu asa...

Code: Select all

void DoSomethingCool()
{
   String s1 = "Un text lung cat zidu' chinezesc...";
   String s2 = s1;
   String s3 = s2;
   // s.a.m.d.
}

int main()
{
   DoSomethingCool();
   return 0;
}
...se poate observa ca s1, s2 si s3 "contin" toate "Un text lung cat zidu' chinezesc...".
Ceea ce si trebuie. Insa daca ne uitam si mai atent pointerul m_pBuffer, observam arata catre adrese diferite.
Deci (si asta se poate observa si din cod), s-au alocat trei zone de memorie pentru exact acelasi text.
Curata risipa mai ales ca stringurile sunt intensiv folosite intr-o aplicatie serioasa, iar asemenea copii pot aparea destul de des.
Este ca si cum am cheltui toti banii dati de UE pentru 100 km de autostrada ca sa facem doar un kilometru. Deja vu? :D

Aici apare ideea de reference counting.
Cam asa:

Code: Select all

class String
{
public:
   char* m_pBuffer;
   mutable int m_nRefs; // reference count
public:
   String();
   String(const char* p);
   String(const String& r);
// ... s.a.m.d.
   ~String();
};

Code: Select all

String::String() : m_pBuffer(0), m_nRefs(0)
{
}

String::String(const char* p) : m_pBuffer(0), m_nRefs(0)
{
   if(p != 0)
   {
      m_pBuffer = new char[strlen(p) + 1];
      strcpy(m_pBuffer, p);
   }
}

String::String(const String& r)
{
   m_pBuffer = r.m_pBuffer;
   m_nRefs = ++r.m_nRefs;
}

String::~String()
{
   if(--m_nRefs < 1)
      delete []m_pBuffer;
}
Se observa cu ochiul liber ca in constructorul de copiere nu am mai alocat o zona noua de memorie in care sa copii.
Am copiat pur si simplu pointerul m_pBuffer, care arata catre adresa care contine textul.
Ca sa stiu cate obiecte String arata catre aceeasi zona de memorie, am adaugat o variabila "reference counter", m_nRefs.

De fiecare data cand fac o copiere de genul asta, incrementez m_nRefs.
Pe destructor, decrementez m_nRefs apoi vad ce valoare are.
Daca m_nRefs > 0, inseamna ca mai exista si alte obiecte care folosesc (care se refera la) aceeasi zona punctata (referita) de m_pBuffer deci il las in pace.
Daca m_nRefs ajunge la zero, inseamna ca asta-i ultimul obiect care o mai foloseste (se refera la ea) asa ca eliberez m_pBuffer.
Am subliniat "refera" si "referita" pentru a arata de unde provine denumirea de "reference count" si ca nu are legatura cu tipul referinta (reference) din C++.

Simplu. In MFC CString, e putin altfel insa ideea e aceeasi.

User avatar
Ovidiu Cucu
Fondator
Fondator
Posts: 3778
Joined: 11 Jul 2007, 16:10
Judet: Iaşi
Location: Iasi
Contact:

Re: CDBVariant in CArray

Post by Ovidiu Cucu » 28 Apr 2012, 14:57

[continuare ]

Spuneam mai devreme "CString contine un singur membru (LPTSTR m_pchData)".
Asa este, se poate verifica, de exemplu in fereastra "Watch".

Atunci unde-i acel "reference count" (m_nRefs)?
Este intr-o structura CStringData care sta "lipita" in fata bufferului alocat pentru un obiect CString (in fata buferul pointat de CString::m_pchData). De notat aici ca intotdeauna intr-un CString valid, m_pchData NU este NULL, chiar si pentru stringuri "empty".

Exemplul de mai jos se poate incerca pentru ca sa fie mai clar unde este si ce se intampla cu acel reference count dar si pentu a elimina confuzii, mituri si legende programatoricesti (pe care le-am intalnit destul de des in discutii) gen "un obiect de tip CString este un obiect baban", "adresa unui obiect tip CString este aceeasi cu adresa bufferului", "la copiere, musai se aloca un buffer nou" si in fine ca o incununare a celor precedente: "daca tin intr-o strucura, pointer la CString si nu un obiect de tip CString, codul este mult mai performant".

Code: Select all

void CTestDialog::DumpStringData(const CString& str)
{
   const CString* pstr = &str; // pointer to CString object
   TRACE1("CString object address: %p\n", pstr);

   LPCTSTR pchData = (LPCTSTR)str; // CString::m_pchData member
   TRACE1("CString::m_pchData points to: %p\n", pchData);
   TRACE1("Memory pointed by CString::m_pchData contains: %s\n", pchData);

   CStringData* pStringData = (CStringData*)pchData - 1; // pointer to CStringData structure
   long nRefs = pStringData->nRefs; // reference count
   TRACE1("Reference count: %d\n\n", nRefs);
}

Code: Select all

void CTestDialog::OnButtonTest() 
{
   TRACE0 ("\n+++++ Construct objects +++++\n\n");

   CString* pstr1 = new CString(_T("Un string lung cat zidu' chinezesc..."));
   DumpStringData(*pstr1);

   CString* pstr2 = new CString(*pstr1);
   DumpStringData(*pstr2);

   CString* pstr3 = new CString(*pstr2);
   DumpStringData(*pstr3);

   TRACE0 ("\n----- Destruct objects -----\n\n");

   DumpStringData(*pstr1);
   delete pstr1;
   
   DumpStringData(*pstr2);
   delete pstr2;

   DumpStringData(*pstr3);
   delete pstr3;

} // <-- put a beakpoint here, start debug then take a look in "Debug" window.
Dupa toate astea, sper ca Adam ar fi absolut de acord sa punem stampila :)
Myth Busted.jpg
Myth Busted.jpg (11.17 KiB) Viewed 6299 times
Acuma, mergand pe stack-ul acestui topic ajungem la acele structuri CFoo si CBar, prima continand un pointer la CString, cealalta un obiect CString.
Cred ca, dat fiind cele spuse mai sus e destul de clar ca, CFoo nu ofera cinestiece avantaj, cel putin din punctul de vedere al utilizarii memoriei.
In schimb, CBar imi asigura ca, daca un obiect de acest tip este copiat iar unul dintre ele este distrus, in celalalt, membrul lui (CString m_string) ramane un string valid.
Ceea ce nu se intampla si la CFoo care contine pointer (CString* m_pstring).

In cele din urma ajungem si la postul initial in care am spus "CDBVariant e cea mai idioata clasa din tot MFC-ul".
Mentin afirmatia, dat fiind argumentele de pana acum.
Nu ofera cine stie ce castig de performanta, in schimb iti da sanse sa o bagi pe maneca daca nu esti atent. Asa ca, daca cineva vrea sa puna la pastrare date dintr-un recordset, mai bine nu foloseste direct CDBVariant ci o clasa cu membri obiecte si nu pointeri.

Daca am spus ceva gresit si/sau mai e ceva de completat, feel free and shoot! ;)
BTW, n-am intalnit pana acum pe nimeni care sa se fi jucat prima oara cu CDBVariant si sa nu fi dat de crapaciuni, macar o data.

Post Reply