- Milyen videókártyát?
- Bambu Lab 3D nyomtatók
- Milyen egeret válasszak?
- NVIDIA GeForce RTX 3080 / 3090 / Ti (GA102)
- SSD kibeszélő
- Kezdő fotósok digitális fényképei
- AMD K6-III, és minden ami RETRO - Oldschool tuning
- Milyen belső merevlemezt vegyek?
- Samsung LCD és LED TV-k
- Azonnali informatikai kérdések órája
Új hozzászólás Aktív témák
-
jattila48
aktív tag
válasz
jattila48 #3190 üzenetére
Közben kicsit jobban belegondoltam, és az igazító kód valószínűleg a többszörös öröklődésből adódó this pointer igazítást is, és az esetleges virtuális fv. híváshoz szükséges vftbl pointer igazítást is elvégzi. Az incomplete BodyClass forward deklaráció miatt a fordító nem látja, hogy a BodyClass *pimpl pointer valójában egy BodyClass-ból publikusan leszármazott osztályra is mutathat, sőt az is lehet, hogy az osztálynak egy másik publikus ősosztálya is van, amitől előbb örököl, mint a BodyClass-tól:
class DescClass : public UnknownClass, public BodyClass{...}
A handle_class.cpp-ben pedig a HandleClass ctor.-a:
HandleClass::HandleClass():pimpl(new DescClass ()){}Ebben az esetben a pimpl valójában az UnknownClass részre mutat (mivel az van előbb az ősök között), és nem a BodyClass részre, ahogy kéne. Ezért ha a DescClass * típusú pointert a pimpl értékül kapja, azt igazítani kell a BodyClass részre (ezt végzi egyébként a thunk kód többszörös öröklődés esetén).
Virtuális tfv. esetén a member function pointer szintén fordítás idejű konstans, azonban nem a tfv. közvetlen címe, hanem a virtuális fv. táblában a rá vonatkozó bejegyzés indexe (ez nem biztos, ezt csak így gondolom). Ezért az esetleges öröklődésből adódó vftbl címet is igazítani kell, mivel a fordító ugye nem látja, hogy a BodyClass valójában egy ebből leszármazott osztállyal példányosult. Az igazító kódban szerintem az első sor
004116D6 cmp dword ptr ds:[4168B4h],0
annak vizsgálata lehet, hogy tfv. virtuális-e. Ha nem, akkor egyszerűen a thunk kód kerül végrehajtásra a linkelési időben kitöltött információk alapján. Ha virtuális, akkor a másik ágban a vftbl. megfelelő beállítása, és a thunk végrehajtása történik. Mivel rossz működés esetén ebben az első utasításban rossz a "globális" cím, ezért gondolom, hogy a linker (vagy a betöltő) ront el valamit. Az, hogy a call ezután indirekt címzéssel történik, arra utal, hogy futási időben dől el, hogy milyen kódrészletet kell végrehajtani (talán maga az esetleges virtuális hívás?). Remélem érthető volt, és elnézést ha kicsit hosszúra nyúlt. -
jattila48
aktív tag
válasz
jattila48 #3189 üzenetére
"Úgy tűnik, hogy a forward deklarációval nincs elég információja a BodyClass osztályról, hogy a pimpl és az mfp alapján meghatározza a BodyClass f tfv.-ének valódi címét."
Ez butaság, a BodyClass f tfv.-ének címe konstans, és jól van eltárolva. Az aktuális BodyClass objektumra mutató pipl-et akarja hozzá igazítani. Talán mégis inkább virtuális tfv.-re mutató member function pointer esetén lehet erre szükség. Ha tudná, hogy az f nem virtuális, akkor erre nem is lenne szükség. Ez csak tipp.
-
jattila48
aktív tag
válasz
jattila48 #3186 üzenetére
A másik programomban teljesen hasonló kódra a VS ezt fordította:
auto pimpl=hc.pimpl;
00426AAF mov eax,dword ptr [ebp-1Ch]
00426AB2 mov ecx,dword ptr [eax]
00426AB4 mov dword ptr [ebp-28h],ecx
.
.
.
auto sc=hc.f(1);
00426ABF push 1
00426AC1 lea eax,[ebp-74h]
00426AC4 push eax
00426AC5 mov ecx,dword ptr [ebp-1Ch]
00426AC8 call HandleClass::f (04113CFh)
.
.
.
auto sc2=(pimpl->*hc.mfp)(2);
00426AD7 mov esi,esp
00426AD9 push 2
00426ADB lea eax,[ebp-1D4h]
00426AE1 push eax
00426AE2 mov ecx,dword ptr [ebp-28h]
00426AE5 call dword ptr ds:[42B8A0h]Látható, hogy a mfp-én keresztüli hívás előtt nincs befordítva a "fölösleges" kód. Egyébként a kétfajta hívás kódja teljesen hasonló, csak az első call direkt címzéssel, a második pedig indirekt címzéssel történik. Viszont a második call már közvetlenül a BodyClass f tfv.-ét hívja, vagyis valóban nincs forwarding call overhead (ami egyébként elég jelentős). Fórumokon (pl. StackOverflow) azt olvastam, hogy a pimpl-et nem lehet enélkül megcsinálni. Ezek szerint mégis. Ez azért működik, mert az incomplete type-ok member pointerei is forward deklarálhatók.
-
jattila48
aktív tag
Attól tartok, nem egészen érted miről van szó. A member pointerek nem függenek egyetlen példánytól sem, hanem az osztály (nem az objektum!) tagjaira hivatkozó fordítás idejű konstansok. Úgy kell elképzelni, mint offset-et, ami az osztály kezdetéhez képest mutatja az adott tag helyét az osztályban. Pl. struct semmi{int a; int b;} osztályban a semmi::* pa=&semmi::a és semmi::* pb=&semmi::b deklarációkkal a pa=0 és pb=4 konstans értékeket vesz fel (feltehetőleg), mivel az a tag offsetje az osztályhoz lépest 0, a b-é pedig 4 (ha az a mérete 4 byte volt). Ugyanígy (nem virtuális) tfv.-ekre is, csak ekkor nem az offset, hanem a tfv. címe mint fordítás idejű konstans kerül a member function pointerbe. A HandleClass::mfp nálam ilyen member function pointer, ami történetesen a BodyClass::f tfv.-ének címét tartalmazza (és ez független a BodyClass bármely példányától, egy közönséges fv. cím). Ezért, mivel nem függ sem a HandleClass, sem a BodyClass egyetlen példányától sem, nyugodtan lehet statikus, hiszen minden példányban ugyanaz a konstans érték. Amikor (pimpl->*hc.mfp)(2); formában hívom a BodyClass f tfv.-ét, akkor ez nem azt jelenti, hogy az mpf a pimpl által mutatott objektum tagja lenne (mint ahogy a ->* miatt első ránézésre tűnik), hanem a BodyClass::f tfv. kapja a pimpl-et első paraméterként, 2-őt második paraméterként, (mint ahogy a tfv.-ek első paramétere mindig az aktuális példányra mutató this pointer), az mpf pedig történetesen a hc statikus tagja. Egyébként ahogy írtam, ha az mpf-et const-ként deklarálom akkor működik, különben pedig futási hibával elszáll. A VS kever valamit, mert a gcc jól fordítja. A VS is lefordítja, csak szerintem rosszul.
-
jattila48
aktív tag
válasz
jattila48 #3182 üzenetére
Úgy néz ki, megoldódott a probléma. Ha az mfp member function pointert const-nak deklarálom, úgy jól működik. handle_class.h-ban:
int (BodyClass::* const mfp)(int);
handle_class.cpp-ben:
int (BodyClass::* const HandleClass::mfp)(int)=&BodyClass::f;
pimpl_proba.cpp-ben visszaírtam egy utasításba a debug miatt több sorra szedett hívást:
BodyClass *pimpl=hc.pimpl;
n=(pimpl->*hc.mfp)(2);Szerintem const nélkül is jól kéne működnie, úgyhogy a VS-ben lehet hiba. Továbbra sem értem azonban, hogy mi az a "fölöslegesnek" tűnő kód, amit a hívás előtt generál (ez volt hibás ha nem const volt az mfp):
004116D6 cmp dword ptr ds:[4168B4h],0
004116DD jne main+80h (04116F0h)
004116DF mov eax,dword ptr [pimpl]
004116E2 add eax,dword ptr ds:[4168ACh]
004116E8 mov dword ptr [ebp-100h],eax
004116EE jmp main+0A9h (0411719h)
004116F0 mov ecx,dword ptr [pimpl]
004116F3 add ecx,dword ptr ds:[4168B0h]
004116F9 mov edx,dword ptr [pimpl]
004116FC add edx,dword ptr ds:[4168B0h]
00411702 mov eax,dword ptr [edx]
00411704 mov edx,dword ptr ds:[4168B4h]
0041170A add ecx,dword ptr [eax+edx]
0041170D add ecx,dword ptr ds:[4168ACh]
00411713 mov dword ptr [ebp-100h],ecx
00411719 mov esi,esp
0041171B push 2
0041171D mov ecx,dword ptr [ebp-100h]
00411723 call dword ptr ds:[4168A8h]A pimpl-et igazgatja, de nem tudom miért. Másik programomban ez nincs. Valami hasonlót a gcc is csinál. Szerintetek mi lehet ez?
-
jattila48
aktív tag
válasz
jattila48 #3181 üzenetére
Közben annyit sikerült kideríteni, hogy ha a HandleClass::mfp BodyClass tfv.-re mutató member function pointer nem statikus, hanem sima tag, akkor jól működik. Azzal, hogy az mfp statikus, azt akartam elkerülni, hogy a hc-én keresztül "meg kelljen hivatkozni", hiszen ez is overhead lenne. Nem értem miért nem működik, hiszen a a member pointerek fordítás idejű konstansok, ezért lehet az mfp statikus. Ugyanakkor lefordul, tehát a VS szerint is lehet.
-
jattila48
aktív tag
Sziasztok!
Egy olyan pimpl megvalósítást szeretnék csinálni, amiben nincs a handle osztály (ez tartalmazza a pimpl pointert) tfv.-einek hívásából adódó forward hívás overhead. "Normálisan" a handle osztály tfv.-ei csak egy forwardolást csinálnak a body osztály (ebben van az implementáció) tfv.-einek hívásával pimpl->f() formában. Ezt az overheadet akarom elkerülni, vagyis a body osztály tfv.-eit közvetlenül akarom hívni.
Példa://pimpl_proba.cpp file
#include "handle_class.h"
int main(int argc,char *argv[])
{
HandleClass hc;
BodyClass *pimpl=hc.pimpl;
int n=0;
n=hc.f(1);
int (BodyClass::*ff)(int)=HandleClass::mfp;
n=(pimpl->*ff)(2);
n=1;
return 0;
}
//handle_class.h file
#ifndef _HANDLE_CLASS_
#define _HANDLE_CLASS_
class BodyClass;
struct HandleClass{
HandleClass();
~HandleClass();
BodyClass *pimpl;
int f(int n);
static int (BodyClass::*mfp)(int);
};
#endif
//body_class.h file
#ifndef _BODY_CLASS_
#define _BODY_CLASS_
class BodyClass{
public:
BodyClass();
int f(int);
private:
int a;
};
#endif
//handle_class.cpp file
#include "handle_class.h"
#include "body_class.h"
HandleClass::HandleClass():pimpl(new BodyClass()){}
HandleClass::~HandleClass(){delete pimpl;}
int HandleClass::f(int n){
return pimpl->f(n);
}
int (BodyClass::*HandleClass::mfp)(int)=&BodyClass::f;
//body_class.cpp file
#include "body_class.h"
BodyClass::BodyClass():a(5){}
int BodyClass::f(int n){
return a+n;
}A pimpl_proba.cpp fájl nem függ a body_class.h fájltól, vagyis ha a BodyClass-ban megváltozik az implementáció, nem kell újrafordítani a pimpl_proba.cpp-t. Azonban az n=hc.f(1); fv. hívás a return pimpl->f(n); fv. hívással két fv. hívást eredményez, amit szeretnék elkerülni. Ezért a HandleClass osztálynak van egy statikus BodyClass tfv.-re mutató member function pointere, ami a BodyClass f tfv.-re mutat. Az n=(pimpl->*ff)(2); utasítás közvetlenül ezen keresztül hívja meg a BodyClass f tfv.-ét, és így megspórolok egy forwarding fv. hívást (csak a debuggolás miatt lett több sorra szedve). Ez így lefordul VS2012 alatt, azonban access violation-nel elszáll. Belenéztem a lefordított assembly kódba, és szerintem hibás amit generál,és részben "fölöslegesnek" tűnik. Ugyanakkor gcc-vel simán lefordul és fut. Bár a gcc is mintha "fölösleges" kódot generálna a közvetlen hívás előtt (talán this pointer igazítás, virtuális tábla keresés?). Ezt a módszert már használtam nagyobb programban is, ott a VS is jól fordította és a "fölösleges" kódnak sem láttam nyomát.
Kérdés: mi lehet a baj?
A válaszokat előre is köszönöm, de kérem, aki hozzászól, a lényegre koncentráljon, ne arra hogy minek ez nekem, a pimpl miért nem private, és miért nem unique_ptr, stb. -
-
jattila48
aktív tag
válasz
AsterixComic #3101 üzenetére
1. Ha átdefiniálod a new operátort pl. debug céljából. Ekkor magában az átdefiniálásban nem new-t, hanem malloc-ot fogsz használni.
Vagy ha egyszerűen csak szükséged van egy bizonyos méretű pufferre, ahová később olvasol be (pl. fájlból, hálózatról) adatot. Ezt lehet persze char *p=new char[meret]; -tel is, de akkor már a malloc legalább olyan jó. Vagyis akkor, ha nem konstruálni akarsz egy előre ismert típusú objektumot, hanem csak külső forrásból (fájl, hálózat) beolvasni bináris adatot, aminek a típusát esetleg nem is ismered előre. Ilyen lehet pl, ha IP csomagokat olvasol be, amikről előre nem tudod, hogy TCP vagy UDP csomagot fog-e tartalmazni. Ekkor a típus megállapítása után (IP protokoll jelzés) az IP payload részére egyszerűen egy C pointercasttal "ráhúzod" a TCP vagy UDP struktúrát. -
jattila48
aktív tag
válasz
dobragab #3067 üzenetére
De hát a fájl lezárás most is a dtor-ban van, de nem a ctor-ban van a megnyitás.
B leszármazottai abban különböznek egymástól, hogy más-más helyről olvassák be az egyébként ugyanolyan formátumú bináris adatot. Tehát a leszármazott osztályok egyetlen dolga, hogy ezt megtegyék, és a blob-ot átadják az ősosztálynak (erre van az init). A B nem függ a leszármazottaktól, mert ő már csak a blob-on dolgozik, de nincs értelme önálló B objektumot létrehozni, mert a blob-ot mégis csak be kell olvasni valahonnan.
"Tényleg jobb lenne D konstruktorába pakolni az allokációt, de ebben a formában nem lehet megoldani. Az ős-ctor hívása mindenképp előbb van, mint a ctor törzse, úgyhogy arra nincs esélyed normálisan megoldani."
Hát pont erről beszélek. Ha viszont a D ctor-ában szeretném megoldani az allokációt, akkor mindenképpen B::init-et kell hívnom a D ctor-ának törzsében. Ez lehet, hogy szerinted nem normális megoldás (alapesetben szerintem sem az), de mivel hogy B nem példányosítható, ezért elnézhetőnek tartom. Ha ezt nem teszem meg, akkor valóban csak ez a fajta factory megoldás marad amit írtál, azonban itt az erőforrások átvétele (shared vagy unique pointerek vagy egyéb erre a célra létrehozott osztályok formájában) sokkal macerásabb és több hibalehetőséget rejt, mint az init fv."Mindenesetre a kötelezően hívandó init() szerintem ennél sokkal büdösebb: el lehet felejteni a hívását, és B többet tud, mint a feladata."
Mint ahogy fent írtam, szerintem nem, de ez már ízlés kérdése. Azt hogy el lehet felejteni a hívását, ennyi erővel D bármely más tagjának az inicializálását is el lehet felejteni, ahogy írtam. Tulajdonképpen úgy tekinthető, hogy az init nem a B-t inicializálja, hanem a D inicializálásához szükséges. "Normál" esetben is szokás a ctor-ban különböző tagokat inicializáló fv.-eket hívni. B egyáltalán nem tud többet mint a feladata, mert a forrásról semmit sem tud, csak a blob-ot kapja meg, ami minden leszármazott esetén ugyanolyan formátumú.
"Alapesetben nem kéne, hogy egy osztálynak legyen ctor-ként működő init-je."
Valóban. De ha nem példányosítható? Erről szólt az egész elmélkedésem."Kicsit más jellegű lenne az a megoldás, ha B kap tisztán virtuális read() és write() függvényeket, és azokat override-olja D. Viszont read()-et nem hívhatod ctorból, úgyhogy ez nem működik."
Azon kívül, hogy ctor-ból nem működik, nem lehet megcsinálni, mert forrásonként különböző paraméterezést igényelne. Másrészt ekkor már tényleg fölöslegesen sokat tudna a B és függne a leszármazott osztálytól.
Minden esetre köszönöm az eddigi hozzászólásaidat, most ezt a kétféle megoldást látom alkalmasnak, a mondott okok miatt azonban a sajátomat fogom választani.
-
jattila48
aktív tag
válasz
dobragab #3059 üzenetére
"Ha B önmagában nem olvasható be, vagy D-vel együtt kellene, mert B beolvasása után kellenek még az adatok, D-t is adhat vissza a beolvasó függvény."
Factory? És ez a D-t hogy állítja elő? Egy másik construktorral? Ugyanezzel nem lehet, mert végtelen rekurzió lenne. Hogyan inicializálná a B részét a visszaadott D-nek? Hiszen most pont ezen küzdünk. Vagy ha már a beolvasó fv. visszaad D-t, akkor minek ezt újra felhasználni a D egy másik ctor-ában? Akkor már jó a most visszaadott is. Vagy a beolvasó fv. inicializálatlan B részű D-t adna vissza? Ez ismét csak kétfázisú inicializálás lenne, ráadásul rosszabb mint az enyém, mert konkrétan létre is kéne hoznod félig konstruált D-t.
A statikus tfv.-nyel való inicializálás (nem több, hanem osztályonként csak egyetlen stfv.) életképes lehet, ha nem B-t ad vissza (főleg nem pedig D-t), hanem valami egyszerű struktúrában a bináris adatot (pointer+méret: röviden blob). Itt viszont megint csak az történik, hogy a D ctor-ában meg KELL hívnod egy bizonyos fv.-t (a beolvasó statikus tfv.-t), ugyanúgy ahogy az én megoldásomban is meg KELL hívni a B init tfv.-ét. A különbség annui, hogy nálad a D ctor-ának inicializáló listájában, míg nálam a törzsben kell ezt megtenni. Ja, és szükség lesz egy "köztes" blob struktúrára (amit a beolvasó visszaad), aminél már csak az a kérdés, hogy mikor, és hogy szűnik meg. -
jattila48
aktív tag
válasz
dobragab #3059 üzenetére
Ha jól értem, akkor fájlból, innen-onnan binárisan beolvasó statikus függvény a D osztály statikus fv.-e. Minden egyes D osztályhoz csak 1 ilyen statikus fv. van (nem több forrástól függően), hiszen ha a forrás különböző, akkor az osztálynak is különbözőnek kell lenni. Vagyis igazából a forrás beolvasása a D osztály feladata, bár ha jól értem, szerinted nem. Azonban pl. a fájlból való beolvasáshoz tartozik egy file handle, amit az objektum megszűnésekor le kell zárni (RAII). Ez a file handle természetesen nem lehet a B osztály tagja, mert registryből olvasva már egy registry handle fog hozzá tartozni, stb. Vagyis egész természetes módon adódik, hogy a beolvasó fv.-nek nem statikusnak, hanem rendes tfv.-nek kell lenni. Ha egyetlen D osztály lenne, aminek forrásonként külön beolvasó statikus fv.-ei lennének, akkor a D osztály valójában felesleges, hiszen free fv.-ekkel ugyanez megoldható, a bináris adat pedig közvetlenül átadható lenne B konstruktorának.
Ha valóban a D statikus tfv.-eiről írtál, akkor a
"A statikus függvények meg lehetnek protected-ek, hogy a RAII-t ne lehessen kívülről zavarni, és hívhasd B / D ctorából." mondatodat nem értem. Ez a mondatod arra utal, hogy a B statikus tfv.-eiben valósítanád meg a beolvasást, ami azért nem lenne jó, mert előre nem tudhatja a B írója, hogy milyen forrásokból lesz a beolvasás.
"B innentől önállóan is működőképes osztály": Mármint úgy, hogy a konstruktorának meg tudunk adni bináris adatot is (pointer+méret: röviden blob)? Erre egyáltalán nincs szükség, hiszen ha ilyen osztály kellene, írnék egy leszármaztatott D-t ami pont ezt tudja. -
dobragab
addikt
válasz
jattila48 #3058 üzenetére
Most már nekem is kicsit koncepcióhiba-szagú, persze simán lehet, hogy indokolt.
B ellátja a funkcióját, valamilyen adatokon dolgozik. Adsz neki egy ctort, ami az adattagjait darabonként inicializálja, és nem bűvészkedik a bináris adatokkal.
A bináris memóriaterületből olvasás, feldolgozhatóvá alakítás ilyen formában (pointer + méret) nem egy osztály dolga, hanem egy függvényé. Tipikusan egy statikus függvényé, ami megkapja fájlnevet, akármit, és visszaad érték szerint egy B-t. Én ebből csinálnék többet, attól függően, hogy fájl / registry / memória a forrás. Ezek hívják majd B adattagonként inicializáló konstruktorát. A statikus függvények meg lehetnek protected-ek, hogy a RAII-t ne lehessen kívülről zavarni, és hívhasd B / D ctorából. Írj B-nek move ctort, akár default is jó lehet.
Ezzel különválasztottad a program egyáltalán nem összetartozó részeit. B innentől önállóan is működőképes osztály, és hogy a te programodban mindig fájlból érkezik, az mellékes.
Ezután D konstruktora valahogy így nézhet ki:
D::D(std::string filename, T data) :
B{std::move(D::ReadFrom(filename))},
data{data}
{ }Ha B önmagában nem olvasható be, vagy D-vel együtt kellene, mert B beolvasása után kellenek még az adatok, D-t is adhat vissza a beolvasó függvény.
-
jattila48
aktív tag
válasz
dobragab #3057 üzenetére
Akkor egy kicsit konkrétabban: B egy olyan osztály, ami bináris adatot tartalmazó memória területet dolgoz fel. A memória terület pointerét, és méretét kapja meg az init tfv.-ből. Van nem default ctor-a, amiben egyéb paramétereket vár. A leszármazott D osztály valahonnan (pl. file, registry, stb.) beolvassa a bináris adatot, lefoglalja a megfelelő memória területet, és ennek a címét és méretét a B::init-nek argumentumként átadva B::init meghívásával (a D ctor-ában) teljesen inicializálja B-t. A D ctorának fájl név a paramétere, ha fájlból olvas, vagy registry kulcs, ha registryből, stb. A probléma az, hogy ha nem az init-et hívom, hanem a B ctorának akarnám átadni a memória területet, akkor az adatot előbb kellene beolvasni, mint ahogy a B ctora meghívódik. Azonban az adat beolvasást mindenképpen a D ctor-ában szeretném elvégezni, nem pedig kívül (pl. RAII miatt). Ha a B ctor-ának az egyéb paraméterek mellett a memória pointer és a méret is paramétere lenne, akkor ezeket valahogy az inicializáló listában kéne előállítani (akár D statikus fv.-ei segítségével). Ehelyett én azt mondom, hogy mivel B-t nem lehet példányosítani, a D pedig nem tekinthető teljesen megkonstruáltnak ha nem hívja meg ctor-ából az init-et, talán elfogadható a B kétfázisú inicializálása. Ez koncepcionálisan nem mond ellent annak, hogy ne hozzunk létre félig konstruált objektumot, mivel ha D a konstruktorában nem hívja meg az init-et, akkor maga a D tekinthető félig konstruáltnak.
-
LordX
veterán
válasz
EQMontoya #3035 üzenetére
Lehet jó falura, de ugyanannyit kell írni (na jó, a függvény/osztálynevek hosszabbak), ha rendesen használsz <random>-ot.
Map: Nem értek egyet: mivel a map node-okban tárolja az adatot, amiben benne van a kulcs, az érték, és jellemzően 3 pointer (left, right, parent), ezért masszívan több memóriát foglal, mint a vector: Az összes általam ismert 64 bites implementációban 32 byte + sizeof(pair<K,V>) a node méret + allocation overhead. Win x64-en a sizeof 16 bye, tehát 40 byte a blokk, ahol az overhead átlagosan 56 byte (!!), azaz 96 byte. Per node. Ja, és a map-nek kell egy extra node (az end node). Szóval 10 elemhez kell 1056 byte heap memória map-el.
Vector esetében ez N*sizeof(int) + overhead, ami most 10*4+56 = 96, összesen a 10 elemre. 100 elemre ez 9696 vs 449 byte.
Szóval ha a cellák több, mint ~9%-át használod ki, akkor a vector nyer map ellen. És még nem is beszéltünk mennyire cache-friendly a vector, és mennyire cache-ellenséges a map; hogy a vektorban indexelsz, nem keresel.
Azért ha tényleg el kell tárolnod a kulcsot, egy kategóriával szebben áll a map, de még mindig vesztésre áll, nem véletlen van a flat_map a boost-ban (ami gyakorlatilag egy map interfész vector felett).
-
ToMmY_hun
senior tag
válasz
jattila48 #2877 üzenetére
Lehet, hogy túlbonyolítom. Leírom mi a végső cél. Adott egy program, amely számításokat végez és socket-en küldi le ezeknek az eredményét 5 kliensnek. A klienseknek folyamatosan csatlakozva kell lenniük, különben az adatok érvénytelenek. A főprogram alatt gondolom a fő végrehajtási szálat érted. A további szálakat úgy oldottam meg, hogy csináltam egy SocketComm osztályt, abban van egy statikus metódus, ami a kommunikációért felel. Ez a statikus metódus az osztályban lévő attribútumokból olvas és küld, magát a kapcsolatot nem ő építi fel hanem az osztály konstruktora. A thread indításáért felelős függvénynek a statikus függvény pointerét adom át (ezt nevezhetjük callbacknek), amit így futtat a threadben és ezzel azt is megoldottam, hogy a későbbiekben ha változik a kliensek száma, akkor az indítások számát kell csak változtatni.
UI: Egyelőre azért van egy thread, mert csak server -> kliens irányú a kommunikáció, a válaszokat később szeretném beépíteni és ahhoz valóban kelleni fog a kliensenkénti thread.
-
jattila48
aktív tag
válasz
ToMmY_hun #2867 üzenetére
Nem igazán értem mit szeretnél, de tartok tőle, hogy túlbonyolítod. Ha TCP listen socket-ről van szó, akkor a főprogramban listen-el, majd az accept-ált connected socket-tel új thread-et indítasz. Minden kapcsolathoz külön thread-et, tehát nem 1 vagy 2 thread-ed lesz, hanem annyi, amennyi kapcsolatot fogadsz (természetesen ezek dolguk végeztével (pl. a kliens lezárja a kapcsolatot) "elhalnak"). Az új thread-nek paraméterként csak a konnektált socket-re lesz szüksége, amit void *-ra castolva a CreateThread-nek átadsz.
Te valamiféle callback függvényt akarsz így member function pointerrel csinálni? Ez egyfajta delegate lenne? Miért csak 1 thread-et akarsz indítani? -
ToMmY_hun
senior tag
válasz
EQMontoya #2866 üzenetére
Először is köszi a választ! Azért szeretném ezt használni, mert lenne egy osztályom amiben egy szerver socketet szeretnék futtatni és a socketet új threadben akarom indítani. Az új threadet az osztály konstruktorában indítom és ugye ott kéne neki átadni a futtatandó metódust is, ezért lenne szükségem a pointerre. Az osztálynak csak a socket által kiküldött vektor feltöltése és magának a socket kommunikációnak a lebonyolítása a feladata, egy példány lesz belőle. Ha erre lenne valami szebb megoldási javaslat, akkor annak örülnék és köszönöm előre is! Addig is kipróbálom amit javasoltál.
Static metódus ugye azért nem frankó, mert akkor nem érem el az osztály belső változóit. Persze ha úgyis csak egy példány lesz, akkor lehet singleton és úgy el is érem, de nem tűnik szimpatikusnak a megoldás, tuti van ennél szebb.
-
EQMontoya
veterán
válasz
ToMmY_hun #2865 üzenetére
class A {
public:
int f();
int (A::*x)(); // <- declare by saying what class it is a pointer to
};
int A::f() {
return 1;
}
int main() {
A a;
a.x = &A::f; // use the :: syntax
printf("%d\n",(a.*a.x)()); // use together with an object of its class
}Ugyanakkor muszáj felhívom a figyelmed, hogy ha ilyet akarsz csinálni, akkor valamit nagyon elb@sztál a tervezésnél, vagy nagyon nem ismersz valami olyan patternt, amivel kiváltható lenne ez a működés. Látatlanban nem tudom, hogy pontosan mi lehet szükséged, de a visitort, decoratort, nézd meg.
-
ToMmY_hun
senior tag
Sziasztok!
Tudnátok egy jó megoldást mondani arra, hogy miként tudnám elérni egy osztály konstruktorában az adott osztályon belüli metódus pointerét? Static-ként természetesen megy, de objektumhoz tartozó kellene.
-
EQMontoya
veterán
Én azért alapvetően igyekszem távol tartani magam tőle.
Persze, van, amikor kényelmesebb és tényleg van, de láttam már referenciát váró függvényt pointerrel meghívva lefordulni és lefutni az elb..ott konverziós konstruktorok miatt. Szóval amit nem használunk feltétlenül, az legyen explicit, és alap típusok esetén mindenképp. -
LordX
veterán
válasz
ToMmY_hun #2787 üzenetére
Ha előre tudod a szükséges típust, és csak egyféle kell egy adott helyen, akkor minek tárolod őket egy közös mapban?
Külön map az Effector-nak, Joint-nak, stb, a "factory" (off: én nem hívnám factorynak, mert a factory nem tulajdonosa a legyártás után az objektumnak, ez inkább valami storage) meg a típus alapján template:
Storage<Effector> effectors;
Storage<Joint> joints;
effectors.create("effector1", 1);
joints.create("joint1", 2.0, true);
auto &x = effectors.get("effector1");
auto &y = joints.get("joint1");Teljes kód itt: [link]
Ja, én nem szeretem a naked mutatókat; smart pointer, observing pointer objektum (asszem jön egy standard C++17-el) vagy referencia, különben nem egyértelmű, ki destruálja az objektumot, ha már nem kell.
-
EQMontoya
veterán
válasz
ToMmY_hun #2784 üzenetére
RapidXML-t próbáltam, de nem kezeli a string objektumot, csak karaktertömböt, abból meg nem kérek ha nem muszáj.
A a void* azért lenne jobb, mert később még szeretném kiegészíteni a factory-t más osztályokkal is. Emvy kolléga jól látja, a Part-ból származtatott osztályok különböző tagváltozókkal, metódusokkal rendelkeznek, így a sima Part pointer visszatérés még nem elég.
A factory miért nem template class?
Az pedig, hogy a partban nincs megfelelő infó ennek eldöntésére, tervezési hiba.Ha PartSubC mellett lesz egy PatrSubD, ami szintjén jó Neked, akkor 2x fogsz castolni, és még pár módosítás múlva láncolt listát bejáva fogsz castolni (
), vagy lesz egy PartSubVirtualCD osztály, amiből leszármazik mindkettő, tehát az objektumstruktúrádat b@szod szét fölöslegesen?
-
ToMmY_hun
senior tag
A a void* azért lenne jobb, mert később még szeretném kiegészíteni a factory-t más osztályokkal is. Emvy kolléga jól látja, a Part-ból származtatott osztályok különböző tagváltozókkal, metódusokkal rendelkeznek, így a sima Part pointer visszatérés még nem elég. Végülis a dynamic_cast működik és a miatta keletkező overhead sem probléma, mert csak az inicializálásnál lesz használva.
Szerk: Akkor inkább legyen az objektum típusát tartalmazó tagváltozó és ellenőrizzem azt?
-
ToMmY_hun
senior tag
Sziasztok!
Ismét lenne egy kérdésem, amire szeretnék egy viszonylag szép megoldást találni (szakdoga lesz belőle). Van egy metódusom egy Factory DP implementációban, amely keres több map-ben tárolt objektum között, amelyeket egyedi string azonosít. Minden map a Part osztály leszármaztatott osztályainak példányait tárolja, de ebből van 4 féle. A metódus ebből kifolyólag void pointerrel tér vissza, amit a felhasználás helyén vissza cast-olok a kívánt típusra. Ez szép és jó, de nem garantálja semmi a típusbiztos cast-olást. Ennek ellenőrzésére tudnátok ajánlani valami jól bevált módszert? A dynamic_cast<>()-ot néztem, de van ennél esetleg jobb?
Köszi előre is!
-
dabadab
titán
válasz
HussarF #2750 üzenetére
"De amit nem értek, hogy valahol mégis csak beolvassa így is a string-be"
Nem a stringbe, hanem egy random tárterületre, ugyanis a printf egy char *-ra számít, ehelyett megkapja a string értékét (nem magát a C-stílusú stringet, ami jelen esetben egyetlen nullla karakterből állna, mert üres, hanem kompletten a belső változók értékeit) és ezt értelmezi pointerként (printf szintén). Szóval valami tök random helyre írsz a memóriában és onnan is olvasol.
-
jattila48
aktív tag
A http://nyelvek.inf.elte.hu/leirasok/C++0x/index.php?chapter=5 ELTE jegyzetben a move szemantikánál találtam az alábbi, szerintem hibás példát:
std::vector<int> && function() {
std::vector<int> ret; // ret feltöltése jó sok adattal
return ret;
}Azt akarja bemutatni, hogy hogy kell rvalue referenciát visszaadni, a költséges másolás elkerülése érdekében. Csakhogy a ret lokális változóra vonatkozó referenciát ad vissza, ami megszűnik visszatérés után. Ugyanígy lokális változóra vonatkozó lvalue referenciát, vagy pointert sem szabad visszaadni. Ha mindenképpen a move szemantikát akarja használni, akkor az
std::vector<int> function() {
std::vector<int> ret; // ret feltöltése jó sok adattal
return std::move(ret);
}lenne erre megfelelő. Még jobb, ha egyszerűen érték szerint adja vissza.
std::vector<int> function() {
std::vector<int> ret; // ret feltöltése jó sok adattal
ret;
}A modern fordítók RVO optimalizációt hajtanak végre (se copy, se move ctort nem hívnak), vagy ha ez nem lehetséges, akkor automatikusan a move szemantikát használják (a ret-et std::move nélkül is rvalue-nak tekintik, mert visszatérés után megszűnik). A return std::move(ret) elveszi az RVO optimalizáció lehetőségét, és a költségesebb move szemantikát alkalmazza, ezért ne használjuk. Azt azért furcsállom, hogy egy ilyen súlyos hiba benne maradhat egy ELTE jegyzetben. Egyébként általában is nagyon ritka esetben kell rvalue referenciát visszaadni. SOHA ne adj vissza lokális változóra vonatkozó pointert, lvalue vagy rvalue referenciát!!!
Vélemények? -
jattila48
aktív tag
válasz
ToMmY_hun #2739 üzenetére
Ha exceptiont dob a konstruktor, akkor nem jön létre az objektum (és így a destruktora sem fog meghívódni). A new lefoglalja a megfelelő tárhelyet, majd mikor a ctor exceptiont dob, fel is szabadítja azt. A factory függvényedben kell lekezelni az exceptiont, és NULL-t (vagy nullptr-t, vagy "üres" smart pointert) visszaadni, ha a konstruktor exceptiont dobott.
class MyClass{
....
};
MyClass * factory(int arg){
MyClass *p=NULL;
try{
p=new MyClass(arg);
}
catch(...){{
return p;
}Az egyszerűség kedvéért írtam raw pointer visszatérő értéket, igazából a factory fv.-nek inkább std::unique_ptr<MyClass> smart pointert célszerű visszaadni.
Egyébként a paraméter ellenőrzést már a factory fv.-ben is elvégezheted, de akkor csak ezzel célszerű objektumot létrehozni, ctor közvetlen hívásával nem, mert az nem ellenőrzi a paramétereket. Ezt úgy lehet elérni, hogy a ctor-t protected-dé teszed, a factory-t pedig az osztály friend-jévé, vagy statikus tfv.-évé.MyClass * factory(int arg){
if(arg>0){
return new(std::nothrow) MyClass(arg);
}
else{
return NULL;
}
} -
jattila48
aktív tag
válasz
pengécske #2715 üzenetére
Amit te akarsz csinálni, pontosan arra való az std::pair, illetve több visszatérő érték esetén az std::tuple. Ezek ugyan wrapper osztályok, de az STL-ben már készen kapod. Nem kell félni tőle, mert C++ 11-ben az RVO miatt nem kevésbé hatékony mint referenciaként vagy pointerrel átadott output paraméterekben (mint ahogy EQMontoya írta) visszakapni a kívánt értékeket, viszont sokkal átláthatóbb.
"így marad az, h stringbe kodolgatok": na ez az amit ne csinálj! Nagyon nem hatékony, sok hibalehetőséget rejtő, nagyon nem C++ szemléletű amatőr "megoldás".
Ursache: "Nekem még annyi jutott az eszembe, hogy Vector2D": ez tök jó, de mire is? -
jattila48
aktív tag
válasz
pengécske #2713 üzenetére
Nem pontosan értem a kérdésedet. Egy fv. pl. pair-ben adhat vissza két értéket (pl. két int-et), de C++ -ban ez igazából egy db. pair mint visszatérő érték. Ha komplex értéket ad vissza, az sem egy valós és egy képzetes rész float értékként, hanem egy komplex típusú érték, ahol a komplex osztályt te definiálod pl. valós és képzetes rész float típusú adattagokkal:
class komplex{
public:
....
private:
float valos,kepzetes;
};Ahhoz, hogy egy komplex számokat kezelő fv.-t valós számokra is lehessen alkalmazni (ez természetes elvárás), szükség van a valós számot komplex-re konvertáló fv.-re. Ez a komplex osztálynak egyetlen valós paramétert váró konstruktora lesz, amelyet szükség esetén (komplex értéket váró fv.-t valós paraméterrel hívsz) a C++ automatikusan meghív.
Tehát egy fv. egyetlen értéket képes visszaadni, azonban ez lehet alaptípus (int,char,float,...), vagy bármilyen osztály példánya (komplex) (vagy erre mutató pointer illetve hivatkozás). Ugyanolyan nevű fv.-ekből viszont lehet több, ha a szignatúrájuk (paraméter lista elemeinek típusa) különbözik. Ezek C++-ban teljesen különböző fv.-ek, és így különböző értékeket is adhatnak vissza (gondolom ezt tudod), azonban csupán a visszatérési értékük típusa szerint nem lehet így fv.-eket megkülönböztetni (bár a fv. prototípus ekkor is különbözőnek számít).
-
jattila48
aktív tag
válasz
dabadab #2702 üzenetére
A constság szerintem benne van a kérdező által írt szignatúrában, mivel ez a tagfüggvények első implicit this pointer paraméterére vonatkozik. Tehát nem const tfv. esetén ez a paraméter (a this pointer) T * const típusú, míg const tfv. esetén const T * típusú (T az osztály típusa), ami természetesen különböző szignatúrát jelent.
-
AsterixComic
csendes tag
Sziasztok !
Egy kis segítséget kérnék szépen c++ programozáshoz kapcsolódóan.
Teljesen kezdő vagyok c++ programozás terén.Három kérdésben kérném szépen a segítségeteket, hogy mi lehet a pontos válasz rájuk.
1.) Adjon példát parancssori paraméterek elérésére. 1 int paraméter van a parancssorban.
Itt annyit tudok, hogy a main fgv. paraméterei közül az argc tartalmazza a parancssorban levő elemek számát. A char* argv[] -vel pedig hivatkozni tudunk rájuk.
Elég, ha feladatmegoldásként egy for ciklussal valahogy kiíratnánk azt az 1 int paramétert.2.) Mikor van feltétlenül szükség konstruktorra és destruktorra ?
Véleményem szerint destruktorra akkor van mindenképp szükség, ha dinamikusan foglaltunk le valamit a memóriában a new kulcsszóval és azt a destruktorban fel kell szabadítanunk.
Konstruktor esetében pedig egy default konstruktorra mindig szükség van, mert ha például úgy hozunk létre objektumot, hogy nem adunk meg minden paraméterének kezdeti értéket, akkor a default konstruktorban ezt le kell kezelnünk, hogy pl. pointer esetében NULL értéket ad, egész esetén pedig 0(nullát).3.) Mi határozza meg egyértelműen az eljárásokat meghíváskor ?
Véleményem szerint az eljárás neve és a paraméterek száma ÉS típusa határozza meg egyértelműen. Ugyanis tudtommal azonos nevű eljárásból sok lehet, de a hozzá tartozó paraméterlista csak egy lehet ugyanolyan.köszönöm szépen !
-
EQMontoya
veterán
Példakód, nyilván. De alapvetően szerintem az a baj, hogy az egy paraméteres konstruktor nem explicit.
Azért az bárkivel megesik, hogy referencia helyett próbál meg pointert átadni, vagy fordítva. Nyilván értelmes esetben csak az egyik megy, és beszól a fordító, hogy csinálj valamit a szaroddal. Ha mindkettő megy, az baj, szerintem ez nem a hívó fél hibája, itt egyértelműen a konstruktorért jár a körmös. Legalábbis szerintem. -
EQMontoya
veterán
válasz
Krono91 #2652 üzenetére
A friend kicsit gusztustalan, es annyira nem is kell. Listaelemet siman definialhatod a lists belso osztalyakent is, ha mar mindenkepp osztalyba szeretned rakni.
Strazsa szinten nem tul hasznos szerintem, elso elem prr pointers es utolso next pointers null. Persze az elso elem egyben az utolso is lehet, vagy akar 0 elem is lehet. -
Krono91
csendes tag
Reggel amint lesz rá időm nekiesek és átnyálazom az erről szóló dolgokat.
A többieknek csak hogy a hibát kicsit jobban specifikáljam:
Ha a ListaElem konstruktorát kijavítom ez a kódrészlet nekem tökéletesen lefordul, de ha használni szeretném már nem tudom.
Első gyors ránézésre azonnal feltűnik hogy a Lista konstruktorában mikor a strázsákat hozom létre a next és pre pointereket mintha nem definiáltam volna. tehát olyan mintha a a Lista osztály nem látná a ListaElem struktúra belső felépítését.A megoldás az lett hogy létrehoztam egy ListaElem.h headert és abba tettem bele ezt a kódrészletet:
template <class Adat>class ListaElem // privát struktúra így nem kell a fiend ami nehezíti az olvashatóságot, és így tényleg senki nem fér hozzá ehhez az adattaghoz
{
friend class Lista;Adat data; // maga az adat része az objektumnak
ListaElem *next; // a listában következőre mutató pointer
ListaElem *pre; // a listában előzőre mutató pointer//ListaElem konstruktora, ezzel már azonnal beszúrhatóvá is válik 2 listaelem közé
ListaElem(ListaElem *next = 0, ListaElem *prev = 0) :next(next), pre(prev) {}
};A másik headerben a generikusság miatt egy kis átalakítással pedig ez maradt:
#include "ListaElem.h"template<class Adat>
class Lista
{
ListaElem<Adat> *first; // Első eleme a listának strázsa
ListaElem<Adat> *last; // Utolsó eleme a listának strázsapublic:
//Lista konstruktora, strázsák létrehozása
Lista()
{
//Strázsák létrehozása
first = new ListaElem<Adat>;
last = new ListaElem<Adat>;
first->pre = 0;
first->next = last;
last->pre = first;
last->next = 0;
}
};Ekkor már tökéletes a kód és használható is.
A kérdés az hogy az első megoldásom miért nem helyes.
El szeretném kerülni a friend class használatát és egy belső privát struktúrában szeretném tárolni a ListaElemek felépítését.Bocsánat ha ködösen fogalmaztam elsőre, de már tényleg lassan alvás idő van
-
Krono91
csendes tag
Hellosztok.
Szeretném megtudni hogy ez a kódrészlet miért nem működik helyesen:
template<class Adat>
class Lista
{
struct ListaElem // privát struktúra így nem kell a friend ami nehezíti az olvashatóságot, és így tényleg senki nem fér hozzá ehhez az adattaghoz
{
Adat data; // maga az adat része az objektumnak
ListaElem *next; // a listában következőre mutató pointer
ListaElem *pre; // a listában előzőre mutató pointer//ListaElem konstruktora, ezzel már azonnal beszúrhatóvá is válik 2 listaelem közé
ListaElem(ListaElem *next = 0, ListaElem *prev = 0) :n(next), p(prev) {}
};
ListaElem *first; // Első eleme a listának strázsa
ListaElem *last; // Utolsó eleme a listának strázsapublic:
//Lista konstruktora, strázsák létrehozása
Lista()
{
//Strázsák létrehozása
first = new ListaElem;
last = new ListaElem;
first->pre = 0; // nem ismeri a fordító a pre pointert...
first->next = last; // nem ismeri a fordító a next pointert...
last->pre = first;
last->next = 0;
}
}A problémám a Lista konstruktorával van, bele is kommenteltem a megakadásom okát.
Elképzelhető hogy én vagyok nagyon fáradt és valmai triviális dolgot nem veszek észre de akkor sem látom, mi lehet a hiba.
A segítséget előre is köszönöm -
válasz
proof88 #2621 üzenetére
Na átírtam a függvényeket, hogy referenciaként kapják meg a vectorokat. Ez megoldotta a problémát, most már csak kb. másfélszer lassabb a függvényekbe szedett kód a spagettinél. Ezt már be lehet szerintem tudni a függvényhívás idejének. Valójában észrevettem, hogy a vectorok nagy részét már így is referenciaként kapja és nem úgy, ahogy szombaton mutattam. Erre úgy emlékszem azért volt szükség, mert ha értékként adtam át, akkor csak a függvényen belül létrehozott másolatban változtatta meg az értéket, de az eredetit nem módosította. Most átírtam referenciára azokat a vectorokat is, amelyeknek nem kell az értékét változtatni, ezért előzőleg nem írtam át őket referenciára.
Egyébként valóban azért adtam át mindent pointerként a függvényeknek, mert én C-t tanultam és nem C++ -t. Bár mostanában már C++ -t használok, és próbálom kihasználni az előnyeit a C-hez képest, de sajnos én még mindig C-ben írom a programokat C++ -os beütéssel. Próbálom megtanulni minél jobban a C++ -ban való kódolást.
Egyébként kivettem a pointereket és referenciaként adtam át őket a függvénynek, de ez a sebességen már nem változtatott.
Kösz a segítséget -
proof88
addikt
válasz
HussarF #2620 üzenetére
ebben az esetben, ne pointerként add át őket, hanem simán csak értékként (ezt az E1-re és az sz_E-re értem). Nem vesztesz sebességet. Pointereket akkor használj, ha tényleg tömbről van szó, és dinamikusan lefoglalt memóriaterületre mutat a mutatód. Ha esetleg kifelé is változtatni akarsz a beadott paramétereken, akkor inkább referenciaként add át, ne mutatóként. Érdemes ezt használni, ha már C++. C-ben nincs referencia.
-
Rendben, hétfőn át fogom írni akkor. Amikor azt kerestem a neten, hogy hogy lehet vector-t átadni függvénynek akkor ezt a két módszert láttam. Már kicsit rozsdás a C++ tudásom, de próbálom feleleveníteni. Nem programozó vagyok, hanem fizikus, aztán van, hogy hosszabb ideig nem kell programozgatni és ilyenkor feledésbe merül pár dolog.
(#2619) proof88
Az *E1 és az *sz_E nem tömbökre mutató pointerek. Az E1 a foton aktuális energiája, az sz_E meg egyszerűen azt számolja, hogy hányszor lépett kölcsönhatásba (azaz hányszor történt energialeadás). Ugye a program úgy működik, hogy mindig indít egy adott energiájú fotont, az pedig a detektorba belépve kölcsönhatásba lép a detektor anyagával, szóródik, elnyelődik stb. és egy ilyen kölcsönhatás után veszít az energiájábólKöszönöm még egyszer a segítségetek.
-
dabadab
titán
válasz
HussarF #2617 üzenetére
"Meg a vector-okat, de azok is pointerként működnek, mint a tömbök, nem? vagy legalábbis nem hiszem, hogy az okozná a problémát"
Pedig de. Az std:vector<> az ugy mukodik, hogy amikor lemasolod, akkor a komplett adatstrukturat lemasolja.
(A QT containere pl. nem igy mukodnek, azok copy-on-write-ot csinalnak, de ez most csak mellekszal.)vagyis neked a kovetkezo kell pl:
void PhotoEffect(double *E1, int *sz_E, const vector<double> &E_tarolo)
-
válasz
proof88 #2615 üzenetére
Sajnos most hétvégén nem érem el a végleges verziót, de a függvény paraméterek nem változtak. Azok így néznek ki:
void InTheInfiniteTube(double *v1, double *v2, double *v3, int *sz2, double *xbe, double *ybe, double *zbe)
void AroundTheCylinder(double *v1, double *v2, double *v3, double xy_det_tav, double hatarszog, int *sz2, double *xbe, double *ybe, double *zbe)
void FreePath(double *v1, double *v2, double *v3, vector<vector<double>> hkm, vector<double> hkm_E, double *xbe, double *ybe, double *zbe, double *E1)
void ComptonScatter(double *v1, double *v2, double *v3, double *E1, int *sz_E, vector<double> E_tarolo)
void PhotoEffect(double *E1, int *sz_E, vector<double> E_tarolo)
void PairProduction(double *xbe, double *ybe, double *zbe, double *v1, double *v2, double *v3, double *E1, int *sz_E, vector<double> E_tarolo, vector<double> hkm_E, vector<vector<double>> hkm)
Végülis túlnyomórészt pointereket adok át nekik. Meg a vector-okat, de azok is pointerként működnek, mint a tömbök, nem? vagy legalábbis nem hiszem, hogy az okozná a problémát
-
proof88
addikt
-
jattila48
aktív tag
BUÉK mindenkinek!
Egy kis agyalgás: Általában rossz ötlet publikusan származtatott osztályban elrejteni az ős valamely tagfüggvényét, mivel ez esetben a származtatott osztályból létrehozott objektum különbözőképpen viselkedik, ha az ős osztályra mutató pointeren/hivatkozáson keresztül hívjuk meg az adott tfv.-t, mint ha közvetlenül. Ez idáig tiszta sor, de mi a helyzet a statikus tfv.-ekkel? A statikus tfv.-ek ugyebár nem az objektumhoz, hanem az osztályhoz kötődnek (vagyis nem függenek/nincsenek hatással egy adott objektum állapotától/ra), így az előző tanács ilyenkor értelmetlen. Sőt, az ilyen osztályok könnyen lehetnek template argumentumok, amikor pontosan az kell, hogy a származtatással létrehozott osztály adott statikus tfv.-e másképp viselkedjen, mint az ős osztályban (leginkább ezért készül belőle származtatott osztály). Mi a véleményetek?
-
dabadab
titán
"Ha a fuggveny, amit hivsz, virtualis, akkor igazabol barmi megtortenhet, lenyegeben a leszarmazott osztalyok azonos nevu (virtualis) metodusai mind elerhetoek lesznek a hivo (friend) szamara."
Igen, de csak akkor, ha az adott objektumra base class tipusu pointerrel hivatkozik az ember (mert kulonben latja a fordito, hogy a leszarmazott methodjat kellene hivni)
#include <stdio.h>
class Q
{
protected:
friend class P;
virtual int q()
{
return 9;
}
};
class R : public Q
{
protected:
virtual int q()
{
return 3;
}
};
class P
{
public:
int p()
{
R r;
Q* q=&r;
//return r.q(); // NOT OK
return q->q(); // OK
}
};
int main(void)
{
P p;
printf("%d\r\n",p.p());
} -
Jester01
veterán
válasz
kemkriszt98 #2554 üzenetére
Azért azt érzed, hogy ez borzalmas, ugye?
A hiba mindenesetre itt van: string s = "PC" + a+b+c+d;
Itt a + operátor az nem karakter hozzáfűzés, hanem a "PC" konstanshoz képest pointer művelet. Éljen az operator overloading
Egy lehetséges javítás: string s = string("PC") + a + b + c + d; -
WonderCSabo
félisten
válasz
Oppenheimer #2481 üzenetére
Nem tudom, hogy világos-e, de itt nincs semmi extra szintaxis (ahogy javában sincs ebben az esetben). Az std::thread egy sima osztály, thread_name itt a változó neve, és létrehozatalakor a két paraméteres konstruktort hívja meg. Az első paraméter egy függvény pointer, jelen eseten itt egy member function-ra, a második paraméter pedig sima address of operátor, hogy referencia szerint legyen átadva. Ez azért szükséges, hogy a metódus hívásakor adott legyen a this. (Mellesleg a példánymetódusoknál amúgy is valami ilyesmire fordul, mármint mindig a függvény első paramétere az objektum lesz.)
-
cog777
őstag
válasz
bandi0000 #2423 üzenetére
C-ben azert nehezebb programozni mint C++-ban. Biztos hogy C-ben akarod megcsinalni? Ugyanis C-ben nagyon kell figyelni dolgokra, pl ha elszursz egy pointert akkor megjosolhatatlan lehet a program viselkedese, vagy ha nem inicialialsz 0-val egy teruletet ahol string-et tarolsz akkor a printf tovabbi karaktereket is kiirat mert nem talalja a 0-a veget.
for ciklust 1-es index-el kezdted, igy akartad?
fscanf beolvassa ugyan az ertekeket es a string-et is de mi a garancia hogy a string pont akkora a szovegben mint a memoria teruleted:char re[3][7]? Mivel csak 7 karaktert foglaltal le es nem 8at azert hogy 0-t rakj a vegere ezert a printf tovabb irja a szoveget mint 7 karakter.printf utolso parametere nem &re[ I ]hanem csak re[ i ]
"Elvileg ennek tökéletesen kellene mennie"
Bocsanat, nem kotekedesnek szanom, de szerintem a buffer overflow betoreseket is ezek a kielentesek okozzakUgy erdemes programot kesziteni hogy torekszel arra hogy a hiba lehetosegek szamat minimalisra szoritsd.
Hacsak nem kifejezetten C-t akarsz tanulni linux kernel driver iras miatt, akkor inkabb hasznalj C++-t modern technologiakkal:std::array ahol beallitod a meretet a tomb-nek, .at(index) -el elered az elemet (es ha tul index-elsz akkor egybol latod mi a baj), komplett sort olvasnek be a helyedben es std::string-et hasznalnek majd onnan masolnam at az egyes reszeket struct-t ba mivel ezek a sorok egybe tartoznak.
Remelem sikerul atirnod! Sok sikert!
-
chabeee
aktív tag
válasz
h1ght3chzor #2320 üzenetére
hát elvileg ez jó,
tomb2 tartalmát törli és mivel ő egy pointer ráállítom a temp-re ami ugyanúgy egy többre mutat. -
modder
aktív tag
szerintem átrendezted a kódot ahhoz képest, ahogy a hibaüzenetek állapotában volt
Egyik dolog:
adatok uzenetek[uzszam]=elso();
[...]
adatok elso() { ... }az elso() egy darab elemet ad vissza, míg az uzenetek egy tömb.
Nagyobb gond, hogy ha az elso() még tömböt is adna vissza valamilyen csoda folytán, akkor sem inicializálhatnád így az uzenetek változót. Ha inicializálni akarnád, akkor:
adatok uzenetek[uzszam] = {adat1, adat2, adat3, adat_uzszam-1}Miért?
- Mert fordítási időben tudni kell a tömb méretét, ha inicializálni is akarod deklarálásnál!
- Mert nem lehet egész tömböket másolni az értékadás operátorral. Tömb másolása vagy memcopy() fv. vagy for-ciklussal lehetséges. Általában az utóbbit használjuk.Ha belegondolsz, hogy az adatok uzenetek[uzszam] egy sizeof(adatok)*uzszam hosszú memóriaterület, rájössz, hogy az értékadásnak egy ilyen hosszú memóriaterületet kéne átmásolni úgy, mint a memcpy() fv. Ez túl sok hibához vezetne valószínűleg ezért nincs benne.
Másik dolog:
adatok[] elso() { ... return uzenetek2; }sem működik. Nem tudom tanultad-e hogyan működik a függvényhívás és mi a stack. Amikor hívsz egy függvényt, akkor a függvény visszatérési típusának megfelelő méretű memória területet szabadon hagy a stacken fv hívása előtt, visszatéréskor ide másolja a visszatérési értéket. Ehhez tudni kell a visszatérési érték pontos hosszát.
Ezért nem térhetsz vissza tömbbel, mert annak nem tudod az egzakt hosszát futásidőben.Visszatérhetnél pointer típussal:
adatok* elso() {... return &uzenetek2; }Ezzel már csak az a baj, hogy a mód, ahogyan uzenetek2-t deklaráltad, az a stacken jött létre, és megszűnik létezni, miután a függvény visszatért, ezért ha visszatérés után hivatkoznál rá, memóriaszemétre mutatna. (new-val lehetne a heap-en foglalni, de most ez nem kell)
Megoldás:
adatok uzenetek[uzszam];
elso(uzenetek, uzszam);
void elso(adatok[] uzenetek, uzszam) { beolvasol az uzenetekbe }Amint látod a függvényargumentumokat lehet visszatérési értékek tárolására is használni. Tömb esetében ez ilyen egyszerű. Ha nem tömböt használsz, akkor referenciát kell átadni:
void valami(adat& uzenet) { uzenet = createUzenet(); }Ez teljesen elfogadott, hogy lefoglalod a memóriaterületet a tömbnek először, és ezt adod át a függvénynek, hogy feltöltse adatokkal.
-
modder
aktív tag
válasz
Dave-11 #2157 üzenetére
ja, hát igen, ott nem hívsz delete-et, szóval értelemszerűen nem szabadítod fel, tehát ja, memória szivárgás. azért hozzá kell tenni, hogy ahogy meghal a processz, nyilván felszabadul minden lefoglalt memória, de ha egy ilyen kódrészlet valahol be van ágyazva a programban, akkor igen, ez pont az: memóriaszivárgás
Szerk:
Ja várjál, most látom, hogy mit csinálsz. Igen, ez mindenképpen rossz, mert amikor a Pajtit létrehozod, akkor a Dog másoló konstruktora hívódik meg, szóval lesz két változód. Egy a heap-en, amit a makeDog()-ban csináltál, egy pedig a stack-en, a Pajti. És már fel sem tudod szabadítani a heap-en foglalt memóriaterületet, mert elveszetetted, a hozzá tartozó memóriacímet, amikor visszatértél a függvényből. A címet, amit a lenti esetben a Pajti* változód tárol, azaz a pointered. -
cog777
őstag
válasz
WonderCSabo #2135 üzenetére
Találkoztam már vele igen, de pár platform sajnos nem támogatja.. így ezt találtam ki. Még fejlesztenem kell rajta, rendezett listát akarok csinálni és bináris kereséssel hamarabb megtalálni a keresett pointert. Fákat nem hiszem hogy akarok implementálni.
-
WonderCSabo
félisten
válasz
kingabo #2127 üzenetére
A láncoláshoz még kell a dinamikus memóriakezelés, illetve a polimorfizmust is csak így tudjuk megvalósítani C++-ban. De ezek már bonyolultabb dolgok.
Az indoklás annyiban helyes az indoklás, hogy a fv-ben lokális pointeren keresztül létrehozott adat túléli a fv-t.
Mellesleg a stacken eleve nem szoktunk nagy objektumokat tárolni, mert betelik / gáz másolgatni. Ezt is lehet egy indoklás.
-
yksz
aktív tag
válasz
Dave-11 #2121 üzenetére
Ha kulsoleg nem tároltad el valahol a mutatót akkor ezentúl sehogy nem éred el, ezért is memory leak a neve... majd felszabadul ha a teljes programod futása véget ér. Az ilyenek viszont nagyon szépen fel tudják zabálni a memet. milliószor lefuttatod és bamm máris van egy 32millió használatlan bited.
Legtobbszo tobb visszatérési érték átadására használják. fuggvényparaméterként megadsz n darab pointert és azokat állítod a fuggvényben így a fuggvény által létrehozott értékek fuggvényen kívulrol is elérhetoek lesznek.
-
chabeee
aktív tag
lenne még egy feladat.
van egy aru osztályom amiből származtattam elelmiszert és muszakicikket.
van egy raktár osztályom ahol árukat (élelmiszerekre és műszakicikkekre mutató pointereket tárolok)
A raktárban polcok vannak. tudjuk, hogy a raktárban hány darab polc van, és ismert az is, hogy egy polcon hány darab (minden polcon ugyanannyi) áru tárolható. Raktár osztályhoz van egy konstruktort amely paraméterként átveszi a raktárban tárolt polcok számát, illetve azt az értéket, hogy a polcokon egységesen maximum hány árú fér el.
kell egy addÁru függvény ami nálam így néz ki:bool raktar::addAru(aru* pt, int ppolc){
int i = 0;
while(i < max_aru && tomb[ppolc][i]){
i++;
}
if(max_aru > i){
tomb[ppolc][i] = pt;
return 1;
} else{
delete pt;
return 0;
}továbbá a main-ben így hívnám meg:
r1.addAru(new elelmiszer("Liszt", 540,2013,10,23),0); // a 0. polcra kerül
r1.addAru(new elelmiszer("Kenyer", 250,2012,11,17),0); // a 0. polcra kerül
r1.addAru(new muszakicikk("Hangfal", 12000,"Philips"),1); // az 1. polcra kerülmásoló konstruktor megvan, igaz lehet az a rossz, mai nap csináltam először 2 dimenziójú dinamikus tömböt:
raktar::raktar(int v1, int v2) : tomb(0), sz(0), polc(v1), max_aru(v2){}
raktar::raktar(const raktar & a){
tomb = new aru**[polc];
for(int i = 0; i < polc; i++){
tomb[i] = new aru*[max_aru];
}
for(int i = 0; i < polc; i++){
for(int j = 0; j < max_negy_aru; j++){
tomb[i][j] = a.tomb[i][j];
}
}
sz = a.sz;
polc = a.polc;
max_aru = a.max_aru;
}hiba: memória hiba miatt elszáll, valaki ötlet?
-
modder
aktív tag
válasz
mgoogyi #2079 üzenetére
Hali,
Azt hiszem igazad van, a "A b = B();" tényleg az A copy konstruktorát fogja meghívni, tehát ez egy A objektum lesz. Akkor viszont pvt.péter felvetésére mégis csak jogos, hogy a dinamikus kötés csak pointeren keresztül lehetséges C++-ban. De nem a stack vs. heap miatt, hanem a pointer miatt.
Megnéztem az egyik régi házimat, ahol nem lehetett new operátort használni, és így oldottam meg:
Henger henger;
Asztal asztal;
Object* objects_[ MAX_OBJECTS ] = {&henger,&asztal};Ezt csak azért írom, mert a new operátort direkt el akartam kerülni az egész példámban, hogy látszódjon, anélkül is lehet dinamikus kötést alkalmazni.
Köszi a tisztázást!
-
mgoogyi
senior tag
"A b = B();"
Itt lehet van egy kis zavar az erőben, ugyanis ez a sor arra enged következtetni a 'b' változónévvel, hogy ez egy B típusú objektum lesz.
Holott ez a sor a következőt a jelenti:
A b ( B() );
Azaz hozz létre egy A típusú objektumot annak a copy konstruktorával, amely egy B objektumot kap paraméterként.
Ennek két lényeges aspektusa van.
Egyrészt a B() objektumból ledarálódik minden és A típusú objektum marad, merthogy a copy konstruktor érték szerinti A típusú bemenetet vár.
kb így néz ki és defaultból létrejön a copy constructor: A (A input)
Ez a ledarálás az ős irányába meg mindig implicit ( ~= láthatatlanul magától) megtörténik.Másrészt A típusú objektum fog létrejönni.
Másik dolog, hogy ez a kései virtuális kötés csak pointeren és referencián keresztül értelmezhető.
-
-
mgoogyi
senior tag
válasz
pvt.peter #2057 üzenetére
röviden:
polimorf osztályoknál van értelme, amikor specializálod a működését az ősosztálynak és minden leszármazottat kezelhetsz úgy, mint ha az egy ősosztálybeli objektum lenne.
pl. minden almát és körtét kezelhetsz gyümölcsként
pl. Akkor van ennek haszna, mikor van egy rakás valamilyen gyümölcsöd mondjuk egy halmazban és együtt akarod kezelni őket, mert pl. az adott helyzetben számodra nem lényeg, hogy milyen specializált gyümölcsről van szó.Amikor örökölsz, akkor a virtuális függvények mindig befigyelnek!
hosszan:
class Gyumolcs
{
...
virtual void print() {printf("gyumolcs")}
...
};
class Alma() : public Gyumolcs
{
...
virtual void print() {printf("alma")}
...
};
class Korte() : public Gyumolcs
{
...
virtual void print() {printf("korte")}
...
};
Gyumolcs * a = new Alma();
a->print(); //azt írja ki, hogy alma, pedig ez egy Gyumolcs pointer
/*mivel: a virtuális függvényeknél futási időben dől el (dinamikusan), hogy mi hívódik (megnézi, hogy valójában milyen objektumról van szó és annak a függvényét hívja - a háttérben egyébként van az objektumnak egy virtuális táblája és abból nézi ki, hogy mit kell hívni)
ha nem virtuális lenne a függvény, akkor fordítási időben (statikusan dőlne el, mit kell hívni és "gyumolcs" íródna ki)*/ -
modder
aktív tag
válasz
Azazel999 #2000 üzenetére
Az király ha sikerült. Tegnap én sem értettem az algoritmusodat, aztán lejátszottam papíron úgy, hogy a keresett elem (a vágási pont) tetszőleges a fában, aztán rekonstruáltam az új fát, és jó lett.
Viszont nem jöttem rá, hogy beszúrásnál mi a teendő, mert ha ez egy szimpla bináris fa, és keresünk egy nem létező elemet, akkor elérünk az egyik levélbe. akkor melyik lesz a vágási pont? Kipróbáltam több verziót: a vágási pont a létező levél, vagy a vágási pont az új elem, vagy a vágási pont a levél előtti elem, de egyik esetben sem kaptam kiegyensúlyozott fát. Ezt a lépést leírnád?
Amúgy meg rekurzióval tényleg egyszerűbb. az ugye csak egy Depth First Search, ahol minden lépés után vagy B vagy J tömbbe teszed a részfákat, a végén pedig mikor visszatérsz a keresésből építesz egy új fát a B és J elemekből. De általában "hatékonyabb" a nem rekurzív megoldás: erőforráskímélőbb, hiszen nem kell állandóan függvényt hívni.
Amúgy meg erről eszembe jutott, az 1. féléves C nagyházim. Egy logikai kifejezés kiértékelő program tetszőleges logikai kifejezést megadva, épít belőle egy fát (amit ma Abstract Syntax Tree-nek mondanék, mert az jó hangzatos), majd bejárja és közben kiértékeli a kifejezést. Miután működött, három napomba tellett, mire kijavítottam a pointerezést, és a Valgrind végre nem mutatott memória szivárgást
szerk: azt akartam kihozni belőle, hogy jó, hogy meg tudtad oldani egyedül, mert mire kiszeneded magadból a megoldást, sokat megtanulsz
-
kingabo
őstag
válasz
Azazel999 #1988 üzenetére
Ha jól sejtem Te avl fát akarsz implementálni: m. wiki, a. wiki, egyetemi jegyzet ebben, ha a matekos részeket kihagyod, sztem jó le van írva, képekkel. Csak pár pointert kell átállítani a forgatástól függően. Talán próbáld meg papíron, ott elvileg könnyebbnek kell lennie.
-
Azazel999
csendes tag
válasz
Jester01 #1986 üzenetére
Köszönöm, de nem ez volt a baj. Tökéletesen értem a pointereket (szerintem) és az, hogyan működnek. A problémám az, hogy nem tudom hogyan valósítsam meg a vágás/összeragasztás műveletét az önszervező bin.ker. fáknál. Tudom rá a szabályt, meg lerajzolom füzetben az egészet, de egyszerűen nem tudom lekódolni. Már tényleg sokféleképpen próbáltam.
-
Azazel999
csendes tag
válasz
WonderCSabo #1978 üzenetére
Ráadásul a beszúrással gondom akadt, mert elkezdtem írni a vágás metódust és rájöttem, hogy üres (vagyis NULL pointer) fába nem tud beszúrni ez a szerencsétlen.
-
Azazel999
csendes tag
Sziasztok!
C++-ban szeretnék készíteni egy önszervező bináris keresőfát (vagy Java-ban, de azzal elakadtam, mert pointerek nélkül nehézkes a dolog, szóval inkább C++). A vágás (vagyis a legfontosabb metódus
) még nincs meg, de azon kívül minden más igen (keresés, beszúrás, törlés, minimális/maximális elem, előző/ következő elem, teljes fa és egyetlen elem kiíratása). A problémám az, hogy bár a kód "szépen" néz ki és működnie is kéne, összeomlik, amikor keresek egy elemet. Már pedig ez nagyon nagy baj, mert a többi metódus szinte mind elemekre (részfa-gyökerekre) mutató pointereket kér paraméternek. Ebből adódóan egyik sem fut, mert nem tudok nekik pointert adni. Van egy Fa osztályom és egy Main állományom, ezek lennének:
Fa.cpp
#include "Fa.hpp"
#include <iostream>
#include <cstddef>
using namespace std;
Fa::Fa(){
this->kulcs = -1;
this->bal = NULL;
this->jobb = NULL;
this->apa = NULL;
}
Fa::Fa(int kulcs, Fa* bal, Fa* jobb, Fa* apa){
this->kulcs = kulcs;
this->bal = bal;
this->jobb = jobb;
this->apa = apa;
}
Fa::~Fa(){}
Fa::Fa(Fa& other){
this->kulcs = other.kulcs;
this->bal = other.bal;
this->jobb = other.jobb;
this->apa = other.apa;
}
Fa* Fa::keres(int kulcs){
if (this->kulcs == kulcs){
return this;
} else if (this->kulcs < kulcs){
return this->bal->keres(kulcs);
} else {
return this->jobb->keres(kulcs);
}
}
Fa* Fa::min(){
if (this->bal == NULL){
return this;
} else {
return this->bal->min();
}
}
Fa* Fa::max(){
if (this->jobb == NULL){
return this;
} else {
return this->jobb->max();
}
}
Fa* Fa::kovetkezo(){
if (this->jobb != NULL){
return this->jobb->min();
} else {
Fa* q = this->apa;
Fa* p = this;
while (q != NULL && p == q->jobb){
p = q;
q = p->apa;
}
return q;
}
}
Fa* Fa::elozo(){
if (this->bal != NULL){
return this->bal->max();
} else {
Fa* p = this->apa;
Fa* q = this;
while (q != NULL && p == q->bal){
p = q;
q = p->apa;
}
return q;
}
}
void Fa::beszur(/*Fa* fa, */Fa* beszurando){
Fa* y = NULL;
Fa* x = this;
while (x != NULL){
y = x;
if (beszurando->kulcs < y->kulcs){
x = x->bal;
} else {
x = x->jobb;
}
}
beszurando->apa = y;
if (y == NULL){
//fa = beszurando; //Ures volt a fa
} else {
if (beszurando->kulcs < y->kulcs){
y->bal = beszurando;
} else {
y->jobb = beszurando;
}
}
}
void Fa::torol(Fa* fa, Fa* torlendo){
Fa *x, *y;
if (torlendo->bal == NULL || torlendo->jobb == NULL){
y = torlendo;
} else {
y= torlendo->kovetkezo();
}
if (y->bal != NULL){
x = y->bal;
} else {
x = y->jobb;
}
if (x != NULL){
x->apa = y->apa;
}
if (y->apa = NULL){
fa = x;
} else if (y == y->apa->bal) {
y->apa->bal = x;
} else {
y->apa->jobb = x;
}
if (y != torlendo){
torlendo->kulcs = y->kulcs;
}
}
void Fa::kiir(){
cout << "kulcs: " << this->kulcs;
if (this->bal != NULL){
cout << " bal: " << this->bal->kulcs;
} else {
cout << " bal: NULL";
}
if (this->jobb != NULL){
cout << " jobb: " << this->jobb->kulcs;
} else {
cout << " jobb: NULL";
}
if (this->apa != NULL){
cout << " apa: " << this->apa->kulcs << endl;
} else {
cout << " apa: NULL" << endl;
}
if (this->bal != NULL){
this->bal->kiir();
}
if (this->jobb != NULL){
this->jobb->kiir();
}
}
void Fa::kiir_egyet(){
cout << "kulcs: " << this->kulcs;
if (this->bal != NULL){
cout << " bal: " << this->bal->kulcs;
} else {
cout << " bal: NULL";
}
if (this->jobb != NULL){
cout << " jobb: " << this->jobb->kulcs;
} else {
cout << " jobb: NULL";
}
if (this->apa != NULL){
cout << " apa: " << this->apa->kulcs << endl;
} else {
cout << " apa: NULL" << endl;
}
}Main.cpp
#include <iostream>
#include <cstddef>
#include <cstring>
#include "Fa.hpp"
using namespace std;
int main (int argc, char** argv){
//fa letrehozasa
Fa *fa = new Fa(5, NULL, NULL, NULL);
fa->beszur(new Fa(3, NULL, NULL, NULL));
fa->beszur(new Fa(4, NULL, NULL, NULL));
fa->beszur(new Fa(15, NULL, NULL, NULL));
fa->beszur(new Fa(2, NULL, NULL, NULL));
fa->beszur(new Fa(7, NULL, NULL, NULL));
cout << endl;
fa->kiir();
cout << "\n" << endl;
//cout << fa->keres(15)->kiir_egyet();
}Tudnátok adni valami tippet, hogy mi lehet a baj?
-
mgoogyi
senior tag
válasz
jattila48 #1933 üzenetére
Igazad lehet.
Valszeg jobb a konstruktor-ból hibát dobni, mint kétfázisúan inicializálni.
Viszont ha tényleg exception lehetséges a konstruktorban alapesetben, akkor már inkább elmennék a factory irányba, ami lekreálja az objektumot, visszaad rá egy pointert és azt nézheti a hívó fél, hogy NULL vagy sem.
Ebben az esetben a hívó félnek nem kell semmi meglepetésre számítania. -
proci985
MODERÁTOR
pedig a kódrészletek darabonként próbálgatása ahol lehet tényleg sokat javít. sokkal egyszerűbb egy 20soros kódban megérteni pár dolgot, mint kapásból integrálni egy többszáz sorosba.
ha nem vagy biztos a pointerkezelésben és a függvényhívásokban, akkor különösen ajánlott először kipróbálni ezeket egy dummy vectoron, és csak utánna integrálni a véglegesbe.
gyakorlatilag hiába kell kétszer megírni a kódot, az ember időt nyer vele.
még egy tipp: használj értelmes változóneveket ha nem feladatkiírás (ha igen, akkor is megoldható a dolog), a numberOfClasses kissé beszédesebb, mint az M. (ha pedig nem lehet, használj angol változóneveket, későbbiekben a magyar változónevek akkor jók, ha a cég biztos nem akarja eladni a kódot országhatáron túlra)
-
mgoogyi
senior tag
válasz
pityaa23 #1923 üzenetére
Keress neten a házitokhoz hasonló feladatokat és próbáld megoldani.
+ kérdezz itt, ha van időm, válaszolok.Mindenképp értened kéne ezeket, majdnemhogy készségszinten:
for, while ciklusok
if, else if, else
pointerek, pl int *
referenciák pl. int &
new, new[], delete, delete[]
tömbök kezelése
class
{
private:
public:
protected:konstruktor
destruktoradattagok
tagfüggvények
virtual tagfüggvényekstatic adattagok
static függvényeköröklődés
}fontosabb collection-ök: std::set, vector, list
Mondjuk ez így elég sok, nem igazán két hetes téma.
Jó lenne tudni, hogy mi az, amit elvárnak. -
jattila48
aktív tag
válasz
mgoogyi #1917 üzenetére
Biztosan meg lehet oldani másképp is ezt a problémát, pl. ahogy írod. Az én javaslatomnak az az előnye, hogy a konstruktorban nem kell kezelni az exceptiont, sőt az init tfv.-ben sem. Az init tfv. természetesen generálhat exceptiont, de azt lehet kezelni felsőbb szinten, és mikor az objektum kilép a scope-ból a destruktora (illetve a tagobjektumok destruktorai) automatikusan mindig elvegzik a deallokációt. Tehát bármilyen "mélyről" jön egy pointer tag inicializálásával keletkező exception, a végén minden sikeresen allokált pointer tag automatikusan felszabadul.
Nem mondtam, hogy ez lényeges kérdés, viszont szerintem érdekes. Ha nem akarsz, ne válaszolj rá! A kétfázisú inicializálást pedig sok tanulmány ellenzi. Keress rá! -
jattila48
aktív tag
válasz
mgoogyi #1912 üzenetére
Így van, az auto_ptr nem jó tömbre.
Általában nem tanácsolják a kétfázisú inicializálást. Egy olyan helyzetet próbáltam mutatni, ahol mégiscsak célszerű lehet.
Ha a try cathc-et berakom a ctor-ba, ekkor a try-ban auto_ptr-t kéne használni, ami nem jó tömbre.
Az exception-nel az a problémám, hogy ha ctor-ban keletkezik, akkor nem fut le a destruktor (mivel ekkor nem lett teljesen megkonstruálva az objektum), és azok a felszabadítások amiket a destrukdornak kéne elvégezni, nem végződnek el. A kétfázisú inicializálás azon segít, hogy a destruktor mindenképp lefut (mivel a ctor nem dobott exceptiont, hiszen a kritikus inicializálást az init tfv. végzi), és elvégzi a nem NULL pointerek felszabadítását. A ctor-nak természetesen NULL-lal kell inicializálni a szóbanforgó pointereket.
Egyébként éppen tömb inicializálása kapcsán jött ez elő. Remélem így érthetőbb a dolog, persze felhajtást nem szeretnék csinálni, csak megbeszélni, kinek mi a véleménye. -
mgoogyi
senior tag
válasz
jattila48 #1911 üzenetére
- auto_ptr-rel nem tudsz tömböt felszabadítani (ha tudsz is róla, nem biztos, hogy mindenkinek egyértelmű, főleg mert char tömbökkel dolgozol)
- miért lenne baj a kétfázisú inicializálás? azt kell használni, ami célszerű az adott helyzetben.
- az exception dobás elkerülhető a new esetén az std::nothrow paraméterrel: new(std::nothrow), ekkor NULL pointer-t ad vissza a new, ha nem volt elég memória.
- a destruktorodnak muszáj az objektumod által lefoglalt dolgokat felszabadítani, ez nem kérdés. Nem értem ezt a nagy felhajtást.
- a try catchet ennyi erővel berakhatod a ctor-ba is, ha csak az exception a nagy problémád -
jattila48
aktív tag
Kétfázisú inicializálás az, amikor az objektumot a konstruktora nem inicializálja teljesen, hanem még egy inicializáló tfv.-t kell meghívni ahhoz, hogy az objektum használható legyen. Akkor szokták használni, ha az inicializálás során virtuális tfv.-t kell meghívni, vagy a konstruktor nem dobhat exception-t. A kétfázisú inicializálást általában kerülendőnek tekintik. Azonban szerintem kifejezetten hasznos is lehet, és nagyban egyszerűsítheti a megfelelő resource kezelést pl. memory leak elkerülésére használható. Ha pl. van egy objektumunk, aminek inicializálása során két memória foglalást végez, előfordulhat hogy az első allokáció sikeres, a második pedig nem. Konstruktorral megoldva ilyenkor auto_ptr-t használhatunk, mert az első pointert fel kell szabadítani, mielőtt a második allokáció sikertelensége miatt a konstruktor exception-t dob. Ilyenkor az objektum destruktora nem fut le, mert csak teljesen megkonstruált objektumra fut le a destruktor, ha az kilép a scope-ból. Na már most kétfázisú inicializálással az objektumot a konstruktora úgy hozza létre, hogy a szóban forgó két pointert NULL-lal inicializálja, majd az inicializáló tfv. elvégzi a szükséges allokációkat. Ha valamelyik allokáció nem sikerül, akkor az init tfv. exception-t dob, vagy false-val tér vissza, de a sikertelen allokáció eredményeként a pointer NULL marad. Az objektum destruktora csak a nem NULL pointert szabadítja fel, a NULL-t békén hagyja.
class A {
public:
A():c(NULL),d(NULL){}
~A();
bool init(int,int);
private:
char * c;
char *d;
}A::~A()
{
if(c!=NULL)delete[] c;
if(d!=NULL)delete[] d;
}bool A::init(int n,int m)
{
c=new char[n];
d=new char;
return true;
}void main(void)
{
try{
A a; //Ez mindenkepp lefut, nem dob exception-t
a.init(10,0x7fffffff); //A::d-nek tul nagy memoriat akarunk foglalni, ezert exception-t dob
}
catch(std::bad_alloc &){
std::cout << "Memory allpc error";
}
//Itt a lefoglalt A::c felszabadult, nincs memory leak
}A lényeg az, hogy a try blokk végén az A destruktora lefut, mert az A a objektum konstruktora mindenképp lefutott. Kicsit olyan ez, mintha az A osztály maga is egy "smart pointer" lenne.
Mit szóltok hozzá? Ugye nem is feltétlenül rossz a kétfázisú inicializálás? -
mgoogyi
senior tag
válasz
jattila48 #1867 üzenetére
Szia,
Ennek mi lenne a gyakorlati haszna számodra?
Csak a polimorfizmust akarod kihasználni, mert minden ilyen objektumra ugyanazt a virtuális függvényt hívod majd meg az f(Base **)-on belül?
Ezesetben azt tudod mondani, amit előttem is írtak, hogy Base ** tömb-ben közlekedtesd az összes leszármozott objektum-ra mutató pointert.Kooperálnak is ezek az objektumok f()-en belül, vagy csak minden egyes tömbelemre meg kell hívni egy függvényt?
Googyi
b, lehetőség egy makró, aminek a paramétere a tömböd, amit meg csinálni akarsz a makrón belül, azt úgyis csak a Base-ben definiált függvényekkel tudod.
-
Vico87
tag
válasz
jattila48 #1878 üzenetére
Az egyik megoldás, ha Base** példányt vár a függvényed és létrehozáskor is Base**-ba pakolod a Derived példányaidat. Ez akkor jó, ha ki akarod használni a polimorfizmust és a kollekciód heterogén, azaz statikus típusa Base* tömb, de az egyes elemek dinamikus típusa elemenként eltérő.
Ha biztosan csak Derived példányokat akarsz átadni és ez nem fog változni, akkor minek Base* tömb? A megoldás, hogy a függvényed Derived* tömböt kapjon.
Megjegyzés: a fordító számára nincs különbség Base** és Base*[] között. Az, hogy számodra szemantikailag a két dolog eltérő, az jogos, de a szándékodat (hogy ez most egy Base* tömb, vagy pedig egy Base példányra mutató mutatónak mutatója) a tömb technikai megvalósítása (memóriaterületre pointer) miatt nem tudja megvalósítani a fordító. Csak akkor tudná kitalálni a pontos konverziót, ha pontosan tudná, hogy a Base* tömbödben mennyi példány van, de ezt nem tudhatja, mivel ez futási időben derül csak ki és függvényhívásonként nem konstans.
Ez a konverzió olyan esetben veszélyes, amilyet írtál: egyik leszármazottból konvertálás másik ágon lévőbe, például a tömbödben nem csak Derived*-ok vannak, hanem Derived2*-ok is.
-
jattila48
aktív tag
válasz
jattila48 #1873 üzenetére
Ha a Derived **-ról Base **-ra castolást megengedné a fordító, akkor meg lehetne csinálni a következőt:
Derived2 szintén származzon Base-ből.Derived *d=new Derived;
Base **b;
b=&d; //ezt a Derived **-ról Base **-ra castolást nem engedi a fordító, de reinterpret_cast-tal ki lehet erőszakolni
*b=new Derived2; //ez simán megy, mert Derived2 *-ról Base *-ra castolEz után a d pointer egy Derived2 típusú objektumra fog mutatni, holott Derived *-ként van deklarálva. Ez nyilván baj lenne, ezért nem engedi meg ezt a fajta cast-ot a fordító. Azonban más a helyzet pointer tömbökre. Ha b nem Base ** típusú, hanem Base *[], akkor a b=&d értékadást eleve nem lehetne megcsinálni, mivel b nem megváltoztatható (hiszen konstans tömb cím, amit a fordító konstans értékként kezel). Tehát a Derived *[]-ról Base *[]-ra való castolás biztonságos, és esetenként kívánatos is lenne, de sajnos a fordítók nem tesznek különbséget ezek között.
-
jattila48
aktív tag
Azt is lehetne, de így egyszerűbb volt. Igazából a lényegen nem változtat. Ilyenről még nem hallottam, hogy a pointerek mérete különböző lenne (ettől persze lehet), de a Derived **-ról Base **-ra castolás nem is azért veszélyes. Viszont a pointer tömbök közötti castolást nem látom veszélyesnek.
-
Karma
félisten
válasz
jattila48 #1871 üzenetére
Nem úgy értem a cserét, hanem hogy sehol se használsz Derived tömböt, csak Base tömböt és virtuális metódusokat. Élő helyzetben ennek még értelme is lehet
Veszélyességet csak nagyon madártávlatból gondoltam: StackOverflown olvastam, hogy van olyan architektúra, ahol a pointerek mérete nem állandó. Arra nem emlékszem melyik, valószínűleg nem is releváns.
Agyilag ma elég zokninak érzem magam.
-
jattila48
aktív tag
De, le lehet cserélni a a Derived *[] tömböt Base *[] tömbre, ez történt az átmásolással. A kérdés azonban elvileg is érdekes. Szerintem a Derived *[] tömb Base *[]-ra castolása teljesen legális lehetne, (de a Derived ** Base **-ra castolása már nem), a probléma szerintem az, hogy függvény paraméterként a fordító nem tesz különbséget a pointer és tömb között. Természetesen általában a tömbök közti castolás nem legális, de ebben a példában semmilyen veszélyét nem látom (vagy esetleg tudnál veszélyes példát mutatni?). Egész más a helyzet a Derived **-ról Base **-ra castolással.
-
jattila48
aktív tag
válasz
jattila48 #1868 üzenetére
Ugyanakkor, ha az f függvény Base[] típusú paramétert vár f(Base *), akkor simán meg lehet hívni Derived[] típusú tömbbel, mivel a Derived[] típust Derived *-nak tekinti, a Base[] típust Base *-nak, és az ősosztály pointerére gond nélkül lehet konvertálni. Ez azonban egészen biztosan hibás működést eredményez. Ebből is látszik, hogy a tömb az nem pointer (minden ellenkező hiedlemmel szemben), még ha hasonlóan kezelhető is.
-
jattila48
aktív tag
Sziasztok!
A következő problémára várok megoldási javaslatot:
Van egy függvényem, ami paraméterként Base * pointereket tartalmazó tömböt vár, de szeretném Derived * pointer tömbbel is meghívni. Valahogy így:class Derived : public Base{...}
void f(Base **){...}
Derived *d[10];
....
f(d);A fordító természetesen hibát jelez, hogy nem tud Derived **-ról Base **-ra konvertálni. Ez világos hogy miért veszélyes általában, de nekem nem igazán Base ** típusú az f paramétere, hanem Base *[] tömb, amire teljesen természetes és veszélytelen lenne Derived *[] típusú tömböt konvertálni. reinterpret_cast-tal megoldható, de az elég rondán néz ki. Az a gond, hogy paraméterként átadva a Base *[] típust Base **-nak tekinti, holott nem egészen az. Esetleg valami elegánsabb megoldást tudnátok javasolni? Lehet, hogy a Base *[] tömböt be kéne ágyazni egy Barray osztályba, a Derived *[] tömböt pedig egy ebből származó Darray osztályba, az f-nek Barray * típusú paramétert adni. Ez sem tetszik, akkor már inkább a reinterpret_cast.
-
Karma
félisten
válasz
Dave-11 #1824 üzenetére
Hát bármi más, amit visszatérési típusnak megadsz. Amíg helloworldözöl valószínűleg int, double, vagy egyéb primitív típusok főleg - ezt írod a void helyére és kész.
Egyébként a void *-nak különleges jelentése van, ez az általános pointer amihez nem tartozik típus. Manuális memóriakezelésnél (C-ben malloc/free) kerül elő főleg, de ideális viszonyok között C++-ban nem kellene ezzel találkoznod (magasabb szintű absztrakcióknak hála).
-
Davs
tag
válasz
Dave-11 #1816 üzenetére
Ha ertesz angolul, akkor ajanlanam a Stanford egyetem programozas-eloadas triojat. Nem csak c/c++ van benne, hanem altalanos programozas, memoriarol es assemblerrol is van kicsit szo, hogy tudd mi folyik a hatterben..Youtube-on keress ra a programming methodology, programming abstractions es programming paradigms-re(ebben a sorrendben), mindharom playlist kb 25-28 ~50perces eloadast tartalmaz (nagyon jok, nekem nagyon tetszenek). A methodology a legkonnyebb, Java nyelven "tanitanak", de nem is igazan a nyelvet, hanem amolyan bevetezest a programozasba (kezdoknek valo). Az abstractions mar c++ orientaltabb, mindenfele sorting-algorithm-eket, adatstrukturakat taglal (array, vector,map, hashmap, binary search tree, queue, stack stb - ezekkel nagyon jo tisztaban lenni, hogy tudd melyik hol eros es miben verzik el). Aztan a 3. a programming abstractions (most nezem, kb 10. eloadasnal tartok). C-vel kezdi, nagyon ramegy a pointerekre, hogy megtalnuld kezelni a memoriat, aztan most kezdi kicsit az assembly-t, hogy kb tudd mi tortenik a hatterben pl egy i++ parancsnal, vagy egy funkciohivasnal, aztan lesz szo alap concurrecy-rol is ("parhuzamositas", szoval multithreaded alapok)..a harmadik elegge low-level szintu (hardver-hez kozeli). Amugy lattam meg MIT-nek is eloadasaint iTunes-on, de meg nem lestem nagyon belejuk..Amugy en is hobbiprogramozonak vallom magam, viszont fosuli utan ebbol szeretnek megelni.
-
Davs
tag
válasz
Dave-11 #1813 üzenetére
Ha van egy tombod, pl int x[10], akkor az x valojaban egy pointer a tomb elso elemere..
x[0] az ugyanaz, mint a *(x+0), x[1] ugyanaz, mint az *(x+1), szoval a char *y lenyegeben karaktertombot jelez. Amugy pointereket akkor is szokjak meg hasznalni, ha egy funkcioban egy masik funkcio (pl akar a main() ) valtozoit akarod modositani, vagy ha dinamikusan hozol letre valtozokat (a konyv remelem jobban elmagyarazza majd, mint en)
-
kispx
addikt
válasz
Dave-11 #1811 üzenetére
Pontosan annyiszor használom őket ahányszor kellenek (se többször, se kevesebbszer
)
Feleslegesen nem fogom tele pointerezni a forráskódot, azért mert csak azzal is meglehet vele oldani a feladatot. Végig kell gondolni a programot, hogy éri meg lekódolni és aszerint fejleszteni.Legtöbbször használom: (hirtelen ennyi jutott eszembe)
- ha külső függvénykönyvtárok megkövetelik
- char*
- ha olyan paramétert adok át aminek az értékét meg kell változtatni (bár ha már C++ a nyelv, akkor inkább referencia) -
Dave-11
tag
Úúúúh na végre értem a pointereket!!
Ez a Tanuljuk meg a .... 24 óra alatt sorozat nagyon jó, aki valami nyelven tanulni szeretne annak csak ajánlani tudomMikor megláttam hogy van C++ -szal foglalkozó változata tudtam hogy ez az ami kell nekem, így hát elkezdtem bújni, és most hogy a pointereknél járok és szépen haladok bégig rajta és magyaráz és hozza a példákat egyre jobban és jobban értem őket, nagyon faja
Ha már ennél a témánál vagyok megkérdezem az itt fórumozgató nagy programfejlesztőket, hogy ti a projektjeitek során milyen gyakran használjátok őket, és mikre például?
-
Dave-11
tag
Áááh srácok segítsetek mert megőrülök
Újra belekezdtem a C++ tanulásába, de még nem haladtam nagyon előre, csak az alapoknál járok, szinte még csak annyit tudok, hogy kiíratok/bekérek szöveget, meg a tömbökről tudok egy keveset.
Legutóbb mikor tanulgattam fekete lyukat hagyott bennem a pointer szó, gondoltam most újra esélyt adok neki, de egyszerűen nem tudom megérteni. Nem csak magát a működését, de azt sem, hogy később majd mit lehet vele kezdeni.Ráadásul a könyv amit olvasok nagyon sok példába beleteszi, amitől meg végképp elvesztem a fonalat.
Valahogy tudnátok segíteni vagy ajánlani valamit? Le kéne tisztáznom az egész dolgot újra, a legelejétől mert összezavarodtam. Könyvet is küldhettek. -
Davs
tag
Hali!
Lenne egy Qt-s memory managmentes kerdesem memory leakkel kapcsolatban. Van egy Dialogom, a constructorjaban ez all:QStandardItemModel *model = new QStandardItemModel(0,numCols,this) ;
Ugye mivel most megadom parentnek (3. parameter) a this-t, ezert ha bezarodik a program, vagy a Dialog bezarul, akkor az automatikusan torli a child elemeit, koztuk a model-t is.
Ha ezt a model-t beallitom pl egy QTableView modeljenek, akkor minden egyes "cellaban" egy pointer lesz egy QStandardItem-re. Ezek az elemek ugyebar a QStandardItemModel childjei lesznek, tehat ha a Dialogom bezarult, torli a childjeit-> modelt, ami szinten torli az o childjeit, azaz a cellak egyes elemeit.
Mi tortenik azonban, ha meghivok egy
model->setItem(row,1,new QStandardItem(QString("Hello")));
Ilyenkor, ha a setItem elott nem hivom meg a delete-t arra a bizonyos cellara, akkor az torlodik magatol, vagy memory leak keletkezik? (termeszetesen a "magatol torlodest" ugy ertem, hogy a Qt automatikusan torli-e helyettem) -
-
proci985
MODERÁTOR
oké, de igazából ha szépen megtervezed, akkor GUIt később tudod csatolni.
annyira nem veszélyes a másik nyelvezete sem.
igazából amiatt nem ajánlott kezdőknek, mert kegyetlenül csapongó: néhol nagyon szájbarágós, máshol pedig kell némi háttér HW architektúrában, hogy az ember megértse, miről van szó. utóbbi Neked valszeg ismerős, de mondjuk egy gimnazistának sok lenne, ahogy elmondja, hogy a pointer/referencia miért úgy lett megoldva, ahogy.
fura stílus tükörfordítás miatt is lehet.
olvasás egyébként jó, ha tapasztalt vagy, akkor lehet így fog menni a leggyorsabban.
-
WonderCSabo
félisten
válasz
kmisi99 #1614 üzenetére
Elég gány ez a kód. Egyik fv-t az osztályon belül deklarálja, a másikat nem, semmi logika alapján, egyszer a felhasználói bevitelt az osztály metódusán belül kezeli, másszor azon kívül. A char alphabet változó teljesen feleslegesen van deklarálva, a getmeaning-nek totál hülyeség pointert átadni, ha memóriát akarsz spórolni akkor konstans referenciát inkább. Az std:: névteret minek írja ki, ha már gány módon behúzta globálba? Ez a program baromi lassú lesz, minden egyes szótárműveletnél beolvassa az egész fájlt, ahelyett, hogy feltöltené vmi kis "adatbázisba" a memóriában. Sőt, még azzal se foglalkozik, ha már megtalálta a jelentést, szépen végigolvassa a fájlt tovább...
Csak az a baj eléggé szar vagyok programozásból így inkább a fejem használtam. A google ki is adta nekem ezt a forráskódot mit gondoltok ez működhet?
Arra használtad a fejed, hogy kikopizd a kódot vhonnan?
A progi egyébként természetesen működik, ha jól látom, de én ezt több ok miatt nem adnám be:
- gány
- asszem a közéleti események után már mindenki érzi, hogy mennyire nagy gond az, ha vki MÁSOLT
- és nem tanulsz vele semmit -
WonderCSabo
félisten
válasz
Dave-11 #1577 üzenetére
Egyébként van valamilyen szisztéma, ami alapján a címekbe rakja az értékeket?
Sajnos ez egy nagyon bonyolult kérdés. Az alap koncepció az, new operátor hívásakor keres egy akkora összefüggő, üres területet a heap-en, amitbe belefér a változó, és ennek a területnek a címe kerül bele a pointeredbe. Hogy milyen szisztéma alapján keresi a területet, az egyrészt rendszerenként is változó, másrészt az adott szituációtól is függ. Ennél sokkal többet nem tudok erről a dologról, mert nem igazán volt szükséges utánajárnom eddig. De ha további kérdéseid vannak, talán a hozzáértőbbek tudnak válaszolni rá.
-
WonderCSabo
félisten
válasz
Dave-11 #1575 üzenetére
Nem, nem erre való. A memóriában valóban egy címen van a változó értéke, de ehhez szükségtelen pointereket használni. A címet egyébként is le lehet kérni az & operátorral.
A pointerek azért jók, mert segítségével láncolt adatszerkezeteket tudunk létrehozni. Pl. nem kell a tömbben tárolni a bazinagy képeket, elég csak a rá mutató pointert, és emiatt a tömb sokkal jobban kezelhető, vagy létrehozhatsz láncolt listát, fákat, stb.
A másik dolog, amit Te is írtál, hogy a pointerek segítségével tudunk dinamikusan lefoglalni memóriaterületet, és ezt bármikor fel is szabadíthatjuk. Ettől a program nem fog feltétlenül gyorsabban futni, viszont a memóriahasználata nyilvánvalóan csökkenni fog. Ellenkező esetben a sima automatikusan lefoglalt változók esetén a változó csak az adott blokk lefutása végén szabadul fel.Ezen kívül C++ - ban még a pointereken keresztül lehet megvalósítani a polimorfizmust és a dinamikus kötést, és még ezer dolog van, hogy miért is jók a pointerek.
-
Dave-11
tag
Kicsit jobban utánnajártam a pointereknek, megnéztem két videót ( videó1 és videó2 ), és hát kicsit segített letisztázni pár dolgot, de még mindig van pár dolog, amiben nem vagyok biztos. Tehát:
1. ha létrehozunk egy változót, és annak adunk egy értéket, akkor az az érték eltárolódik a memóriánk egyik címében. És valahányszor használja a programunk a változót, az értékét az adott memóriacímről nyeri ki? És erre jó a pointer, hogy megkapjuk az adott memóriacímet?
2. ha egy változó feleslegessé válik, vagy ha változik az értéke, akkor a korábbi memóriacímről törölni tudjuk a pointerünk segítségével, és egyszerűen új értéket adunk neki, ami már egy más címen lesz? És ezzel kerüljük el, hogy felesleges dolgok megmaradjanak, és a program kevesebb memória használatával, valószínűleg gyorsabban fusson? -
Chipi333
csendes tag
válasz
h1ght3chzor #1570 üzenetére
Szerintem a pointer és a tömb fogalmát nem érdemes összemosni, mert két teljesen különböző dologról van szó. Az, hogy egy tömböt általában úgy szokás használni, hogy egy pointerrel mutatsz az elejére nem jelenti azt, hogy minden pointer mögött tömb lesz.
Új hozzászólás Aktív témák
Hirdetés
● ha kódot szúrsz be, használd a PROGRAMKÓD formázási funkciót!
- Szép! HP EliteBook 850 G8 Fémházas Multimédiás Laptop 15,6" -65% i7-1185G7 32/512 Iris Xe FHD Magyar
- HP EliteBook 850 G8 Fémházas Multimédiás Laptop 15,6" -65% i7-1185G7 8/512 Iris Xe FHD Magyar
- 512 Gb-os NVME-k
- Eladó autós gyerekülések, Römer és Peg-Pérego márkák
- ASUS TUF Gaming A15 FA506 - 15,6"FHD IPS 144Hz - Ryzen 5 7535HS - 16GB - 512GB - RTX 3050 -3 év gari
- ÁRGARANCIA! Épített KomPhone Ryzen 5 7600X 32/64GB RAM RTX 5070 12GB GAMER PC termékbeszámítással
- Dell D6000 univerzális dokkoló USB-C/ USB-A, DisplayLink & Dell WD15 (K17A) USB-C + 130-180W töltő
- Lenovo V130-15IGM laptop (Pentium Silver N5000/8GB/256GB SSD
- AKCIÓ! Apple MacBook PRO 15" 2018 i9 32GB 500GB 560X 4GB notebook garanciával hibátlan működéssel
- Konica Bizhub C220 - A3 fénymásoló
Állásajánlatok
Cég: Promenade Publishing House Kft.
Város: Budapest
Cég: PC Trade Systems Kft.
Város: Szeged