====== 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();