Fórum Root.cz
Hlavní témata => Vývoj => Téma založeno: letranger 02. 01. 2013, 19:45:59
-
Dobrý večer, řeším následující problém při návrhu programu v C++, který může využívat pouze věci z ANSI definice, tedy kontejnery z knihovny STL, žádný boost nebo něco podobného.
V jedné třídě (třebas A) potřebuji definovat pole (vektor) vektorů nestejné délky objektů jiné třídy (řekněme B). V konstruktoru chci toto pole naplnit prázdnými vektory (v té době vím, kolik jich bude, ale nevím, jakou budou mít jednotlivé vektory délku) a první vektor naplnit instancí třídy Z. Jsem trochu zmatený tím, jestli vše musí být definováno přes pointery nebo jestli je to možné udělat i pomocí automatických proměnných. Pole pointerů na vektory pointerů na objekty třídy B zní dost hrozivě , ale nevím, jak to udělat jinak, tak abych mohl za běhu programu dynamicky vytvářet objekty třídy B a ukládat je do vektorů. Dá se to udělat tak, aby se člověk mohl vyhnout alespoň používání pointeru na vektor nebo ne? Máte někdo nějaký nápad?
-
std::vector<std::vector<Data>> je standardní řešení, žádné ukazatele nejsou potřeba. Pokud je to C++11, pak std::vector má i přesouvací konstruktor a výkonově to je potom ekvivalentní těm ukazatelům.
-
C++11 to není. Jak ale mám to pole inicializovat a pak ho rozšiřovat? Když např. v některé funkci vytvořím objekt typu "Data" a přidám ho do vectoru, tak po vyskočení z funkce ten objekt zanikne, pokud není vytvořen pomocí pointeru a new - nebo ne? Nerozumím přesně tomu, jak ten std::vector funguje.
-
vector má svůj alokátor. vector<Data> alokuje pro každou položku paměť o velikosti sizeof(Data). Pokud přidáš objekt do vectoru, tak se nakopíruje do paměti alokované vectorem.
V případě vector<Data *> to funguje úplně stejně, ale alokuje se sizeof(Data *) a kopíruje se hodnota ukazatele.
-
O inicializaci a správu paměti se stará std::vector sám (přesněji to dělá jeho alokátor, který můžete nahradit za vlastní, ale ten výchozí v naprosté většině případů stačí). Pro rozšiřování má metodu insert(int, const Data&) a dvě „zkratky“ push_front(const Data&) a push_back(const Data&) (v C++11 mají všechny tyhle metody ještě přetíženou verzi pro přesouvání).
insert předaná data zkopíruje dovnitř std::vectoru pomocí kopírovacího konstruktoru, takže původní instance sice zanikne, ale její kopie tam bude. C++11 řeší výkon pomocí těch přesouvacích verzí, které volají přesouvací konstruktory, když ten objekt má stejně zaniknout. V C++ před-11 tohle není, takže se to u paměťově náročných tříd řeší tak, že se pomocí insert vloží prázdný objekt a až s tím se pracuje; u paměťově jednoduchých objektů se na to kašle v obou verzích :)
insert vrátí iterátor na vložený objekt (push_front ani push_back to nedělají, ale vložené objekty lze získat pomocí metod front() a back()), takže lze udělat něco podobného:
Data &data = *vector.insert(vector.size(), Data());
data.allocateALotOfMemory();
Případně můžete využít metodu resize(int, const Data& = Data()), která „roztáhne“ (nebo „scvrkne“) ten vektor a nové prvky vytvoří kopírováním předaného prvku:
vector.resize(vector.size() + 1);
Data &data = vector.back();
data.allocateALotOfMemory();
Pokud chcete postupně naplnit vektor o délce n, je nejlepší jej předalokovat najednou a až potom plnit, vyhnete se různých výkonostních problémů s kopírováním:
vector.resize(n);
for (std::vector<Data>::iterator it = vector.begin(), end = vector.end(); it != end; ++it) {
it->allocateALotOfMemory();
}
Pozn. tohle všechno se týká C++ před-11; v C++11 by to bylo trochu jednodušší :)
-
Hmm, insert používá iterátor místo pozice (insert(iterator, const Data&)), fix první ukázky:
Data &data = *vector.insert(vector.end(), Data());
data.allocateALotOfMemory();