C++ stack vs heap.

C++ stack vs heap.
« kdy: 07. 08. 2020, 20:29:13 »
Dobrý deň. Keď vytvorím triedu / štruktúru na heape, ale jej členovia sú vytvorení statickým viazaním (mali by byť na stacku):

Kód: [Vybrat]
#include <memory>
#include <vector>
#include <string>

using namespace std;

struct BinData {
vector<char> content{'a', 'b', 'c'};  // statické viazanie
wstring contentType{ L"text/plain" };
};

class NejakaTrieda {
shared_ptr<BinData> binData; // binData je pointer teda ukazuje na heap ale kde je vytvorený jeho member content? na stacku či heape?
NejakaTrieda() : binData(new BinData()) {

}
};

shared_ptr<NejakaTrieda> nejakyPointerNaObjekt(new NejakaTrieda());


1. Kde sú teda dáta skutočne uložené?  Je celá trieda na jednom mieste?

Priznám sa že už som podobné fórum založil inde, ale ani tam som sa veľa nedozvedel.

Poradili mi aby som si to vyskúšal a odtestoval sám. Lenže po tých testoch mám viac otázok ako odpovedí. Napríklad som zistil že aj keď vector alokujem na stacku tak kontainer je síce na stacku ale buffer s položkami vyzerá byť na heape čo ma ešte viac matie.

2. načo sa teda vector vytvára na stacku? Keď jeho buffer je na heape? Je to kôli automatickému uvoľňovaniu pamate?

3. poradíte mi nejaký nástroj na prezeranie byteov pamate? Niečo ako keď si otvorím súbor len by som chcel aby to čítalo RAMku a mohol si zvoliť adresu v pamati atď.


Re:C++ stack vs heap.
« Odpověď #1 kdy: 07. 08. 2020, 20:55:56 »
Vektor je kontajner, který podporuje dynamickou změnu velikosti, což na stacku prakticky nejde udělat. Z důvodů možné realokace musí být vektor implementovaný tak, že data drží na heapu. Se stackem se pracuje jinak než s heap pamětí a má omezenou velikost, doporučuju nastudovat https://en.wikipedia.org/wiki/Call_stack

Re:C++ stack vs heap.
« Odpověď #2 kdy: 07. 08. 2020, 21:07:36 »
Samotný vector (nějaké 3 ukazatele) je přímo tam, kam ho dáte. Pokud bude lokální proměnná ve funkci, tak bude na stacku. Pokud bude jako členská proměnná nějaké třídy, tak bude tam co instance té třídy. Ten objekt vlastně to kde je nemusí vůbec řešit.  (statické vázání jako pojem slyším poprvé, popravdě mi to spíš evokovalo keyword static, který je úplně něco jiného)

Pro svoje data si vector alokuje kus dynamické paměti na heapu. Ta bude na heapu vždycky, bez ohledu na to kde je ten samotný vector. Tu paměť si spravuje ve vlastní režii a programátora zajímá jen to, že přidávání do vectoru může způsobit realokaci. Paměť pro data si musí alokovat dynamicky, protože je její velikost neznámá.

Ten vector samotný nemá smysl samostatně dynamicky alokovat na haldě. Ve chvíli kdy je jako lokální proměnná nebo member, tak má jasně určené odkdy dokdy existuje, automaticky se na konci jeho života vyvolá destruktor a uvolní paměť.

Ten shared_ptr to má stejně. Samotný je na stacku nebo tam co objekt jehož je členem. A odkazuje se na paměť na haldě.

Já osobně paměť prohlížím v debuggeru. Čtení z libovolné adresy mi přijde zbytečné, ale z nějakého objektu se dají byty vykopírovat pomocí memcpy do pole surových charů.

A pro zjišťování velíkostí a rozložení struktur používám :
int a[sizeof(Neco)] = 0;
int b[offsetof(Trida, member)] = 0;
Překlad selže a překladač mi v chybě napíše, do jak velkého pole jsem se snažil ten int nacpat.
« Poslední změna: 07. 08. 2020, 21:13:52 od Jiří Havel »

alex6bbc

  • *****
  • 1 431
    • Zobrazit profil
    • E-mail
Re:C++ stack vs heap.
« Odpověď #3 kdy: 07. 08. 2020, 22:08:44 »
jak je nekde */new tak to jde na heap.
i uvnitr unique_ptr, shared_ptr je uvnitr schovane */new, tak odkazuje na data na heapu.
ale je v pohode to predavat hodnotou protoze, to je jen par hodnot na stacku co se musi zkopcit.
takze ma v sobe uvnitr vector nejake */new, tak pak je tahle * cast na heapu, ale jeho velikost atd. jsou na stacku.

Idris

  • *****
  • 2 286
    • Zobrazit profil
    • E-mail
