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);
stringstream na cout/string
- na příkladu
- stringstream_test.cpp
#include <iostream> 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
istringstreamzpů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
pubsetbufnějak nefunguje výpis zistringstream - 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<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() {}
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<char> 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();
cpp_tips.1472209883.txt.gz · Poslední úprava: (upraveno mimo DokuWiki)
