====== 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 [[http://www.cplusplus.com/reference/streambuf/streambuf/pubsetbuf/|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 arr[] = "abcd"; vector buff; buf->insert(buf->end(), arr, arr + strlen(abcd); ===== stringstream na cout/string ===== * na příkladu #include using namespace std; int main() { stringstream ss("SS"); ostringstream os("OS"); istringstream is("IS"); ss.get(); cout << "1:" << ss << endl; cout << "2:" << ss.str() << endl; cout << "3:" << ss.rdbuf() << endl; cout << "4:" << ss.rdbuf()->str() << endl; is.get(); cout << "1:" << is << endl; cout << "2:" << is.str() << endl; cout << "3:" << is.rdbuf() << endl; cout << "4:" << is.rdbuf()->str() << endl; //os.get(); // cannot be called on ostringstream cout << "1:" << os << endl; cout << "2:" << os.str() << endl; //cout << "3:" << os.rdbuf() << endl; cout << "4:" << os.rdbuf()->str() << endl; cout << "---" << endl; stringstream ss2; ostringstream os2("OS"); istringstream is2("IS"); char my1[]="SS2"; char my2[]="OS2"; char my3[]="IS2"; ss2.rdbuf()->pubsetbuf(my1,3); os2.rdbuf()->pubsetbuf(my2,3); is2.rdbuf()->pubsetbuf(my3,3); ss2.get(); cout << "1:" << ss2 << endl; cout << "2:" << ss2.str() << endl; cout << "3:" << ss2.rdbuf() << endl; cout << "4:" << ss2.rdbuf()->str() << endl; is2.get(); cout << "1:" << is2 << endl; cout << "2:" << is2.str() << endl; cout << "3:" << is2.rdbuf() << endl; cout << "4:" << is2.rdbuf()->str() << endl; //os2.get(); // cannot be called on ostringstream cout << "1:" << os2 << endl; cout << "2:" << os2.str() << endl; //cout << "3:" << os.rdbuf() << endl; cout << "4:" << os2.rdbuf()->str() << endl; } * spuštění vypíše 1:0x7fff38741978 2:SS 3:S 4:SS 1:0x7fff38741c50 2:IS 3:S 4:IS 1:0x7fff38741f18 2:OS 4:OS --- 1:0x7fff38741808 2:SS2 3:S2 4:SS2 1:0x7fff38741ae0 2: 3:S2 4: 1:0x7fff38741db8 2:OS2 4:OS2 * všimni si, že přečtení znaku z ''istringstream'' způsobí, že rdbuf nevypíše všechny znaky * řeší se to přenastavením ukazatele a preventivním vymazáním flagů (například EOF flag) is.clear(); is.seekg(0); * dále, po nastavení bufferu přes ''pubsetbuf'' nějak nefunguje výpis z ''istringstream'' * na příčinu jsem zatím nepřišel, ale řešení je použít ''stringstream'' ===== 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 * 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 ===== vector v(sizeof(mystruct)); mystruct *s = reinterpret_cast(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 #include #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() {} ===== Soubor do paměti ===== ifstream sfile(filepath.c_str()); if (!sfile) { LogMsg(MSGERR, "Cannot open file %s for packing\n", filepath.c_str()); flen = 0; } else { // checking file size... is getFileSize() better? sfile.seekg(0, ios::end); flen = sfile.tellg(); sfile.seekg(0,ios::beg); vector fcxt(flen); sfile.read(&fcxt[0], flen); sfile.close(); } ===== Paměť do souboru ===== ofstream f; f.open(filepath.c_str()); if (!f.is_open()) { LogMsg(MSGERR, "Cannot create file for service %s\n", name.c_str()); return 7; } f.write(v.data(), v.size()); f << flush; // force to create empty file f.close();