cpp_tips
Toto je starší verze dokumentu!
Obsah
Tipy & triky C++
char* na istringstrem (bez kopírování)
- jak z velkého pole znaků udělat istringstream bez kopírování v paměti?
- následující kód paměť kopíruje až 3x (záleží na implementaci copy on write)
char *mybuf[] = "very very .. long"; istringstream myss(string(mybuf, strlen(mybuf)));
- finta
char *mybuf[] = "very very .. long"; istringstream dfile; dfile.rdbuf()->pubsetbuf(mybuf, strlen(mybuf));
- ověřil jsem to zavoláním free na mybuff (kód se musí upravit na dynamickou alokaci mybuf)
- metoda pubsetbuf je nabízena třídou streambuf, ale implementována až v strbuf nebo filebuf (takže v streambuf nedělá vlastně nic)
char* do vector<char>
char arr[] = "abcd"; vector<char> buff; buf->insert(buf->end(), arr, arr + strlen(abcd);
Default parametry metody/funkce
- mám metodu s více parametry. Někdy mi ale stačí změnit jen ten první a ostatní jako default
- jako default lze dokonce použít i funkci nebo proměnou
// declaration void setSomething(int val1 = 1, int val2 = 2, int val3 = 3); // definition void setSomething(int val1, int val2, int val) { this.val1 = val1; this.val2 = val2; this.val3 = val3; } // calling setSomething(1); setSomething(1,2); setSomething(1,2,3);
Zákeřné dočasné objekty
- dočasné objekty mají životnost pouze v rámci výrazu
- příklad se stringstream: chceme postupně vyčítat všechny znaky stringu
- první varianta
- v každé iteraci se provádí znovu alokace paměti pro dočasný objekt str včetně kopírování obsahu
- nejspíš dokonce dvojnásobně, ještě pro volání size
- valgrind naměřil (celý program): total heap usage: 9,890 allocs, 9,868 frees, 1,207,634 bytes allocated
int i; for (i=0; i<outstr.str().size(); ++i) { hash = ((hash << 5) + hash) + outstr.str()[i]; // hash * 33 + c }
- druhá varianta
- vůbec nefunguje - nekončeně cyklí kvůli memory leaku
for (string::const_iterator it = outstr.str().begin(); it != outstr.str().end(); ++it) { hash = ((hash << 5) + hash) + *it; // hash * 33 + c }
- třetí varianta
- správné řešení
- teoreticky se ale kopíruje paměť str objektu do mystr
- valgrind naměřil (celý program): total heap usage: 9,148 allocs, 9,126 frees, 913,802 bytes allocated
string mystr(outstr.str()); for (string::const_iterator it = mystr.begin(); it != mystr.end(); ++it) { hash = ((hash << 5) + hash) + *it; // hash * 33 + c }
- čtvrtá varianta
- mělo by být nejvíce šetrné k paměti
- objekt str by se neměl kopírovat, měla by se pouze prodloužit jeho životnost
- hrozí zde stále nebezpečí přepsání obsahu.. ukazatel ukazuje na interní buffer, který se může při práci se stringstream měnit
- valgrind naměřil překvapivě stejný počet alokací, jako v předchozím případě. Nejspíš zabrala nějaká optimalizace při kompilaci
const string& mystr = outstr.str(); for (string::const_iterator it = mystr.begin(); it != mystr.end(); ++it) { hash = ((hash << 5) + hash) + *it; // hash * 33 + c }
Převod struct na/z string (serializace)
- struktura je v C++ ve skutečnosti třída, která má všechny položky public (žádný rozdíl mezi nimi není)
- v C se struktura dala krásně využít pro parsování pole znaků, například hlavičky paketu
- v C++ lze samozřejmě používat stejným způsobem, ale jak by pro tento úkol šlo použít nové vlastnosti C++?
- pokud struktura obsahuje pouze základní datové typy,
- lze strukturu převést na string zápisem do proudu stringstream po přetypování na pole znaků:
stringstream out(stringstream::out|stringstream::binary) out.write((char *) &acc, sizeof(struct MyRecord));
- pokud obsahuje i objekty, jako například string,
- musí se zapsat po jednotlivých typech:
.. out.write( rec.location.c_str(), rec.location.size() ); // string out.write( (char*) & rec.account_num, sizeof( rec.account_num ) ); // int ..
- čtení probíhá obdobně… voláním funkce read
Mapování struct do vector<char>
vector<char> v(sizeof(mystruct)); mystruct *s = reinterpret_cast<mystruct*>(v.data());
Velikost struktury s polem znaků
- platí v C/C++
- jak velká je tato struktura?
struct mystruct { char data[]; }
- sizeof hlasí 0B :)
- pokud ji chceme započítat, musíme napsat alespoň
struct mystruct { char data[1]; }
Private Hader file
- třídy se typicky deklarují v hlavičkovém souboru
- třída má někdy privátní položku speciálního datového typu, který vyžaduje vložit speciální hlavičkový soubor
- pokud třídu využíváme v jiné třídě, zbytečně vkládáme další a další hlavičkové soubory
- přitom nás privátní položky jiné třídy vůbec nezajímají, jelikož je stejně nemůžeme používat
- vzniká tak potřeba tzv privátních a veřejných hlavičkových souborů
- další využití je pro skrytí vnitřní implementace třídy
- řešení s jedním hlavičkovým
- do hlavičkového přidáme podmínky..
#ifdef CONF_IMPL #include <libxml/xmlmemory.h> #include <libxml/parser.h> #endif .. class Conf { private: .. #ifdef ADSCONF_IMPL void parsePref(xmlNodePtr cur); void parseMet(xmlNodePtr cur); void parseCfg(xmlNodePtr cur); #endif .. public: Conf(); }
- do zdrojového kódu, kde třídu implementujeme, přidáme
#define CONF_IMPL
Memory leak bez virtuálního destruktoru
- pokud se z třídy dědí, měla by mít virtuální destruktor
- memory leak příklad:
class Interface { virtual void doSomething(void) = 0; }; class Derived : public Interface { Derived(void); ~Derived(void) { // Do some important cleanup... } }; void myFunc(void) { Interface* p = new Derived(); // The behaviour of the next line is undefined. It probably // calls Interface::~Interface, not Derived::~Derived delete p; }
- je to zajímavé, protože ve třídě nemusí být vůbec dynamická alokace. Stačí, aby měla jako member nějaký objekt, třeba string, a bez zavolaní správného destruktoru nedojde k dealokaci
- řešení je přidat virtuální destruktor do Base třídy, stačí takto:
virtual ~Interface() {}
cpp_tips.1464166279.txt.gz · Poslední úprava: (upraveno mimo DokuWiki)
