Uživatelské nástroje

Nástroje pro tento web


cpp_tips

Toto je starší verze dokumentu!


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 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<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.1465228681.txt.gz · Poslední úprava: (upraveno mimo DokuWiki)

Donate Powered by PHP Valid HTML5 Valid CSS Driven by DokuWiki