- Google Pixel 9 Pro XL - hét szűk esztendő
- Realme GT Master Edition - mestermunka
- Ilyen lesz a Fairphone 6
- Keretmentesít a Galaxy S25 FE
- Nem lett arányos a fogyókúra
- iPhone topik
- One mobilszolgáltatások
- Mobil flották
- Samsung Galaxy A55 - új év, régi stratégia
- Karaktere biztos lesz az első Nothing fejhallgatónak
Új hozzászólás Aktív témák
-
jattila48
aktív tag
Meg tudná valaki mondani, hogy egy C++ kódban -function prototype kivételével- mi értelme van az extern "C" deklarációnak?
-
jattila48
aktív tag
válasz
Tomi_78 #4394 üzenetére
"WM_CREATE-ből átraktam a program tetejére a változók létrehozását, illetve a képernyőméret lekérdezést az ablak létrehozása elé."
Nem ezen múlik a dolog. Az ablakkezelő WM_CREATE ágát a CreateWindow fc. hívja meg. A legtöbb inicializálás (ablakhoz kapcsolódó, főablak esetén ptogramhoz kapcsolódó is) itt szokott megtörténni. Az itt létrehozott objektumokat (handle-kat, stb.) a WM_DESTROY ágban (amit a WM_CLOSE ágban kiadott DestroyWindow fv. fog meghívni) kell lebontani, lezárni.
Az, hogy az ablaknak van-e címsora, vagy méretezhető-e, a CreateWindow (vagy CreateWindowEx) fv. dwStyle (és dwExStyle) flag paramétereivel kell megadni, vagy esetleg az ablakosztály megfelelő stílusát (WNDCLASSEX típusú struktúra style tagja) kell beállítani, mielőtt a RegisterClassEx fv.-t meghívod. -
jattila48
aktív tag
válasz
Tomi_78 #4392 üzenetére
A GetWindowRect bool típussal tér vissza. Mit akarsz abból kiszedni? Azt csak ellenőrizni kell, hogy sikerült-e a fv. hívás. A RECT típusú 'kijelzo' változóban (struktúrában) vannak a keresett értékek:
ablakszel=kijelzo.right-kijelzo.left;
ablakmag=kijelzo.bottom-kijelzo.top;Bizonyos esetekben a left és top tagok negatívak is lehetnek (a drop shadow miatt?), de itt ez nem fordul elő.
-
jattila48
aktív tag
válasz
DrojDtroll #4391 üzenetére
Amit a GetDesktopWindow visszaad, az csak egy window handle, nem struktúra pointer. Próbálkozz a
HWND kijelzo=GetDesktopWindow();
RECT rect;
GetWindowRect(kijelzo,&rect);kóddal.
-
jattila48
aktív tag
válasz
dabadab #4318 üzenetére
Épp azt írtam, hogy ne kész megoldásokra hivatkozzatok (OK, nem boost hanem már std), hanem hogy hogy lehetne megírni ezek nélkül. A működése érdekel. A generátor azért más mint az iterátor, bár hasonló célt szolgál. Arra kíváncsi lennék azért, hogy az iterátor alkalmazásához előbb eltárolja-e a fájl neveket konténerbe utána pedig azon iterál, vagy "röptében" állítja elő a fájl neveket. Gyanítom, hogy az előbbi, akkor pedig nem túl memória kímélő. Míg a generátor csak az éppen következő fájl nevet állítja elő.
Közben olvasom, hogy mi a LegacyInputIterator, így a fenti megjegyzésemet visszavonom. Ehhez nem kell előzőleg konténerben eltárolni a fájl neveket. Na ezt sem tudtam eddig, hogy létezik ilyen, hogy LegacyInputIterator. -
jattila48
aktív tag
Sziasztok!
C/C++-ban szeretnék írni egy generátort, ami adott directory-tól kezdve rekurzívan visszaadja a fájl neveket. Lenne egy init tfv.-e, aminek meg kell adni a gyökér directory nevét, a get_next_file tfv. pedig visszaadja a következő fájl nevet, a finish tfv. pedig kilép a generátorból. Hasonlóan ahhoz, mintha iterátort használnánk, de a fájl nevek nem kerülnek előzetesen konténerbe, hanem mindig a get_next_file adja vissza a következő fájl nevet, de csak azt. Pythonban van generátor, de C/C++-ban nincs (egyébként egyáltalán nem értek a Pythonhoz). Hogy lehetne hasonlót írni? Ne boost-ot ajánljatok, mert igazából a mechanizmus érdekel. Bár ha tudjátok, hogy a boost file system iterátora hogy működik, az is érdekel. Én megírtam egy működőképes megoldást, amit elegánsnak nem éppen lehet nevezni.
-
jattila48
aktív tag
válasz
Teasüti #4153 üzenetére
"Tehát ami nekem átjött ebből a beszélgetésből, hogy ha new operátorral hozok létre tömböt (foglalok le dinamikus memóriát), akkor az egy ún. heap-re kerül,..."
Ez igaz.
"...míg ha new nélkül, akkor a stack-be."
Ez nem feltétlenül igaz, mert a globális adatterületen is deklarálhatsz tömböt.
Egyéb gyakorlati jelentősége pedig az, amit eddig leírtam. Röviden összefoglalva:
- A tömb név nem változó, nem adhatsz neki értéket, és nem képezheted a címét.
-A pointer változó, ami memória címet tartalmaz (pl. a heapen lefoglalt memória címét, de lehet más pl. "hagyományosan" deklarált tömb címe is). A pointer értéke megváltoztatható, és a címe is képezhető.
-Tömböt csak konstans méretűt deklarálhatsz (kivéve C99), míg new-val változó méretű memóriát foglalhatsz.
-Tömböt nem szabad felszabadítani, new-val létrehozott memóriát pedig fel kell szabadítani.
Kb. ennyi ami gyakorlatban különbség a két fogalom között, és amiről minden programozónak tudnia kell.
Még egy "apróság": C++ -ban tömb név, mint konstans pointer lehet template argumentum (mivel fordítási időben kiértékelhető), pointer viszont nem (mivel csak futási időben értékelhető ki). -
jattila48
aktív tag
válasz
jattila48 #4149 üzenetére
Elnézést, a const int valóban olyan "változó", aminek nem lehet értéket adni. A static const int-re gondoltam, erre már áll az amit eddig írtam. Template argumentumként és tömb méretként is static const int használható, const int nem. Amiket a tömb vs. pointer témában írtam, azt fenntartom.
-
jattila48
aktív tag
válasz
mgoogyi #4148 üzenetére
Ahogy írtam, a const int kezelése esetleg lehet fordító és helyzet függő, pl. a példa programodban képezhető a címe (bár sok értelme nincs, mivel értéket nem adhatsz neki). De amiket példákat írtam (template argumentum, tömb méret), valószínűleg az értékét mint konstanst fogja használni, hiszen fordításkor még semmilyen memória címet nem tulajdoníthat neki. Értékadásnál esetleg lehet más a helyzet, hiszen az értékadás futás időben történik, és ott hivatkozhat a konstansra a memória címén keresztül (sok értelme ennek sem lenne, esetleg gyorsabb lehet az újra fordítás, ha megváltoztatod a forráskódban a konstans értékét. Ez csak tipp).
A #define egy más kérdés, az pusztán szövegfeldolgozás. Ott tényleg semmi különbség nem lehet ahhoz képest, mintha a konstans literált írtad volna minden helyen.
Kicsit elkanyarodtunk a témától. A const int-et és enum-ot csak a tömb mint konstans cím hasonlósága miatt hoztam fel. Eredetileg arra válaszoltam, hogy a tömb nem pointer, még ha sokszor hasonlóan is kezeljük. A különbség gyakorlatban is fontos vonatkozására is felhívtam a figyelmet, amit minden programozónak tudni kell. A technikai különbségek (mi hogy fordul, mennyire hatékony) valóban nem életbe vágók, de érdemes tudni róluk. -
jattila48
aktív tag
válasz
mgoogyi #4144 üzenetére
Nem, nem csak nevezéktanról, hanem lényeges különbségről van szó. A
const int harom=3;
kódban a harom semmilyen módon nem változó, mert a lefordított kódban nem a memóriában elfoglalt címe alapján fognak rá hivatkozni, hanem pl. egy értékadó utasításban konkrétan befordítják a 3 konstans értéket:x=harom;
lefordítva valahogy így fog kinezni:lea eax,DWORD PTR[ebp-12] ;x cime az eax regiszterbe kerul
mov DWORD PTR[eax],3 ;x a 3 konstanst kapja ertekul. a 3 itt jelenik meg a kodbanharom nevu valtozo pedig sehol nincs.Amit angolul idéztél, szerintem kissé értelem zavaró, mert valóban olyan változóként (mintha memóriában el lenne tárolva) hivatkozik rá, aminek az értékét nem lehet megváltoztatni. Ez szerintem nem így van (bár talán lehet fordító függő), annál is inkább, hogy a const int-ek és enumok lehetnek template argumentumok, amiket a fordító egész biztos konstans int-ként fog értelmezni mikor a template-et példányosítja. Sőt a tömb konstans mérete is lehet const int, vagy enum. Mikor a fordító a stack-en (vagy globális adatterületen)" lefoglalja" a megfelelő méretű memóriát a tömb számára, ott konkréten az adott konstans számszerű értékét fogja használni, nem pedig holmi memória címből képzett indirekt hivatkozást.
-
jattila48
aktív tag
válasz
mgoogyi #4142 üzenetére
"Pont nem érdekel, nem látom a gyakorlati jelentőségét"
Pl. az lehet a gyakorlati jelentősége, hogy innen már világos, hogy a C/C++ -ban miért nem lehet dinamikus méretű tömböt deklarálni (bár mint most megtudtam, újabb C szabvány szerint lehet):
int n=10;
int a[n]; //hiba. tomb meret csak konstans lehetEzt kezdők általában nem szokták érteni.
Ha az új C szabvány ezt mégis megengedi, akkor a tömb címe szükségképpen eltárolódik a memóriában (mintha változó lenne), de a fordító nem engedi meg a megváltoztatását és a címének képzését (gondolom így van, de nem ismerem a C-nek ezt a lehetőségét).
Másik gyakorlatban fontos vonatkozása a dolognak, hogy a dinamikusan allokált memóriát fel kell szabadítanod (ha nem akarsz memory leaket), a tömböt pedig nem kell (sőt nem szabad!) felszabadítani (hiszen nem a heapen lett lefoglalva). Tekintsük a következő kódrészletet:int tomb[10];
int *dinamik_tomb=new int[10];
int *pointer;
pointer=tomb;
delete[] pointer; //hiba! nem szabad felszabaditani a tomb-ot
pointer=dinamik_tomb;
delete[] pointer; //OK, fel kell szabaditani a dinamik_tombotAmint látod, a pointer felszabadításánál észnél kell lenni, tudni kell, hogy tömb, vagy dinamikusan allokált memória terület címét tartalmazza-e. Ebből bizony sok hiba adódik (jó párat láttam már), és megy a fejvakarás, hogy miért száll el a program, hiszen a "tomb valójában pointer". Hát nem!
-
jattila48
aktív tag
válasz
mgoogyi #4142 üzenetére
Nevezheted tömbnek a dinamikusan allokált memória területet, de az nem tömb abban az értelemben (konstans mérettel deklarált tömb), ahogy írtam. A különbséget nem írom le még egyszer.
"Pusztán ránézve a kódra én "a"-ra és "b"-re is változóként(előbbire pointer típusú változóként, utóbbira tömb típusúként) fogok hivatkozni."
b nem változó, hiszen nem változhat az értéke, mint ahogy egy változónál (a nevében is benne van) ez megtörténhet. Ennyi erővel a 3-at is nevezheted változónak, holott az sincs sehol a memóriában eltárolva. A tömb esetében kicsit azért kevésbé nyilvánvaló, hogy nem változó, hanem konstans, mert nevet adsz neki. A 3 leírva konstans literál, ezért nyilvánvalóbb hogy ő nem változó. Azonban írhatsz olyat, hogyconst int harom=3;
enum akarmi={nulla,egy,ketto};
ahol a névvel illetett számok szintén konstansok, nem lehet sem megváltoztatni az értéküket, sem a címüket képezni. A tömb ugyanilyen értelemben konstans. Ennek a látszat ellenére igenis van gyakorlati jelentősége, és illik is tudni róla. Csak erre szerettem volna felhívni a figyelmet -
jattila48
aktív tag
válasz
mgoogyi #4140 üzenetére
Attól tartok, teljesen félreérted.
"...én a t-re gondolok, mint változóra"
Ahogy írtam, a t NEM változó, hanem konstans és ez a lényeg. Ha azt írod, hogyint i=j+3;
akkor az i és j változók, amik a memóriában futás időben keletkeznek, a 3 viszont konstans, ami sehol nem tárolódik el e memóriában, hanem a generált kódban jelenik meg mint konstans adat. Hasonlóan a tömb esetén is, sehol nem tárolódik a tömb (mint cím) értéke, hiszen csak egy konstans.
"Az a konstans cím, amiről beszélsz, az maga a t változó tartalma"
Nincs semmiféle t változó.
"Annyit tud egy pointerhez képest, hogy a típusából kifolyólag tud arról, hogy hány elemű"
Nem, nem tud róla hogy hány elemű, hiszen a konstansban ez nincs kódolva. C-ben egyébként sincs tömb index ellenőrzés (futás közben), és a fordító sem különböztet meg ez alapján típusokat (template-tel megoldható, ha a tömb méret template argumentum).
"nem pointer, de mégis pontosan ugyanúgy viselkedik"
Nem pontosan úgy viselkedik, ahogy előző hozzászólásomban írtam, nem adható neki érték, és nem képezhető a címe (szemben a pointerrel).
"Másrészt nem csak a stacken lehet a tömbünk, hanem a heapen is"
Nem, nem lehet a heapen tömb. Az nem tömb lesz, hanem dinamikusan foglalt memória, aminek a címe bekerül egy változóba (vagyis pointer), bár valóban kezelheted tömb szintaxissal, ha az elemeit akarod elérni. Tömböt deklarálni a stack-en lehet, vagy a globális adatterületen (amit a betöltő foglal le a memóriában).
"Egyébként aki most ismerkedik a C++-szal, annak lehet inkább riasztó, mint segítség."
A C++ nem egy egyszerű nyelv, és szerintem jó az elején tisztázni bizonyos dolgokat (pl. ezt), hogy később ne rögzüljön hibásan, mint ahogy az esetedben is. -
jattila48
aktív tag
válasz
Teasüti #4135 üzenetére
"De hisz a tömb is egy pointer.
"
Nem, nem az! C-ben valóban hasonlóan kezelheted a tömböt és a pointert, de ettől még a tömb nem pointer. Sok félreértés és hiba forrása ez, ugyanakkor nagyon kényelmes is tud lenni. A pointer egy változó, ami történetesen memória címet tartalmaz. Mint minden változónak, neki is van címe, és megváltoztathatod az értékét (balérték). Ezzel szemben a tömb egy konstans memória cím (ahol a tönb kezdődik). Ezt a konstanst a fordító/linker/betöltő számolja ki, amit te nem változtathatsz meg, és a címét sem képezheted (hiszen nem is létezik a memóriában).
int tomb[10];
tomb=new int[10]; //Hiba! tombnek nem lehet erteket adni, hiszen az konstans
int **ppi=&tomb; //Hiba! tombnek nem kepezheto a cime, mert az nem valtozo
int *pi=new int[10]; //OK
int **ppi=π //OK valtozonak kepezheto a cime
pi=new int[10]; //OK valtozo erteke megvaltoztathatoAmikor tömb elemre hivatkozol (kiindexeled), a fordító ehhez a konstans címhez adja hozzá az indexet, és erről a címről veszi elő a tömb elemet (intelnél egyetlen utasítás pl. : mov eax, DWORD PTR[ebx+0x1000]; indirekt indexelt címzési mód, ahol 0x1000-nél kezdődik a tömb, ebx-ben pedig az elérni kívánt tömbelem indexe található). Tehát itt a kódba a fordító beleírja a tömb konstans kezdőcímét.
Ha new-val foglalsz helyet, akkor a lefoglalt memória kezdőcímét egy változó kapja értékül, ez a változó a pointer. Ha ezen a memória területen tömb szintaxissal hivatkozol egy elemre, akkor a fordítónak (intel esetén) ehhez legalább két utasításra lesz szüksége. Az elsővel regiszterbe tölti a változó (pointer) címéről a változó értékét (ez maga a new-val foglalt memória kezdőcíme lesz). Ha pl. a pointered címe eax-ben van, akkor mov eax,DWORD PTR[eax] utasítás ezt megoldja. Majd az elem elérése mov eax,DWORD PTR[eax+esi] utasítással történik, ahol az esi tartalmazza azi indexet. Látható, hogy ez jóval lassabb művelet, mint az "igazi" tömb kiindexelése. -
jattila48
aktív tag
válasz
EQMontoya #4106 üzenetére
Melyik ostoba hozzászólásomra gondoltál? Amiben annak minősítettem, hogy szerinted 2018-ban a bináris api fv.-eknek C++ template-eket kéne tudni értelmezni? Ha az szerinted nem volt ostobaság, az baj.
A másik, hogy nincs azzal baj, ha api fv.-eket akarok objektum orientált módon használni, az miért ostobaság? Ilyen simán előfordulhat. Az, hogy te még nem találkoztál ilyennel, nem jelent semmit. -
jattila48
aktív tag
válasz
dobragab #4087 üzenetére
Köszi szépen a válaszodat. Először is engedd meg, hogy egy hibát javítsak:
static RET fptr_handler(ARGS... args)
{
tl_function(std::forward<ARGS>(args)...);
}helyesen:
static RET fptr_handler(ARGS... args)
{
return tl_function(std::forward<ARGS>(args)...);
}Egyébként szép megoldás a problémára.
Lényegében hasonló, mint amit a szimulációban írtam. Ott globálisan hoztam létre az objektumokat, ezek tfv.-eit pedig free wrapper fv.-eken keresztül hívtam meg (amik a globális objektumokhoz hozzáférnek), és a free fv.-ekre mutató fv. pointert tudom aztán használni callback fv. pointerként.
Lényegében a te megoldásod is hasonló, csak nem globális, hanem (thread_local) static std::function objecteket hozol létre (tl_function), amik tartalmazzák az objektum címét (ez a lambdában kapturált argumentum), és a megfelelő tfv. pointert. Majd ezt a tl_function std::function objectet meghívó fptr_handler statikus wrapper tfv.-nek a címét (mint közönséges pattintott C fv. pointert) adod vissza. A trükk pedig az, hogy a to_fptr function template második argumentuma egy []{} dummy lambda, aminek a típusa mindíg (fordító által generált) egyedi típus, ezért a to_fptr_t class template minden egyes alkalomkor külön-külön példányosul (a dummy lambda aktuális típusával), minden egyes function object-re külön osztályt, és azzal külön fptr_handler fv.-t létrehozva. Szép megoldás, gratula! -
jattila48
aktív tag
válasz
PandaMonium #4086 üzenetére
Szerintem meg azt sem érted, hogy pontosan miről is van szó. target-tel nem fv. pointert kapsz vissza, hanem annak az objektumnak a címét, amit az std::function-ban eltároltál (ha ez történetesen fv. pointer volt akkor azt, ha std::bind volt akkor azt). A target template argumentumában pontosan meg kell adni az std::function-ben eltárolt callable object típusát, különben NULL-t kapsz vissza ([link] . Lambdát ugyan lehet megfelelő fv. pointerré konvertálni, de csak akkor ha nem kapturál semmit (ez számomra érdektelen). Szóval előbb olvass, értelmezz és ne az észt oszd!
-
jattila48
aktív tag
válasz
EQMontoya #4084 üzenetére
Milyen API fv. kezel C++ template-eket 2018-ban? Ne beszélj már butaságokat! Tudod mi az az API? Az egy bináris interfész az op. rendszer felé. C++ -ban forráskód szinten használhatók az ilyen konstrukciók, mert a c++ fordító a template-eket megfelelően lefordítja a template argumentumok típusának megfelelő forráskódra, majd azt bináris kódra. Egy bináris interfész-hez hogy akarod ezt illeszteni?
-
jattila48
aktív tag
válasz
PandaMonium #4082 üzenetére
És szerinted ez az std::function egy 32/64 bites fv. pointer lesz, amit assemblyben call eax -szel meg tudok hívni? Mert szerintem nem, de erre válaszoltam EQMontoyanak is. API fv.-nek szeretném átadni, mint callback fv. pointert, aminek fogalma sincs az std::function mibenlétéről. Az egy objektum, amit a ctor.-a másol a stackre, ha argumentum-ként adod át. Mit kezdjen ezzel egy API fv?
-
jattila48
aktív tag
válasz
EQMontoya #4080 üzenetére
A CreateThread-et csak példának hoztam. C++ -ban is előjöhetnek hasonló problémák, pl. különböző objektumok (pl. nyomógomb) tfv.-eit mint üzenetkezelő callback fv.-t kell regisztrálni.
Nem kikerülni, hanem megérteni szeretném a problémát, úgyhogy ne a kikerülésére tegyetek javaslatot. Azt nyilván magam is meg tudom tenni, ha arra van szükségem. -
jattila48
aktív tag
válasz
EQMontoya #4077 üzenetére
Lambdát nem tudsz átadni callback fv. pointerként pl. egy CreateThread vagy StartServiceCtrlDispatcher api hívásnak. Ezek egyetlen pointert várnak, a lambda pedig egy C++ nyelvi konstrukció (a mélyben egy névtelen osztály konstruktorral,...), amit az api fv. nem fog tudni értelmezni.
-
jattila48
aktív tag
válasz
jattila48 #4073 üzenetére
Egy szimuláció a következő lehet:
class Akarmi{
public:
Akarmi(int x):a(x){}
int addto(int x){
return a+x;
}
private:
int a;
};
Akarmi *pa;
int closure_func(int x){
return pa->addto(x);
}
void main(int argc,char *argv[]){
pa=new Akarmi(5);
std::cout << closure_func(6);
}A fenti példában closure_func 5-öt ad hozzá az argumentumához. és nem tfv., címe pedig használható lenne callback fv. pointerként. A gond csak az, hogy ha szükségem lenne egy 11-et hozzáadó closure-re is, akkor létre kéne hozni egy globális Akarmi(11) objektumot is (pl. pa11 nevu pointerrel), és definiálnom kéne egy closure_func11-et, ami a pa11-gyel hívja az addto tfv.-t. Ez nem túl jó megoldás. Amikor előző hozzászólásomban azt írtam, hogy futás közben kódot generálnak, akkor arra gondoltam, hogy kb. ezt a konstrukciót hozzák létre. Amikor létrejön az Akarmi(5) objektum, annak címével (mint konstanssal, vagyis a generált kódban ezt a címet "bedrórozva" konstansként használják) legenerálják a closure_func kódot, és closure fv. pointerként pedig visszaadják a röptében generált kód kezdőcímét. Így talán világosabb mire is gondoltam.
Várom a hozzászólásokat! -
jattila48
aktív tag
C++ -ban nincs lehetőség closure definiálásra, az Embarcadero (Borland) azonban megoldotta ezt a __closure kulcsszó bevezetésével. Szerintetek hogy lehetne sima C++ -ban szimulálni a closure-t? A C++ keretein belül nyilván sehogy, erre kellet a __closure kiterjesztés. De vajon hogy oldják ezt meg a mélyben? Szerintem fordítás/linkelési időben ezt nem lehet, valószínűleg futás közben generálják le a closure végrehajtható kódját (legalábbis egy forwarding fv. kódját), és ezt futtatják. Ehhez azonban Windowsban VirtualProtect-tel futtathatóvá kell tenni azt a memóriaterületet, ahová a kód kerül. Mit szólnak ehhez a vírusirtók?
Ha esetleg valaki nem tudja mi a closure: egy objektum tagfüggvényére mutató fv. pointer. Nem azonos a C++ tfv. pointerével, mert az egy sima fv. pointer, ami történetesen az osztály tfv.-re mutat (fordítás idejű konstans), és csak az osztályból létrehozott objektumon keresztül lehet meghívni. A closure azonban az adott objektumot (a this pointert) "magába olvasztva" (mint konstans értéket) tartalmazza, és csak a this pointeren kívüli argumentumok az argumentumai. Ezáltal alkalmassá valik callback fv. pointerként való használatra (ez a lényeg). -
jattila48
aktív tag
A private tagokat nem csak a konstruktor, hanem az összes tagfüggvény eléri. A tagfüggvények elől nincs elzárva a private tag, csak a külső (nem tfv.) függvények elől. Sőt ugyanannak a típusnak másik példánya is hozzáfér a private/protected tagokhoz (pl. egy olyan tfv., ami argumentumban másik ugyanolyan típusú példányt (vagy pointert/referenciát) kap, szintén gond nélkül eléri az argumentumban kapott objektum private/protected tagjait).
-
jattila48
aktív tag
válasz
choco01 #4060 üzenetére
Ahogy b.kov leírta, k a kölcsönzéseket tároló tömb, ami kolcsonzes * típusú (vagyis kolcsonzes tipusu strukturara mutató pointer). Ez azért pointer, (és nem tömb, de tömbként lesz használva), mert előre nem tudod, hogy hány darab kölcsönzést fog tartalmazni (ezt is fájlból olvasod be a db változóba), tömböt deklarálni pedig csak konstans mérettel lehet. Ha megvan hogy hány darab kölcsönzést tartalmaz a fájl, akkor ekkora méretű tömböt fogsz dinamikusan (new-val) lefoglalni, ami kolcsonzes * típusú pointert ad vissza. Talán az okozza a zavart, hogy nem érted miért kell pointer, ha egyszer tömbbe olvasol be. A C-ben (és C++-ban) pointereket lehet tömbszerű szintaxissal használni (ki lehet indexelni mint a tömb elemet), illetve a tömb neve (indexelés nélkül) a tömb kezdőcímét jelenti. A tömb indexelés tulajdonképpen nem más, mint a kezdőcímhez képest indexszel eltolt pointerre való dereferencia. Tehát tomb ugyanaz, mintha *(tomb+i) -t írnál (ez pedig ugyanaz mint *(i+tomb), ami ugyanaz mint i[tomb]; egy kis nyalánkság a C szintaxisból). Ahogy fent írtam, csak a dinamikus helyfoglalás miatt kell pointer, és nem tömb. Ha azt gondolnád, hogy ilyen módon a pointer és a tömb tulajdonképpen ugyanaz, akkor nem lenne egészen igazad, ugyanis a tömb név (mint a tömb kezdő címe) konstans pointer (fordítás idejű konstans), ezért nem lehet neki értéket adni, és nem lehet a címét képezni, ellentétben a pointer típusú változóval. Elég baj, hogy a C-ben a tömb és pointer fogalma így összemosható, ebből szoktak félreértések (és hibák) adódni. Ennyit a tömbökről.
Hogy miért private ez a tömb (pointer), az pedig azért, mert nem feltétlenül kellene a kölcsönzéseket tömbben tárolni (lehetne pl. lista), ezért az osztályod felhasználója nem kell (nem szabad) hogy tudjon az implementációról, számára elég a publikusan biztosított tagfüggvények ismerete, ami az implementációtól függetlenül mindíg ugyanúgy hívható (ez OOP alapelv).
"De most akkor azt is lehetne írni a feltöltéskor hogykolcsonzes[i].datum
;"
Ebben a sorban nem csináltál semmit, egyszerűen a stackre helyezted akolcsonzes[i].datum
értékét.
"hogyan tehetem a private tagokat elérhetővé más számára?"
Leginkább public tagfüggvények által, magát a private adatszerkezetet nem szokás (hiszen ezért private). Ha feltétlenül elérhetővé akarod tenni, akkor legyen public. Előfordulhat azonban, hogy bizonyos külső (free vagyis nem tfv.-ek) függények számára meg akarod engedni a private/protected adattagok elérését, akkor az osztályod ezeket a fv.-eket friend-ként deklarálja (komplett osztályt is lehet friend-ként deklarálni). A friend deklaráció nem esik a public, private, protected láthatóság hatálya alá, mindegy hová írod. Mindíg az az osztály fogadhat barátjának egy fv.-t (vagy másik osztályt), amelyik ezek számára meg akarja engedni a private tagjainak elérését. Nem fordítva, tehát egy külső fv. nem jelentkezhet be, hogy szeretné az adott osztály private tagjait elérni.
A struktúra és az osztály között összesen az a különbség, hogy a struktúra tagjai default public elérésűek, míg az osztályé default private. Vagyis ha nem írsz ki láthatóságot, akkor ez vonatkozik a tagokra. Más különbség nincs, ettől eltekintve teljesen ugyanúgy használhatók. Szokás szerint, ha csak adattagok vannak, akkor struktúrát használnak, ha tfv.-ek is vannak, akkor osztályt, de ez csak konvenció.u.i.: közben míg írtam a választ dabadab megelőzött, kb. ugyanazt válaszolta.
-
jattila48
aktív tag
válasz
choco01 #4050 üzenetére
tanulo.nev = "Tamas";
helyetttanulo->nev = "Tamas";
sorral próbálkozzál. Úgy jónak kell lenni. Mivel new-val hoztad létre az objektumot, egy erre mutató pointert kaptál vissza (mint ahogy helyesen így is deklaráltad a tanulo változót), aminek a tartalma (*tanulo) maga az objektum, és ennek van nev nevű adattagja. Ezért így is írhattad volna:(*tanulo).nev = "Tamas";
De mivel annyira gyakori konstrukcióról van szó, a rövidség kedvéért err bevezették a -> operátort. -
jattila48
aktív tag
válasz
jattila48 #4044 üzenetére
Ha egyáltalán nem definiálsz copy ctor-t, akkor a fordító generál. Ez valószínűleg jó is lesz neked, mivel nem írtál destruktort. Az értékadó operátor hasonlóan. Egyébként ezt a három fv.-t (copy ctor, értékadó operátor, destruktor) általában vagy mindet definiálja a programozó, vagy egyiket sem.
-
jattila48
aktív tag
válasz
kemkriszt98 #4043 üzenetére
De hát leírtam, hogy nem hívódik. Definiálni kell a megfelelő copy ctort a fv. visszatérési értékének létrehozásához, de a copy elision miatt NEM hívódik meg. Ez azért lehet így, mert az optimalizálás (copy elision) a fordítás után egy későbbi fázosban történik, a fordító pedig előre nem tudja, hogy majd lehetséges lesza alkalmazni a copy elision-t, ezért biztos ami biztos, megköveteli a copu ctor definiálását.
-
jattila48
aktív tag
válasz
kemkriszt98 #4041 üzenetére
A második esetben az értékadó operátorod hívódik meg (ami szintén nem egészen jól van megírva, ld. az előző kommentemet). Viszont az A::initFromFile függvényed érték szerint ad vissza A típusú objektumot (egyébként vissza adhatná new-val létrehozott objektum címét is, vagy még jobb, ha unique_ptr-ben adja vissza), aminek a létrehozásához mindenképpen szükség van a copy ctor.-ra. Látni kéne ezt a fv.-t, mert lehet, hogy a visszatérő értéket nem bal értékből állítja elő (pl. return A() formában valamilyen ctor.-ral, ahol a ctor.-ral létrehozott objektum a fv. törzsében temporális lesz), ez esetben pedig a stack-en történő visszatérő érték létrehozásához a const A& argumentumú copy ctor-ra van szükség. Megjegyzendő, hogy a modern C++ fordítók ilyen esetben a copy elision nevezetű optimalizálást fogják végrehajtani, vagyis valójában nem hívják meg a copy ctort, hanem egyből a stacken mint visszatérő értéket hozzák létre a szóban forgó temporálist. Ennek ellenére a megfelelő copy ctor definiálását megkívánják. Összefoglalva: valószínűleg az initFromFile fv.-ed visszatérő értékének létrehozásához követeli meg a fordító a megfelelő copy ctort.
-
jattila48
aktív tag
válasz
kemkriszt98 #4039 üzenetére
Ahogy EQMontoya mondta, a copy ctor.-nak A(const A&), az értékadó operátornak pedig A& operator=(const A&) típusúnak kell lenni. Mivel neked ilyen ctor.-od nem volt, ezért szólt a fordító. Amit te írtál az is copy ctor, csak a programodban nem volt megfelelő, mert a változót A::initFromFile("asd") fv.-el akartad inicializálni, aminek a visszatérő értéke nem balérték (nem lehet neki értéket adni, nem lehet a címét képezni,... A fv.-ek visszatérő értékei nem balértékek, hanem csak temporálisok). A C++ szabvány szerint nem balérték pedig csak konstans referenciához köthető. Ezért nem volt jó a te copy ctor.-od, és ezért jó a const referencia argumentumú copy ctor. Ugyanígy az értékadó operátorra is. Ha az inicializálás/értékadás jobboldalán nem fv. visszatérési értéke lenne, hanem egy közönséges változó (balérték), akkor a te ctor.-os és értékadó operátorod is jó lenne. Egyébként milyen fordítót használsz? Mert a MSVC a szabványtól eltérően megengedi nem balérték nem const referenciához kötését. Lehet, hogy azzal működne a programod.
Nem próbáltam ki, de szerintem itt lehet a baj. -
jattila48
aktív tag
C++ -ban sajnos nincs (java értelemben vett) interface, csak valami hasonló az absztrakt class-okkal. Ez azonban nem ugyanaz (egyébként nem ismerem a Javát), mert az ilyen osztályok mérete nem 0. Ebből adódik a jól ismert diamond probléma, annak minden nyűgjével. Ilyen pl., hogy a virtuális ősosztály pointert nem lehet static_cast-olni leszármazott osztályra (csak dynamic_cast => runtime overhead), és a legleszármazottabb osztály konstruktorából kell hívni a virtuális ősosztály konstruktorát. Ez akkor is így van, ha a virtuális ősosztály összes tfv.-e pure virtual és nincs adattagja (interface). Nekem most nagyon jól jött volna, ha java-szerű interface lenne a C++-ban, és nem kell megküzdeni (fölöslegesen) a diamond problémával. Végül nem is használtam többszörös öröklést emiatt, megoldottam kompzícióval. Így azonban az ősosztály pointert kell static_cast-olnom egy típusmezőtől függően egyik illetve másik leszármazott osztály pointerré, és ezzel elérni a kompozícióval mindkét osztályban létrehozott tagobjektumot, holott ez lehetett volna ős is.
Az MSVC-nek van ugyan _interface kulcsszava, de az nem különbözik attól, mintha tiszta absztrakt osztály írnék. Van-e olyan C++ fordító, ami valami java féle interface-t támogat? Nincs is tervbe véve hogy későbbi szabványok támogatni fogják? -
jattila48
aktív tag
válasz
nonsen5e #3826 üzenetére
.hpp-be lehetőleg ne tegyél fv. vagy globális változó defíníciót (csak deklarációt), mert ezeket beinklúdolod a különböző fordítási egységekbe, amik le is fordulnak, de mindegyik object kódban megjelenik a beinklúdolt fv. kódja, amit a linker többszörös definíciónak vesz. Pl:
//akarmi.hpp
#ifndef _AKARMI_HPP_
#define _AKARMI_HPP_
int f(int x){
return x;
}
#endif
//akarmi1.cpp
#include "akarmi.hpp"
#include <stdio.h>
int g(int x);
void main(void){
printf("%d",f(5));
printf("%d",g(6));
}
//akarmi2.cpp
#include "akarmi.hpp"
int g(int x){
return f(x);
}Itt az akarmi1.cpp-be, és az akarmi 2.cpp-be is bekerül az f fv. kódja, linkeléskor pedig dupla definíció miatt hibát ad a linker. Helyesen az akarmi.hpp-be csak deklaráció kerül (fv. prototype)
//akarmi.hpp
#ifndef _AKARMI_HPP_
#define _AKARMI_HPP_
int f(int x);
#endifés valamelyik .cpp állományba (akár új akarmi3.cpp-be) írod az f fv. definícióját.
Remélem tudtam segíteni. -
jattila48
aktív tag
válasz
dobragab #3621 üzenetére
Nincs túl nagy különbség a két megoldás közt. Végül is nálad is van type switch, csak egy kicsit másképp. Scope alatt azt értem, ami pl. a Pascal-ban a scope. Egy adott sope-ban nem lehetnek azonos nevű szimbólumok, de egymásba ágyazott scope-okban vagy különállóakban már igen. A belső scope-ban lévő név elfedi a bentfoglaló scope-ban lévő ugyanilyen nevet. Ha kilépünk a scope-ból, akkor megszűnnek e nevei. Tehát a szokásos. Egységes szimbólum táblát (és vektort benne) a scope egyszerűbb kezelése miatt vezettem be. Nálad a különböző szimbólum táblák mindegyikében kezelni kell a scope-ot, ki kell jelölni a scope határokat. Nálam csak az egyetlen táblázatban. Nálad viszont valóban nincs típus attribútum, és static_cast. A set szerintem nem alkalmas, vagy csak akkor, ha scope-onként is külön táblázatokat tartasz fent, és ezeket stack-be szervezed. Azt hiszem maradok a static_cast-nál.
Köszönöm mindenkinek a hozzászólást! -
jattila48
aktív tag
válasz
dobragab #3617 üzenetére
Kicsit visszatérve a type switch vs. virtuális fv. problémához:
Szerintem a zavart az okozza, hogy a szimbólumot reprezentáló osztály type mezője nem az osztály típusát jelöli, hanem a szimbólumét. Egy objektum típusa meghatározza, hogy rajta milyen műveletek végezhetők. Itt azonban a típusmező a név forráskódbeli szerepére utal, nem pedig a szimbólum osztályon végezhető műveletekre. Ez két külön dolog. A virtuális fv. az objektum típusához kapcsolódik, nem pedig a szimbólum név típusához. A szimbólum név típusa ugyanolyan attribútum, mint a többi, amik a program szerkezetére lesznek hatással (hogy generálja a kódot a fordító, a név típusától függően), ezért a type switch elkerülhetetlen. Mellesleg éppen ezért helytelen type switch-nek nevezni a dolgot. -
jattila48
aktív tag
válasz
dobragab #3617 üzenetére
A set-ben (vagy nem vektorban) való tárolásról még annyit, hogy a scope kezelést nem lehet megoldani benne (hogy tartod nyilván a scope határokat?). A sope-ok stack szerűen rakódnak egymásra, ezt pedig vektorban a legegyszerűbb kezelni. Ha set-et használsz, akkor scope-onként külön-külön (sőt szerinted ezen belül még típusonként is) szimbólum táblákra lenne szükség (annál is inkább, mivel különböző scope-okban lehetnek azonos nevű szimbólumok,a set már csak ezért sem alkalmas), amiket aztán a scope-oknak megfelelően stack-be szervezel. Biztos, hogy ez jó elgondolás?
-
jattila48
aktív tag
válasz
dobragab #3617 üzenetére
"Futásidejű költsége nem a static_cast-nak van, hanem a type switch-nek"
Én is ezt írtam. A type switch-et meg nem tudom elkerülni, mert mikor megtalálok egy szimbólumot, akkor a tíőusától függően kell folytatni a fordítást. Pl. egész mást kell csinálni ha a szimbólum változó, mint ha függvény. És azt előre nem tudom, hogy a keresett szimbólum milyen típusú lesz.
Nem értem mi előnye lenne a különböző típusok külön tárolásának, azonban azt látom, hogy rengeteg a hátránya.Minden, típusonként külön szimbólum táblában kezelni kell a scope-ot, holott a scope a típustól függetlenül ugyanúgy vonatkozik az összes szimbólumra. A find_symbol fv.-nek végig kell keresni az összes szimbólum táblát, és attól függően, hogy melyikben találta meg a szimbólumot, vissza kell hogy adja a típusát (ezután pedig mindenképpen type-switch jön). Sőt nem csak a típusát, hanem valami módon magát a szimbólumot is, pl. iterátorral. A visszaadott iterátor minden esetben más típusú lesz, ha csak az összes szimbólum nem egy közös őstől származik, és a táblázatok az ős pointert tárolják, amiket aztán ugyanúgy típustól függően static_cast-olni kell (mint ahogy most is csinálom). De akkor miért kéne külön táblázatokba tenni? Ha valamiért új típusú szimbólumot kell bevezetni, akkor a find_symbol fv.-t bővíteni kell az új típusnak megfelelő táblázat keresésével. Ezek mind hátrányok, és bonyolítják a programot. A Te megoldásod egyetlen "előnye", hogy a szimbólumokban nem kell a típusukat tárolni.
Az, hogy a táblázat vektor-e, vagy más, teljesen lényegtelen. Max. pár száz szimbólumról lehet szó, ennyire pedig talán a vektor overhead-je a legkisebb, úgyhogy a keresés sem lesz túl lassú (egyébként is csak fordításkor van szimbólum tábla, futáskor már nincs).
Egy szó mint száz, nem tudsz meggyőzni a külön-külön tároláskor, de nem is ez volt a kérdés. A static_cast nekem sem tetszik, de nem tudok jobbat. -
jattila48
aktív tag
válasz
mgoogyi #3611 üzenetére
De, erről van szó. Azonban a kód generálás, már nem a szimbólum tfv.-einek hívásával történik, hanem a szimbólum attribútumai alapján. A megtalált szimbólum állapotán már nem változtatunk. Ahogy írtad, pl. függvények, változók neveiből keletkeznek szimbólumok, teljesen különböző attribútumokkal. Ezért nem lehet egységes interfészen kezelni őket, viszont mégis egy táblázatban kell tárolni ezeket.
Nyugodtan dumálj bele, nem leugatni akartalak. Bocs, ha így érezted. -
jattila48
aktív tag
válasz
mgoogyi #3606 üzenetére
Nekem úgy tűnik, mégsem egészen tudod, mi a szimbólum tábla (bocs). Ha egyszer bekerült a szimbólum a táblázatba, akkor azt már nem kell "feldolgozni" (legfeljebb törölni), hanem szükség szerint megtalálni kell, és az attribútumai alapján generálni a megfelelő kódot. A tárolt szimbólumnak ha lenne virtuális fv.-e, akkor az csak getter lenne, de semmiképpen nem "processz". Tulajdonképpen a find const pointereket ad vissza, mivel egyáltalán nincs szükség a megtalált szimbólum megváltoztatására. Azonban a szimbólumok, ahogy írtam teljesen különbözők, így nincsenek hasonló attribútumaik sem (a nevet és típust kivéve). Ezért nem lehet (és nem is kell) őket egységes interfészen keresztül kezelni.
-
jattila48
aktív tag
válasz
mgoogyi #3603 üzenetére
Ne haragudj, de szerintem nem érted miről van szó. Persze, hogy csak a pointerek vannak sorfolytonosan a vektorban (hiszen írtam, hogy csak ezeket tárolom vektorban). EQMontoya arra gondolt, hogy vektorban (a sorfolytonos elrendezés miatt), kis elemszám esetén gyorsabb az ismétlődés keresés, mint pl. set-ben. Ez akkor is így van ha, csak az objektumok pointereit tároljuk, mert az algoritmus ezen fog föl-alá futkosni, miközben a pointerek által hivatkozott objektumokat hasonlítja össze. Ha jól van megírva az ismétlődés kereső algoritmus, akkor mindössze egy összehasonlító operátort kell neki átadni, és pont ugyanúgy működik, mint egyéb esetben.
Na de ennek semmi köze az eredeti problémához, mert szimbólum táblában NEM keresünk ismétlődést. -
jattila48
aktív tag
válasz
mgoogyi #3600 üzenetére
Leírtam, hogy miről van szó. Ha nem tudod mi az a szimbólum tábla, akkor azt most nem tudom teljes részletességgel elmagyarázni. Nem ismétléseket kell keresni benne, hanem hanem amikor a programszövegből új nevet olvas a szintaktikus elemző, meg kell állapítani, hogy előfordult-e már ilyen nevű szimbólum. Ezt vagy azért teszi, mert új nevet definiálunk (ekkor ellenőrizni kell, hogy a scope-ban szerepel-e már), vagy azért, mert hivatkozunk rá (ekkor ki kell keresni (nem csak a scope-ból), és a tárolt attribútumai szerint folytatni a fordítást). Leírtam, hogy miért kell az összes különböző típusú szimbólumnak egy táblázatban szerepelni. Mivel különböző típusúak, ezért nem lehet őket egységesen (polimorf módon sem) kezelni. Az, hogy a szimbólum tábla most vektor-e, map, vagy hash tábla, teljesen mindegy. Nekem bőven elég a vektor. A hangsúly a heterogén tároló használatán van.
-
jattila48
aktív tag
válasz
dobragab #3597 üzenetére
Hát akkor marad a típus vizsgálat, static_cast. Nem tudok jobbat. A típusvizsgálatot virtuális fv. használatakor sem lehet elkerülni, hiszen a konkrét típus ismeretében tudom csak eldönteni, hogy meghívjam-e egyáltalán a virtuális fv.-t (ami ráadásul a többi osztályban esetleg nincs is implementálva). Ez után a static_cast már csak a fordítót terheli, a futást nem, szemben a virtuális fv. hívással.
A különböző típusok külön táblázatban való tárolása nem opció, mert nagyon megnehezíti a szimbólum hozzáadást, -keresést, scope kezelést, ráadásul ezeket a fv.-eket át kell írni, ha új típusú szimbólumot vezetnék be. Ez egy olyan példa, hogy noha a tárolandó osztályoknak látszólag nem sok közük van egymáshoz, mégis más okból (scope-ban nem lehetnek azonos nevű szimbólumok) célszerű őket együtt tárolni. Szóval szerintem nem tervezési hiba. A külön táblázatok viszont az lenne. -
jattila48
aktív tag
válasz
dobragab #3597 üzenetére
Ez most egy kicsit bonyolult, de pl. változó esetén a stack-en elfoglalt helyét, az érték típusát, és a statikus level-t tartalmazza. Függvény esetében a fv.-tábla-beli pozíciót, paraméterek számát, típusát, stb. Osztály definíció esetén ...elég bonyolult. A lényeg, hogy teljesen mások a szimbólumok, viszont a scope kezelés miatt egy vektorban (igazából stack-ben) kell őket tárolni.
-
jattila48
aktív tag
válasz
dobragab #3595 üzenetére
Konkrétan ez egy interpreter szimbólum táblája. Lehet benne változó név, típus név, foglalt kulcsszó, függvény név, stb. A konkrét típustól függetlenül, az adott scope-ban nem lehetnek egyező nevű szimbólumok, akkor sem, ha különböző típusúak. Ezért kell őket egy vektorban tárolni, hogy ezt ellenőrizni lehessen. Még csak neve sincs minden szimbólumnak. A változó pl. teljesen másképp kezelendő, mint a típus definíció. Nem hiszem, hogy ez tervezési hiba lenne.
-
jattila48
aktív tag
válasz
dobragab #3593 üzenetére
Erre gondoltam, de mit adjon vissza a virtuális fv.? A konkrét típusú this pointert (kovariáns típus)? Az nem jó. Más virtuális fv.-t meg nem lehet bevezetni, mert amúgy a típusok teljesen különbözőek. Az egyiken elvégezhető művelet értelmezhetetlen a másikon. Ebben az esetben mit csináljon a virtuális fv,? Exception-t dobjon? Vagy esetleg az ősbeli pure virtual-nak legyen törzse, ami exception-t dob, ha meghívódik? Másrészt az ősosztályban fel kéne venni virtuális fv.-ekként az egyes konkrét osztályokon értelmezhető műveletek unióját. Vagy rosszul értem, amit írtál?
-
jattila48
aktív tag
Egy vektorban szeretnék különböző típusú objektumokat tárolni. Ezt nyilván nem lehet, ezért a tárolandó objektumok osztályai egy közös őstől származnak, ami csak a konkrét leszármazott típusra utaló enum mezőt tartalmaz. Az objektumokat new-val hozom létre, és a pointereket (mint ősre mutató pointer) tárolom el a vektorban. Igazából unique_ptr-eket tárolok, de ez most lényegtelen (std::vector<std::unique_ptr<Base> >). Amikor a vektor egy elemére hivatkozok, akkor az adott unique_ptr-ből nyert raw pointert kapom vissza, amit a típusmezőnek megfelelően futás időben static_cast-olok a konkrét leszármazott típusra. Ezzel az a baj, hogy állandóan vizsgálni kell a típusmezőt, és elvégezni a static_cast-ot. Van-e ennek jobb megoldása? (Boost-ot nem használok, ezt ne is javasoljátok).
-
jattila48
aktív tag
válasz
dobragab #3453 üzenetére
Légy szíves írj már rá egy jó kódot, ha már az enyém annyira szar! De legyen aztán boost, regexp, STL, meg minden nyalánkság, ami egy igazán szép C++ kódba kell! Egyébként milyen "súlyos hibákat" fedeztél még fel benne? Az eddig tárgyaltak: strlen, enum helyett int, char[] tömb. Elmondtam, hogy miért. Még valami? Ezek nyilván olyannyira "súlyos hibák", hogy ettől aztán egy életre tönkre teszem vele a gondolkodásmódját, és ezután már csak ilyen szar kódot lesz képes írni.
"nem biztos, hogy észreveszi, hogy szar, több sebből vérzik, esetleg gyanútlanul be is másolja a kódot, vagy úgy gondolja, így kell kódot írni... Szemben azzal, hogy útmutatásként szar kódot kapott."
Nem liheged kicsit túl ezt a dolgot? Nem vagy egy kicsit nagyképű? (dehogy is nem!)
-
jattila48
aktív tag
válasz
dabadab #3450 üzenetére
Ez igaz általában, de most IPV6 sztringről volt szó, ami 40 karakter hosszú (lezáró 0-val együtt). Egyébként az algoritmusra gondoltam, hogy jó. Abban is találtál "hibát"? Tényleg nem értem miért akartok minden áron minden apró szir-szar-ba belekötni, nem ez volt a lényeg. Tekintsétek úgy, hogy az algoritmust írtam le pszeudo kódban! Világos? Elhiszed, hogy saját production kódomban nem írok ilyet (ha nem, az sem baj)? Hadd ne kelljen már egy vázlat-szerű válaszban minden apró szarnak tökéletesnek lenni! Az algoritmust érted? Az jó? Ha nem, akkor szóljál és javítsd ki!
-
jattila48
aktív tag
válasz
dobragab #3445 üzenetére
Nem egészen értünk egyet.
1. Attól, hogy valami hatékony, még lehet egyszerű és világos. Sőt! Ez az algoritmus is világos, de ha nem, akkor nagyon szívesen elmagyarázom. Hagyjuk meg a kérdezőnek, hogy eldöntse számára világos-e vagy sem. Egyelőre még nem szólt hozzá.
2. A kódom nem rossz (legalábbis remélem), hanem lehetett volna szebben is írni, ez igaz. Ettől a lényeg (maga az algoritmus) még jól látszik rajta. Hirtelen felindulásból 5 perc alatt ennyire futotta. Nem a kódolást akartam követendőként állítani, hanem hogy, lehet a saját józan eszünket is használni ahelyett, hogy rögön mindenféle túlfejlett apparátust (boost, regext,...) kezdenénk mozgatni apró problémákra. Erről már többször írtam, a véleményem továbbra is fenntartom. Ha megérti ezt az algoritmust, akkor talán tanult is valamit (remélem), és már megérte.
3. Azt, hogy illik-e manapság C kódot írni STL helyett,... hát a fene tudja. Én sokszor szoktam ilyen kisebb feladatoknál, ugyanakkor az STL-t is használom. Szerintem jól megférnek egymás mellett. Itt a példában az algoritmus szerintem jobban látszik STL tárolók használata nélkül. Nem vonja el fölöslegesen a figyelmet. Továbbá azt is érdemes megjegyezni, hogy az STL tárolók használatának ára van. Fölösleges ide-oda másolások (túl sok ilyen kódot láttam), ha nem pont alkalmas a tároló amire a használni akarja, akkor egész elborzasztó "patkolásra" is képesek,...
4. Egyébként ha nagyon akarja valaki, akkor könnyen ki lehet cserélni STL tárolókra a tömböket, és akkor mindenki boldog lesz.
Én remélem tudtam segíteni, ellenben tőletek semmilyen konkrét segítséget nem kapott.
-
jattila48
aktív tag
válasz
dobragab #3442 üzenetére
Az eredmény tömböt úgy juttatom ki, hogy előbb bejuttatom. Vagy helyben történik a feldolgozás, és akkor az input egybe output is, vagy kívül foglalok az output tömbnek helyet, a rámutató char * pointert és a puffer méretét pedig paraméterként kapja a fv. De ezt szerintem neked nem kell magyarázni.
-
jattila48
aktív tag
válasz
dobragab #3442 üzenetére
Ezt a restrict-et nem tudom mi, sose használtam, de azt hiszem eltértünk a lényegtől. Szerintem akár strlen, akár nem, attól az algoritmusom még hatékonynak mondható. Maradjunk annyiban, hogy a for így módosul:
for(int i=0;ipv6_addr[i]!=0;++i)
És most már próbáljatok nem kötekedni, hanem érdemben segíteni a kérdezőnek. Köszi! -
jattila48
aktív tag
válasz
Jester01 #3434 üzenetére
Már miért lenne fölösleges az strlen? Abban biztos lehetsz, hogy nem fog minden kiértékeléskor lefutni, ezt kioptimalizálják. Rengeteg hasonló ciklust láthatsz példa kódokban. Egyébként hirtelen nem jutna eszembe ennél hatékonyabb megoldás (esetleg neked?), úgyhogy engedd meg, hogy annak mondjam.
-
jattila48
aktív tag
válasz
dobragab #3433 üzenetére
Valóban a trim-nek enum-nak kéne lenni. Ha több állapot lenne, vagy esetleg bővülhetne, akkor biztos úgy csinálnám. Persze osztályt is lehetne csinálni belőle, meg string-gel dolgozni. Csak az algoritmust akartam bemutatni, hogy mennyire egyszerű, és nem kellet hozzá sem boost, sem regexp, sem vector<string>, amiknél feltehetőleg jóval hatékonyabb így (futásidőben, és memória használatban is (főleg ha helyben történik a feldolgozás)).
-
jattila48
aktív tag
válasz
bandi0000 #3429 üzenetére
Egy hatékony megoldás:
const char *ipv6_addr="2001:0e00:41a0:006b:00de:03c0:0e00:60bc";
char trimmed_ipv6_addr[40];
int trim=1,j=0;
for(int i=0;i<strlen(ipv6_addr);++i){
char c=ipv6_addr[i];
if(c==':')trim=1;
switch(trim){
case 0:
trimmed_ipv6_addr[j++]=c;
break;
case 1:
trimmed_ipv6_addr[j++]=c;
if(c=='0')trim=2;
else if(c!=':')trim=0;
break;
case 2:
if(c!='0'){
trimmed_ipv6_addr[j++]=c;
trim=0;
}
break;
default:
//ilyen nem lehet
;
}
}
trimmed_ipv6_addr[j]=0;Akár "helyben" is elvégezhető (ha az átalakítandó karaktertömb már eleve a trimmed_ipv6_addr változóban van).
-
jattila48
aktív tag
válasz
EQMontoya #3342 üzenetére
Én 136-ot mondanék futtatás nélkül. Többszörös öröklődésnél a this pointert igazítani kell, amit az ún. thunk kód végez. Erről írtam a pimpl példámnál, amikor hatékonysági okból a forwarding fv. hívásokat member pointer-ekkel helyettesítettem. Ha csak az osztály incomplete deklarációját látja a fordító, akkor nem tudja eldönteni, hogy kell-e this pointer igazítás vagy nem, ezért a legrosszabb esetre készült. Ez volt az a bizonyos assembly kód, amit a VS feltehetőleg rosszul linkelt. Ha azonban a a forward deklarációban közöljük, hogy a szóban forgó osztály _single_inheritance (MS kulcsszó), akkor nem generálja a felesleges thunk kódot, és jól működik. A gcc-nek nem okozott ez problémát, persze az is generálta a thunk kódot.
-
jattila48
aktív tag
válasz
dobragab #3322 üzenetére
Jó összefoglaló, csak egy kis kiegészítést engedj meg:
"- default ctor: végighívja az ősosztályok és adattagok copy ctor-át, sorrendben."
Itt fontos megjegyezni, hogy az ősosztályok ctorát az öröklési sorrendben, az adattagokét pedig deklarációjuk sorrendjében hívja. Vagyis nem feltétlenül a taginicializáló listában írt sorrendben (néhány fordító warning-ot generál, ha a taginicializáló lista sorrendje eltér a deklaráció sorrendjétől). Ezek után hajtódik végre a szóban forgó objektum ctorának törzse. A destruktorok hívása éppen fordított sorrendben történik: Először az adott dtor törzse fut le, ami végül a ctor-ok hívásának fordított sorrendjében meghívja az adattagok és ősosztályok dtorait.A move operációt illetően: a szabvány bizonyos feltételek mellett (kb. amit leírtál) előírja implicit move operáció generálását, azonban erősen kétséges, hogy ez valóban jó ötlet-e. Bizonyos fordítók (talán a VS2010 is) ezt nem teszik. Ha egyáltalán generálható implicit move operáció, akkor az kb. ugyanaz mint a copy operáció, ezért fölösleges a move. A move-nak akkor van igazán értelme, amikor implicite nem generálható, pl. user defined dtor esetén. A move-val legfőbb probléma az, hogy milyen állapotban maradjon a moved-from objektum, vagyis az operáció forrás objektuma. Legyen default konstruált? Mi van, ha nincs default ctor-a? Az "elmove-olt" erőforrást a dtor-nak már nem szabad felszabadítania. A moved-from objektum egyfajta "zombivá" válik, hasonlóan a kétfázisú inicializálással létrehozott objektum esetén, amikor a ctor már lefutott, de az inicializálás még nem történt meg teljes egészében (ez mint tudjuk, kerülendő). A moved-from objektum ezután már csak copy vagy move assignment cél operandusaként használható, vagy megsemmisítendő, de minden esetre ügyelni kell rá, hogy ne használjuk újra (mint ahogy a félig inizializált objektumokat sem).
-
jattila48
aktív tag
-
jattila48
aktív tag
Hogy az eredeti kérdésedre válaszoljak:
Stanley Lippmann: C++ primer
van magyar kiadása is (nem a legújabb) C++ először címmel. Szerintem egész jó könyv kezdőknek. Utána pedig természetesem a Sroustrup könyv. Az internetem is rengeteg könyvet/cikket találsz pl. Scott Meyers effective c++, vagy Herb Shutter GotW cikkei. A DrDobbs folyóirat cikkei kifejezetten jók C++ témakörben (meg másban is).
Egyébként mivel a C gyakorlatilag része a C++-nak, nincs értelme azon vitázni, hogy a kettő közül melyiket tanuld meg. A C-t meg tudod tanulni anélkül, hogy C++-t tanulnál, de fordítva nem. Ha C++-t tanulsz, akkor meg fogod tanulni a C-t is. És it most nem az a lényeg, hogy nem printf-fel iratsz ki. Értelmetlen azon vitázni, hogy a pointerek használatát C-ben vagy C++-ban sajátítod el, mert ez egy és ugyanaz. Referenciák meg nincsenek a C-ben. Aki nem ismeri alaposan a C nyelvet, az a C++-t sem ismeri. Kezdőknek pedig nem OOP szemléletet és STL-t kell tanítani, hanem írja meg C-ben az alap algoritmusokat (keresés, rendezés, stb), értse meg a paraméterátadás rejtelmeit (érték, cím,...), írjon rekurzív fv. hívást, stb. Ez később mind hasznára fog válni, amikor már a C++ esetleg magasabb szintű absztrakcióit használja. Pontosabban enélkül, soha nem lesz belőle jó C++ programozó. Akkor jön az, hogy mindenre (a legegyszerűbb dolgokra is) kész STL tárolókat/algoritmusokat és könyvtárakat keres, mert nem érti elég mélyen a problémát (hiszen soha nem ásott bele amúgy "pattintott" C megközelítéssel), és azt sem látja át, hogy a készen kapott megoldások nem feltétlenül alkalmazhatók az ő problémájára. Na ekkor jön a patkolás: az eszközhöz igazítja a feladatot, és nem fordítva. A C++ és az OOP csak eszköz, és ha "pattintott" C megközelítéssel egy feladata átláthatóbban, hatékonyabban oldható meg, akkor úgy kell megoldani. Nincs ebben semmiféle ellentmondás. A struktúrált és az OOP megközelítés nem zárják ki egymást, egyik sem szentírás. Az osztályod tfv.-eit legalábbis igyekszel struktúrált módon megírni (C-ben az sem szentségtörés ha nem), még ha az adatszerkezet OOP megközelítésű is. A virtuális tfv.-ek mibenlétét, működési módját, használatát sem igazán lehet megérteni anélkül, hogy C-ben soha nem írtál fv. pointereket tartalmazó tömböt. Nem szaporítom tovább a szót, a lényeg, hogy teljesen felesleges és mesterséges a C és C++ nyelvek szembeállítása, mivel nem két külön nyelvről van szó. -
jattila48
aktív tag
válasz
dobragab #3266 üzenetére
VS használható mingw-gcc-vel. Próbáld ki a VisualGDB-t. 30 napos teljes értékű próba verzió, utána fizetős (nem túl drága). Vannak még hibái, de egész jó. Az MSVC-nél én is találtam olyat amit le kellett volna fordítania, de mégsem tette (ezeket itt meg is tárgyaltuk). Amit "többet" tud mint a szabvány (microsoft extensions), arra adhat warning-ot, vagy hibát, vagy semmit (beállítható). Ilyen pl., hogy temporális értéket megenged nem const lvalue referenciához kötni (szabvány szerint csak const lvalue vagy rvalue referenciához lenne köthető).
-
jattila48
aktív tag
válasz
dobragab #3260 üzenetére
"Windows-ra fordításhoz ott a mingw-gcc cross"
A mingw-gcc valóban jó, de még kell rajta dolgozni. A Windows COM-mal pl. nagyon nem boldogul. A saját headereit sem mindig fordítja le. Különböző rejtélyes makrókat kell #define-olni (vagy preprocessor-nak definiálni), hogy rá vedd a fordításra. Több tízezer soros header-ek esetén ennek kiderítése igencsak strapás.
-
jattila48
aktív tag
válasz
ToMmY_hun #3261 üzenetére
"Egyébként minden egyszerűbb rajta, szóval C++ fejlesztéshez ideálisabb, mint a Microsoft kacatjai"
Ebben engedd meg, hogy vitatkozzak veled! A Microsoft VS kifejezetten jó IDE környezet, stabilitásban, használhatóságban nincs Linux megfelelője (szerintem!). A C++ fordítója is a legjobbak között lehet, bár nem mindig tartja be a szabványt. Generált kód minőségét (sebesség, méret, szabvány megfelelés) pedig a gcc-ét nem múlja felül egyik általam ismert fordító sem.
-
jattila48
aktív tag
válasz
dobragab #3260 üzenetére
Ez lehet, de ha speciel Windows-on kell vele dolgoznom, akkor nem vigasztal. Linux-on nem kell buildelni, nem függ a gcc verziójától, a lib-ek verziói is stimmelni fognak, vagy csak mindezt magától megoldja az apt-get? Azért Linuxon is szívtam én már hasonlóan (a boost-ot nem használtam).
-
jattila48
aktív tag
válasz
ToMmY_hun #3258 üzenetére
Biztos van könnyen kezelhető könyvtár is, de sajnos van aminek az installálásától is lesz néhány ősz hajszálad. A boost-ot pl. azzal az adott fordítóval komplett buildelni kell, amivel majd használni fogod (több óra). Buildelés során hol ezt, hol azt nem talál, az SDK verzió nem jó, stb. Ha esetleg előre buildelt változatot használsz, akkor nem fog működni más verziójú fordítóval, a lefordított kód fut windows7-en de nem fut XP-n, könyvtárak és object fájlok verziói nem stimmelnek, nem lehet statikusan a programhoz linkelni, stb. Aztán jön a dependency walker, meg a szarakodás, hogy mégis mi a fene baja van. protocol buffer hasonló, kicsiben. De az OpenSSL-ből sem szokott jó lenni az előre buildelt. Amikor könyvtárat használsz (főleg template könyvtárat mint a boost), akkor erre is fel kell készülni.
-
jattila48
aktív tag
válasz
ToMmY_hun #3255 üzenetére
Végül is, te tudod... A tapasztalatom az, hogy sokszor érdemes az egyszerű problémát egyszerűen megoldani. Ha saját megoldást dolgozol ki, akkor mélyebben megérted a dolgok működését, és később jobban fogod látni, hogy a könyvtári megoldásoknak mik az előnyei, hátrányai, és érdemes-e alkalmazni egyáltalán. Másrészt ezeknek a kész könyvtáraknak az installálása, használatának megtanulása sokszor több időt/energiát emészt fel, mint ha megírod saját magad a kis problémádat megoldó kódot. Persze ha már ismered, akkor más a helyzet. Én pl. a boost-ról (de kicsit a protocol bufferről is) gondolom, hogy jó-jó, de amire használtam (asio) arra túl sok energiát pazaroltam el miatta. Ezek a könyvtárak (a boost főleg) sokszor erősen "túldizájnoltak" és marha nagyok. Belekényszerítenek egy olyan keretbe, ami nem feltétlenül jó az adott feladatra, és ez rengeteg plusz munkát jelent. (ez most nem flame akart lenni, pusztán vélemény!)
-
jattila48
aktív tag
válasz
dabadab #3254 üzenetére
A protokoll maga a szerializált csomag. Ha ezt TCP-n (vagy UDP-n) átküldöd, a másik oldal ugyanúgy fogja értelmezni. Az mit jelent, hogy a socket-ek kezelése nem thread safe? A socket az nem egy globális valami, amit több thread egyszerre használ, hanem azt maga a thread hozzza létre saját magának. Másik thread másik socket-et hoz létre. Innentől kezdve természetesen thread safe. Ha a program jól van megírva, akkor nem fognak a thread-ek közös socket-et használni, így szinkronizációra sincs szükség. Ha arra gondolsz, hogy több thread ugyan annak a távoli partnernek akar adatot küldeni, akkor persze szinkronizálni kell az írást, de erről nem a socket tehet. Ezt bármilyen library-t használva is szinkronizálni kéne. Itt nem arra gondolok, mint pl. a web szerver esetén, amikor sok kapcsolat épül ki a klienstől a szerver felé, amiket külön-külön thread-ek saját socket-eken keresztül kezelnek, hanem amikor egy adott konnektált socketre akar több thread írni. Az ftp speciel nem annyira szimpla, mert pl. két TCP csatornát használ, aktív vagy passzív módban működik, hitelesít, stb. Maga az adatkommunikációja pedig szintén szimpla TCP. Nem csak néztem ftp kódot, hanem írtam is.
-
jattila48
aktív tag
-
jattila48
aktív tag
válasz
ToMmY_hun #3247 üzenetére
Hát, én nem tudom, mi az a ZeroMQ, thrift, meg avro (őszintén szólva nem is nagyon érdekel), de könnyen lehet, hogy a te problémádra ez az ágyúval lövöldözés verébre esete. Nem kell megijedni a TCP socket-ektől, semmilyen külön library nem kell hozzá, simán a BSD (POSIX) socket API-t kell használni, nem bonyolult. Az átküldendő objektumot persze előbb szerializálni kell. Ezt magad is megteheted, azért általában ez sem annyira bonyolult. C++-ban (tudtommal) nincs külön könyvtár a szerializálásra, ezért ha esetleg nem magad csinálod, akkor a google protocol buffer jó lehet. Ez C++ kódot generál, amit a programodba beépíthetsz. Python oldalon nem tudom, hogy van-e protocol buffer. Sajnos nem túl egyszerű telepíteni, és a generált kód sem túl optimális, ezért én a helyedben saját megoldással próbálkoznék.
Az SNMP nem tudom, hogy merült fel, szerintem az tök másra való. -
jattila48
aktív tag
válasz
dabadab #3240 üzenetére
Nem szokás olyan publikus tfv.-t írni, ami már meglévő publikus tfv.-ekkel megvalósítható. Az ilyet nem tfv.-ként kell megírni, hogy növeljék az enkapszulációt. A free fv.-eket kell előnyben részesíteni a tfv.-ekkel, és a friend fv.-ekkel szemben. Talán ez lehet az oka.
-
jattila48
aktív tag
válasz
ToMmY_hun #3229 üzenetére
És ezt az ID-et miért nem az adott konténer példány azonosítójából, és az elem kulcsából generálod (tulajdonképpen (konténer_ID,elem_kulcs) pár valamilyen kódolásban)? Ez egyértelműen azonosítaná az adott elemet, és nem kéne, hogy a kulcs az összes konténerre vonatkozóan egyedi legyen. Ezt a generált ID-et küldhetnéd aztán át a hálózaton, hogy kiválaszd a megfelelő szkriptet. Nem tudom, jól értettem-e.
-
jattila48
aktív tag
válasz
ToMmY_hun #3225 üzenetére
"Szükség lesz egy STL tárolót burkoló osztályra, mert szeretném megoldani hogy az összes konténerben egy kulcs csak egyetlen egyszer szerepelhessen, biztonsági okokból"
Ez miért, és hogyan? Amikor kiválasztod, hogy melyik tároló objektum milyen kulcsú elemét használod, az már egyértelműen azonosítja a kiválasztott elemet az összes konténer tekintetében. Miért kéne, hogy két különböző konténer objektumban ne lehessen azonos kulcsú elem (ha jól értem, ezt szeretnéd elérni)? Lehet, hogy "véletlenül" más tárolót jelölsz ki, mint amit akartál? Nem értem.
"Ezt manuálisan overloadolnom kell saját osztály esetén?"
Nem overloadolni, hanem megvalósítani.
Ú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!
- Olcsó Laptop! Dell Latitude 7280. I5 7300U / 8GB DDR4 / 256GB SSD
- MSI Thin GF63 12VF 15.6" FHD IPS 5-12450H RTX 4060 16GB 512GB NVMe magyar vbill gar
- Apple iPhone 16 Pro Max - Desert Titanium - 256GB 1 ciklus 100% akku! 1 év garancia! Új készülék!
- iPhone 12 Kék 128GB
- SAMSUNG (LS34C500GAUXEN) 34 " GAMER WIDE MONITOR ! AKCIÓ
- ÁRGARANCIA!Épített KomPhone Ryzen 7 7700X 32/64GB RAM RTX 4070Ti Super GAMER PC termékbeszámítással
- Telefon felvásárlás! Samsung Galaxy A15, Samsung Galaxy A25, Samsung Galaxy A35, Samsung Galaxy A55
- Samsung Galaxy S25 Ultra 1TB, Kártyafüggetlen, 1 Év Garanciával
- Lenovo LEGION Pro 5 / Pro 7, Lenovo Yoga Pro gépek (RTX 4060 / 4070 / 4080 / 4090)
- Beszámítás! Apple Watch SE 2024 44mm Cellular okosóra garanciával hibátlan működéssel
Állásajánlatok
Cég: CAMERA-PRO Hungary Kft
Város: Budapest
Cég: PC Trade Systems Kft.
Város: Szeged