Re:C++ stack vs heap.
« Odpověď #4 kdy: 07. 08. 2020, 23:33:45 »
Obecně lze říci, že kde co alokuje “vector” apod. závisí na implementaci. Třeba “string” má typicky několik verzí podle délky řetězce, jde o implementační detaily za účelem optimalizace. Knihovní třídy, zejména kolekce, by se vždy měly vytvářet na zásobníku, kde pak je jen hlavička obsahující ukazatel na dynamicky alokovanou paměť, o který se ale stará kolekce transparentně. U chytrých ukazatelů se nedoporučuje používat “new,” mají vlastní funkce pro vytvoření instancí (make_shared, make_unique, ...).


Re:C++ stack vs heap.
« Odpověď #5 kdy: 08. 08. 2020, 01:13:52 »
Ďakujem Vám za ozpovede, pomohlo mi to aby som sa v niektorých záležitostiach uistil a zorientoval. Ono teóriu okolo stacku a heapu relatívne dobre poznám, ale ajtak ma niektoré veci prekvapili.

Takže ešte raz by som sa chcel uistiť ak objekt alokujem na heape a jeho member "objectMember" som vytvoril takto:

Kód: [Vybrat]
class MyClass {
    vector<char> objectMember {'a', 'b', 'c'};
}

auto myObject = make_shared<MyClass>();

Bude celý objekt vrátane toho memberu "objectMember" alokovaný na heape?

Jo a pán Idris spomínal, že namiesto syntaxe:

Kód: [Vybrat]
shared_ptr<vector<char>> chars(new vector<char>());
je lepšie používať:

Kód: [Vybrat]
auto chars = make_shared<vector<char>>();
páči sa mi, že je to menej ukecané a že sa to dá použiť aj vo výraze, ale ako zapíšem napr toto:

Kód: [Vybrat]
shared_ptr<vector<char>> chars(new vector<char>{'a', 'b', 'c'});

Re:C++ stack vs heap.
« Odpověď #6 kdy: 08. 08. 2020, 01:21:39 »
Už som si niečo vygooglil:

Kód: [Vybrat]
auto chars = make_shared<vector<char>>(initializer_list<char>{'a', 'b', 'c'});
toto posledné sa mi až tak nepáči, neni nato ešte nejaká krajšia syntax?

Re:C++ stack vs heap.
« Odpověď #7 kdy: 08. 08. 2020, 07:42:24 »
Se standardní knihovnou a initializer listem kratší syntax neuděláš, můžeš ale použít variadické šablony a udělat třeba tohle:
Kód: [Vybrat]
template <typename T, typename... Args>
std::shared_ptr<std::vector<T>> make_shared_vector(Args&&... args)
{
    auto vec = std::make_shared<std::vector<T>>();
    vec->reserve(sizeof...(Args));
    (vec->emplace_back(std::forward<Args>(args)), ...);
    return vec;
}

int main()
{
    auto vec = make_shared_vector<char>('a', 'b', 'c');
    return 0;
}
Tohle používá fold expression a potřebuje C++17, ale jsou možné i jiné varianty, tady najdeš víc možností, jak to udělat: https://tristanbrindle.com/posts/beware-copies-initializer-list
« Poslední změna: 08. 08. 2020, 07:52:00 od linuxak »


tecka

  • ***
  • 138
    • Zobrazit profil
    • E-mail
Re:C++ stack vs heap.
« Odpověď #9 kdy: 08. 08. 2020, 11:07:11 »
Bude celý objekt vrátane toho memberu "objectMember" alokovaný na heape?
Ano, bude uvnitř toho objektu. Ta inicializace nemá na umístění vliv, je to jen syntax sugar, abys nemusel postaru psát inicializaci do konstruktoru.
« Poslední změna: 08. 08. 2020, 11:10:53 od tecka »

Re:C++ stack vs heap.
« Odpověď #10 kdy: 08. 08. 2020, 18:47:10 »
Se standardní knihovnou a initializer listem kratší syntax neuděláš, můžeš ale použít variadické šablony a udělat třeba tohle:
Kód: [Vybrat]
template <typename T, typename... Args>
std::shared_ptr<std::vector<T>> make_shared_vector(Args&&... args)
{
    auto vec = std::make_shared<std::vector<T>>();
    vec->reserve(sizeof...(Args));
    (vec->emplace_back(std::forward<Args>(args)), ...);
    return vec;
}

int main()
{
    auto vec = make_shared_vector<char>('a', 'b', 'c');
    return 0;
}
Tohle používá fold expression a potřebuje C++17, ale jsou možné i jiné varianty, tady najdeš víc možností, jak to udělat: https://tristanbrindle.com/posts/beware-copies-initializer-list

Diky. Tento adaptér vyzerá fajn.

Bude celý objekt vrátane toho memberu "objectMember" alokovaný na heape?
Ano, bude uvnitř toho objektu. Ta inicializace nemá na umístění vliv, je to jen syntax sugar, abys nemusel postaru psát inicializaci do konstruktoru.

Asi som sa nevyjadril jasne. Tým zápisom som nemyslel syntaktický cukor (inicializácia mimo konštruktoru), ale statickú vazbu. Member vytvorený bez operátoru new (make_shared, make_unique, alebo malloc) by mal vytvoriť objekt na stacku. Ale ak je celý objekt vytvorený na heape tak potom aj jeho member sa vytvorí na heape?

Re:C++ stack vs heap.
« Odpověď #11 kdy: 08. 08. 2020, 21:21:13 »
Už som si niečo vygooglil:

Kód: [Vybrat]
auto chars = make_shared<vector<char>>(initializer_list<char>{'a', 'b', 'c'});
toto posledné sa mi až tak nepáči, neni nato ešte nejaká krajšia syntax?

Trochu odbočím. vector je jedna z tříd, které takřka nedává smysl alokovat na heapu tímhle způsobem. Za nějakých 15+ let programování v c++ jsem udělal "new std::vector..." asi tak jednou a to jsem dělal hodně velkou divočinu. "new vector", "new string" a podobně je celkem spolehlivý způsob jak identifikovat špatně přeučeného Javistu.

Takže syntaxe, kterou chcete je pravděpodobně :
Kód: [Vybrat]
std::vector<char> chars = {'a', 'b', 'c'};

C++ se snaží o hodnotovou sémantiku. Pokud to jenom trochu jde, pak se věci chovají jako int - přímo jako chlívek na hodnoty ne jako nějaká samostatně existující entita někde na haldě.

Na haldě se obvykle vytváří "pár" objektů. Všechno ostatní jsou buď lokální proměnné ve funkcích, nebo membeři nějakých tříd. A i u těch dynamicky alokovaných objektů je často identitfikovatelný jeden zodpovědný vlastník, který má na něj unique_ptr. shared_ptr je pro případy, kdy se nedá obejít bez spoluvlastnictví. Cena za to je, že přestává být z kódu jasně patrné, kdy ten objekt zanikne.

Asi som sa nevyjadril jasne. Tým zápisom som nemyslel syntaktický cukor (inicializácia mimo konštruktoru), ale statickú vazbu. Member vytvorený bez operátoru new (make_shared, make_unique, alebo malloc) by mal vytvoriť objekt na stacku. Ale ak je celý objekt vytvorený na heape tak potom aj jeho member sa vytvorí na heape?
Member - členská proměnná nějaké třídy má pro sebe kus paměti přímo v té třídě. Velikost objektu je daná velikostí jeho memberů, protože jsou poskládané jeden za druhým. Member bude tam, kde ten objekt, kterého je členem.

tecka

  • ***
  • 138
    • Zobrazit profil
    • E-mail
Re:C++ stack vs heap.
« Odpověď #12 kdy: 09. 08. 2020, 16:57:31 »
Asi som sa nevyjadril jasne. Tým zápisom som nemyslel syntaktický cukor (inicializácia mimo konštruktoru), ale statickú vazbu. Member vytvorený bez operátoru new (make_shared, make_unique, alebo malloc) by mal vytvoriť objekt na stacku. Ale ak je celý objekt vytvorený na heape tak potom aj jeho member sa vytvorí na heape?
Nevím, co jsi tím myslel, protože sis ten pojem v tomto kontextu nejspíš sám vymyslel. Ale to, co jsi napsal, je inciializace při konstrukci objektu. Tečka. Member bude uvnitř objektu. Vždycky. Tečka.

Re:C++ stack vs heap.
« Odpověď #13 kdy: 09. 08. 2020, 23:07:26 »
Už som si niečo vygooglil:

Kód: [Vybrat]
auto chars = make_shared<vector<char>>(initializer_list<char>{'a', 'b', 'c'});
toto posledné sa mi až tak nepáči, neni nato ešte nejaká krajšia syntax?

Trochu odbočím. vector je jedna z tříd, které takřka nedává smysl alokovat na heapu tímhle způsobem. Za nějakých 15+ let programování v c++ jsem udělal "new std::vector..." asi tak jednou a to jsem dělal hodně velkou divočinu. "new vector", "new string" a podobně je celkem spolehlivý způsob jak identifikovat špatně přeučeného Javistu.

Ďakujem za vysvetlenie, teraz keď viem že buffer kontaineru sa alokuje v heape mi to už aj celé dáva zmysel (Nie som síce javista, ale používal som jazyky z podobného súdka ako Java)

A ďakujem Vám aj za ostatné odpovede, konečne v tom mám jasno. Toto presne som potreboval vedieť.