- Bemutatkozott a Transcend SSD-inek zászlóshajója
- Sugárhajtómű ihlette a Zalman CPU-hűtőjét, de nem az üzemzaj tekintetében
- Félreértések az FSR 4 és a PlayStation 5 Pro körül
- Nem tetszik a Procon-SP-nek, hogy a Nintendo távolról kivégezheti a Switch 2-t
- Megcélozta az NVIDIA-t a 2 nm-es node-jával a Samsung
- AMD Ryzen 9 / 7 / 5 9***(X) "Zen 5" (AM5)
- Fejhallgató erősítő és DAC topik
- Megfizethető, Pulse szériás Sapphire alaplap DDR5-ös Ryzenek alá
- Milyen billentyűzetet vegyek?
- Milyen TV-t vegyek?
- Bluetooth hangszórók
- DUNE médialejátszók topicja
- ThinkPad (NEM IdeaPad)
- Hisense LCD és LED TV-k
- Hobby elektronika
Új hozzászólás Aktív témák
-
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.
-
jattila48
aktív tag
válasz
ToMmY_hun #3221 üzenetére
Lehet, hogy nekem nehéz a felfogásom, de a magam részéről nem értem mit szeretnél.
"Van néhány egymáshoz nagyon hasonló osztályom, amelyek különböző típusú objektumokat tárolnak."
Tehát ezek az osztályok konténerek? Egy konténeren belül lehet egymáshoz képest különböző típusú objektumokat tárolni, vagy egy konténer egy adott típusú objektumok tárolására alkalmas?
"minden ilyen osztály rendelkezzen előre deklarált metódusokkal, amelyek osztályonként különböző típusú objektummal térnek vissza vagy végeznek rajtuk műveletet"
Mármint az adott metódust (c++ -ban tagfüggvény) tartalmazó osztályonként..., vagy paraméterben kapott objektum típusától függően térnek vissza...?
Ha konténerről van szó, akkor ezt mint template-et akarod a tartalmazott objektumok típusával paraméterezni? Miért akarsz leszármaztatni? A leszármazott osztály valamely objektumára mutató pointert (referenciát) ősosztály típusúként akarod használni?
Egy kicsit konkrétabban, és részletesebben írd le mit szeretnél, akkor talán tudunk segíteni.
-
jattila48
aktív tag
válasz
dobragab #3207 üzenetére
Hát, szintaktikusan biztos hogy hibás. using stílusú typedef-et soha nem használtam (VS2012 le sem fordítja), de nem is értem mit kéne csinálnia. Mi a (void) a swallow előtt? Gondolom különböző típusú argumentumokat írna ki. Kerek zárójelek nincsenek párban, a végén a ... sincs jó helyen (szerintem), szóval ennyi.
-
jattila48
aktív tag
válasz
EQMontoya #3200 üzenetére
Majd kipróbálom. De hogy ezt meg tudja oldani, ahhoz a TU-kon "átlátó" optimalizáció kéne. Ezt tudja a g++? Én azt hittem, hogy az optimalizáció csak TU-n belül működik. Mivel itt a BodyClass esetleges ősosztályaitól függ, hogy hogy optimalizálható a kód, ezért ezt az információt meg kell tartani az object fájlban is, ha TU-k közötti optimalizációban szeretnénk ezt elvégezni. Esetleg pszeudokódot generáló fordítóknál lehetséges ez. Nem tudom, hogy a gcc végez-e ilyen pszeudo kód generálást majd azon optimalizálást, lehetséges. De akkor talán inkább az LLVM. Minden esetre a kérdés érdekes.
-
jattila48
aktív tag
válasz
EQMontoya #3196 üzenetére
A gcc generálja a thunk kódot, a virtuális hívást, és még nem tudom mit. Mint ahogy a VS is, ha nem tud a BodyClass-ról semmit. Bármelyik fordítónak a "legrosszabb" esetre kell készülni (többszörös virtuális öröklés, virtuális tfv.), ha csak a BodyClass forward deklarációját látja, és semmi egyéb segítséget nem kap. Magától nem találhatja ki hogy "mizu" van. Nem tudom, hogy a gcc-nek vannak-e erre kulcsszavai, nem nagyon használom. A VS-ben is csak most találtam rá. Mondjuk member function pointert sem igen használtam, és gyakorlatban a forwarding fv. hívás sem túl nagy overhead, inkább csak elméletileg érdekelt a dolog. A C++-ból azért mindig van mit tanulni.
-
jattila48
aktív tag
válasz
jattila48 #3189 üzenetére
Na megint tanultam valamit. Ezt írtam:
"Jó lenne, ha a forward deklarációban meg lehetne mondani, hogy a BodyClass teljesen közönséges osztály, nem örökölt senkitől (főleg nem többektől) és nincs virtuális tfv.-e (még emiatt is lehet ez az igazítás). Akkor talán nem generálná ezt az ilyen osztályokra amúgy tényleg fölösleges igazító kódot. Ilyet sajnos tudtommal nem lehet a C++-ban."És VS-ben lehet! handle_class.h-ban:
class __single_inheritance BodyClass;__single_inheritance kulcsszó a forward deklarációban a megoldás! Így már igazító kód nélkül fordít, és főleg jól. Ha még azt is meg lehetne mondani, hogy az mfp nem virtuális fv.-re mutató member function pointer, akkor az indirekt címzésű call helyett is lehetne direkt címzésűt fordítani (bár ez nem nagy veszteség).
-
jattila48
aktív tag
válasz
jattila48 #3191 üzenetére
Debuggolással sikerült kideríteni, hogy az igazító kód valóban a pimpl pointert igazítja. Akkor van rá szükség, ha pl. a BodyClass osztály két ösosztálytól örököl publikusan, méghozzá az f tfv.-ét a másodiktól örökli. Mivel a forward deklaráció miatt a fordító nem látja a teljes BodyClass osztály definíciójat, így erre az esetre mindenképp fel kell készülnie. A pimpl az egész objektumra mutat, de ez az első ősosztály részét jelenti az objektumnak. Ezért, ha a második ősosztályból örökölt tfv,-re mutat az mfp, akkor a pimpl-nek is a BodyClass objektum második ősosztály részére kell mutatnia. Ezt végzi ez a bizonyos igazító thunk kód. Ha a teljes BodyClass definíciót látja a fordító, akkor el tudja dönteni, hogy a BodyClass nem örököl többszörösen, ezért nincs szükség a thunk kód generálására. Az első
004116D6 cmp dword ptr ds:[4168B4h],0
sort nem értem, mert az eredméntyét nem befolyásolja, hogy a tfv. virtuális-e vagy sem, az sem hogy örökölt-e vagy sem. De a hamis kimenetelkor lefutó ágból nekem mégis úgy tűnik, hogy valami virtuális dologgal lehet összfüggésben. Ha a tfv. virtuális, akkor a kód végén az indirect címzésű call virtuális hívást megvalósító kódra ugrik. Tehát ezért kellet a call-nak indirekt címzésűnek lenni. Ha látná, hogy a tfv. nem virtuális, akkor elvileg direkt címzéssel is meghívhatná, azonban ekkor sem teszi. -
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 #3184 üzenetére
A "fölösleges" kód akkor keletkezik, ha handle_class.h-ban a BodyClass incomplete type-ként forward deklarálva van. Ha beinkludolom a teljes body_class.h-t, akkor "szép" kódot generál. Ekkor persze nincs értelme a pimpl-nek. Ú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. Mintha a pimpl-et akarná különböző információk alapján igazítani, ahogy a this-t szokta igazítani a thunk kód többszörösen örökölt osztályok esetén. Jó lenne, ha a forward deklarációban meg lehetne mondani, hogy a BodyClass teljesen közönséges osztály, nem örökölt senkitől (főleg nem többektől) és nincs virtuális tfv.-e (még emiatt is lehet ez az igazítás). Akkor talán nem generálná ezt az ilyen osztályokra amúgy tényleg fölösleges igazító kódot. Ilyet sajnos tudtommal nem lehet a C++-ban. Egyébként a kódban valószínűleg csak cím hibás, mégpedig a
004116D6 cmp dword ptr ds:[4168B4h],0
sorban a 4168B4. Amikor rosszul fut, akkor ez a cím unicode sztringekre mutat, vagyis nyilvánvalóan rossz. Amikor jól fut, akkor különböző globális konstansok lehetnek itt és környékén (ha jól fut, akkor 0). Mégsem a const deklarációtól függ, hogy jó-e vagy nem, mert újra buildeléskor const-tal is elromlott. Talán a linker lesz a hibás. Egyébként az LLVM erre a kódra assertion failed-del elszáll, és kéri, hogy a hibajelentést küldjem be. Csak a gcc tudta minden gond nélkül buildelni. -
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
jattila48 #3176 üzenetére
Tehát így már jó:
struct tempfv{
int operator()(){return 42;}
};
template<typename R,typename F> struct Temp
{
int f(){
return F()();
}
};
template <template<typename R,typename F> class T,typename R,typename F> struct TempTemp
{
T<R,F> t;
int f(){return t.f();}
};
TempTemp<Temp,int,tempfv> t; -
jattila48
aktív tag
Egy másik probléma:
Szerintetek ez miért nem fordul le?int tempfv(){
return 42;
}
template<typename R,R (*F)()> struct Temp
{
int f(){
return F();
}
};
template <template<typename R,R (*F)()> class T,typename R,R (*F)()> struct TempTemp
{
T<R,F> t;
int f(){return t.f();}
};
TempTemp<Temp,int,tempfv> t;Ha az F nem függvénypointer hanem functor, akkor lefordul. Úgy tűnik, hogy a template template paraméter paramétere nem lehet függvénypointer. Igaz ez? VS ezt írja ki:
error C3201: the template parameter list for class template 'Temp' does not match the template parameter list for template parameter 'T' -
jattila48
aktív tag
válasz
EQMontoya #3174 üzenetére
Valószínűleg nem ez a legjobb megoldás, úgyhogy le is tettem róla. Ettől a kérdés még elméletileg érdekes. A pimpl marad, viszont az int template paramétert (amihez a static_for kellett volna), megszüntetem. Azt hiszem, a pimpl idióma egyébként sem ajánlható template-ekben (pont az explicit példányosítás szükségessége miatt), de nekem most nagyon jól jön, és nem az implementáció elrejtése vagy a gyorsabb fordítás miatt.
-
jattila48
aktív tag
válasz
EQMontoya #3172 üzenetére
Azért, mert a példányosítandó template pimpl idiómát használ, ezért nem a header-ben vannak a tfv.-ek definíciói, hanem külön cpp fájlban. Egy másik cpp fájl szeretné használni a template egy osztálypéldányát, de a pimpl miatt csak a pimpl-es template-ből deklarál osztályt. A handler osztályt a template definíció helyén (a cpp fájlban) ezért explicit példányosítani kell.
-
jattila48
aktív tag
válasz
EQMontoya #3170 üzenetére
Köszi a választ! Ez lényegében ugyanaz, mint az én megoldásom, és a bajom is ugyanaz vele. A P tagfüggvényeit mind meg kell hívni (p.doSomething()
, ha azt akarjuk, hogy példányosuljanak. Valami olyasmit szeretnék, mint az explicit template példányosítás, de static_for-ban. Az explicit template példányosításkor nem kell meghívni a tfv.-eket, de mégis mind példányosulnak. Jó lenne, ha mondjuk egy sima template példány deklarálásakor ki lehetne kényszeríteni a tfv.-ek példányosulását is. Erre nincs mód?
A smiley ; és ) akart lenni. -
jattila48
aktív tag
Egy int paraméterű template-et szeretnék explicit példányosítani egy bizonyos egész számig az összes ennél kisebb argumentummal. Hogy lehetne ezt egyszerűen megtenni?
template<int N> struct P{
int f();
};Ha pl. 3-ig akarom példányosítani, akkor ne kelljen 3-szor kiírni:
template struct P<0>;
template struct P<1>;
template struct P<2>;hanem rekurzívan valahogy így:
template <int N> struct Instantiate
{
//Instantiate():x(i.p.f()){}
P<N> p;
Instantiate<N-1> i;
int x;
};
template <> struct Instantiate<0>
{
P<0> p;
};
template struct Instantiate<3>;Ezzel az a probléma, hogy elvégzi a template példányásítását a megadott argumentumig minden egész számra, azonban a tagfüggvényeket nem példányosítja, ha csak a Instantiate template ctor-ában meg nem hívom azokat (kikommentezett sor). Ha kiírom a három explicit példányosítást, akkor persze a tagfüggvények is példányosulnak, de így nem. Szóval erre a problémára kellene valami szebb megoldás.
-
jattila48
aktív tag
válasz
ny.erno #3127 üzenetére
Gondolom egy (vagy több) kereskedő szoftverről van szó, amit figyeltetni szeretnél. Pontosabban a tőzsdén zajló eseményeket szeretnéd figyeltetni, ami alapján a kereskedő szoftver is kijelzi az állapotot. Magához a szoftverhez nem fogsz tudni hozzáférni, de igazából nem is ez a cél. A weboldalt kéne figyelned. A kereskedő szoftverek is egy adott protokollon (valami HTTP-be ágyazott) kérik le az adatokat, és jelenítik meg. A figyelő programnak ugyanezt kéne tenni. Tehát ismerni kéne a protokollt, és be kell tudni kapcsolódnia a kereskedő szoftverrel fenntartott hálózati kapcsolatba. Erre egy proxy szerű program lenne megfelelő, ami a tőzsdei web oldallal kiépíti a kapcsolatot (ez általában hitelesítéssel történik), és proxyként viselkedik a kereskedő szoftver felé (a kereskedő szoftvernek nagy valószínűséggel beállítható proxy). Hát kb ennyi. Nem túl egyszerű, de nem is megoldhatatlan feladat. Leginkább a protokoll megismerése a kihívás, bár lehet, hogy ez valamiféle szabvány. Ha nem az, akkor pl. Burp proxyval lehet figyelni, kísérletezni, kitanulni. Lehet, hogy megérné.
-
jattila48
aktív tag
válasz
jattila48 #3116 üzenetére
Hát, ez az algoritmus nem valami jó. Ugye mindig olyan számot hagytam el a sorozatból (pontosabban az első ilyet), amivel a legtöbb még ép mintasorozatot rontom el. Most egy kicsit módosítottam az algoritmuson, és az első ép mintasorozatból hagyom el azt az elemet, ami a legtöbb mintasorozatot rontja el. Ennek megfelelően a módosított forráskód:
#include <Windows.h>
#include <stdio.h>
int main(int argc,char *argv[])
{
if(argc<3){
printf("Hasznalata: mintat_gyomlal <a gyomlalando sorozat hossza> <minta elemek 1... novekvoleg>");
exit(1);
}
int mintahossz=argc-2,sorozathossz=atoi(argv[1]);
auto minta=new int[mintahossz];
for(int i=0;i<mintahossz;++i){
minta[i]=atoi(argv[i+2]);
}
int minta_terjedelem=minta[mintahossz-1];
auto sorozat=new int[sorozathossz];
for(int i=0;i<sorozathossz;++i){
sorozat[i]=0;
}
for(int i=0;i<=sorozathossz-minta_terjedelem;++i){
for(int j=0;j<mintahossz;++j){
++sorozat[minta[j]+i-1]; //a tomb kezdeti feltotlese. az i.-edik elem azt jelzi, hogy az i+1 szam hany mintasorozatban szerepel
}
}
printf("A sorozatbol kihuzando szamok: ");
int nkihuzando=0;
while(1){
int kihuzando,max=0;
/*
//megkeressuk a legtobb mintasorozatban szereplo szamot, ez lesz a kihuzando+1 (a kihuzando indexu, mivel 0-val kezdodik az indexeles)
for(int i=0;i<sorozathossz;++i){
if(sorozat[i]>max){
kihuzando=i;
max=sorozat[i];
}
}
*/
//modositas: az elso ep mintasorozatbol keressuk ki a legtobb mintasorozatot elronto elemet
int i;
for(i=0;i<sorozathossz && sorozat[i]==0;++i);
if(i==sorozathossz)break;
for(int j=0;j<mintahossz;++j){
int n=i+minta[j]-1;
if(sorozat[n]>max){
kihuzando=n;
max=sorozat[n];
}
}
if(max==0)break;
printf("%d ",kihuzando+1);
++nkihuzando;
//A kihuzott szam utan a sorozat tomb elemeit csokkentjuk, hogy tovabbra is azt jelezze, a megmaradt ep mintasorozatok kozul hanyban szerepel az adott szam
for(int i=0;i<mintahossz;++i){
int n=kihuzando-minta[i];
for(int j=0;j<mintahossz;++j){
int k=minta[j]+n;
if(k>=0 && k<sorozathossz && sorozat[k]>0){
--sorozat[k];
}
}
}
}
printf("\r\nOsszesen %d db.",nkihuzando);
delete[] minta;
delete[] sorozat;
}A végén kiírom, hogy hány elemet hagytam el. Valamivel jobb eredményt ad mint az előző, de szerintem ez is távol áll az optimálistól.
-
jattila48
aktív tag
válasz
Boryszka #3095 üzenetére
Egy mohó algoritmust találtam ki a feladatodra, ami azért nem biztos, hogy optimális megoldást ad. Mintasorozatnak fogom nevezni az 1...N sorozatban bárhol megtalálható eltolt mintát. Tehát az pl. az 1,2,3,4,5,6,7,8 sorozatban az 1,3,4 mintának megfelelő mintasorozatok az 1,3,4; 2,4,5; 3,5,6; 4,6,7 és 5,7,8. Az elgondolás az, hogy a sorozatban megkeresem az első mintasorozatot, majd törlöm az egyik elemét. Ezzel elrontom az éppen aktuális mintasorozatot, és esetleg még másikakat is (legfeljebb annyit, amennyi a minta elemszáma, a példában ez 3). Azt az elemet fogom elhagyni, amelyik a legtöbb mintasorozatot rontja el. Ha több ilyen is van, akkor az elsőt veszem. Ezután az eljárást megismétlem. Megint veszem az első ép mintasorozatot (az elrontottak már nem lesznek tekintetbe véve), és megont elhagyom azt az elemét, amely a legtöbb mintasorozatot rontja el. És így tovább, egészen addig, amíg már nem lesz ép mintasorozat. Írtam is erre egy egyszerű kis programot. Felveszek egy sorozat nevű int tömböt, aminek az i. eleme (0-tól kezdődik az indexelés) kezdetben azt jelzi, hogy az i+1 szám hány mintasorozatban szerepel. A példában ez így néz ki:
1,1,2,3,3,2,2,1. A 4-es számnak megfelelő érték (3. indexű elem) 3, ami azt jelenti, hogy a 4 3 db mintasorozatban szerepel (1,3,4; 2,4,5; 4,6,7). Ezért az algoritmusnak megfelelően az első kihúzandó szám a 4 lesz. Az 5 is 3 mintasorozatban szerepel, azonban a 4 előbb van, ezért azt választjuk. Ezzel máris elrontottuk a felsorolt 3 mintasorozatot, a továbbiakban ezeket nem vesszük figyelembe. Most a sorozat tömb elemeit csökkenteni fogjuk, annak megfelelően, hogy az egyes számok a még megmaradt mintasorozatok közül hányban szerepelnek. Ezt az eljárást egészen addig ismételjük, amíg a sorozat tömb minden eleme 0 nem lesz.
A programkód:
#include <Windows.h>
#include <stdio.h>
int main(int argc,char *argv[])
{
if(argc<3){
printf("Hasznalata: mintat_gyomlal <a gyomlalando sorozat hossza> <minta elemek 1... novekvoleg>");
exit(1);
}
int mintahossz=argc-2,sorozathossz=atoi(argv[1]);
auto minta=new int[mintahossz];
for(int i=0;i<mintahossz;++i){
minta[i]=atoi(argv[i+2]);
}
int minta_terjedelem=minta[mintahossz-1];
auto sorozat=new int[sorozathossz];
for(int i=0;i<sorozathossz;++i){
sorozat[i]=0;
}
for(int i=0;i<=sorozathossz-minta_terjedelem;++i){
for(int j=0;j<mintahossz;++j){
++sorozat[minta[j]+i-1]; //a tomb kezdeti feltotlese. az i.-edik elem azt jelzi, hogy az i+1 szam hany mintasorozatban szerepel
}
}
printf("A sorozatbol kihuzando szamok: ");
while(1){
int kihuzando,max=0;
//megkeressuk az elso, legtobb mintasorozatban szereplo szamot, ez lesz a kihuzando+1 (a kihuzando indexu, mivel 0-val kezdodik az indexeles)
for(int i=0;i<sorozathossz;++i){
if(sorozat[i]>max){
kihuzando=i;
max=sorozat[i];
}
}
if(max==0)break;
printf("%d ",kihuzando+1);
//A kihuzott szam utan a sorozat tomb elemeit csokkentjuk, hogy tovabbra is azt jelezze, a megmaradt ep mintasorozzatok kozul hanyban szerepel az adott szam
for(int i=0;i<mintahossz;++i){
int n=kihuzando-minta[i];
for(int j=0;j<mintahossz;++j){
int k=minta[j]+n;
if(k>=0 && sorozat[k]>0){
--sorozat[k];
}
}
}
}
delete[] minta;
delete[] sorozat;
}Kissé off topic voltam, de ha már itt tetted fel a kérdést, itt válaszoltam. Nem vagyok biztos benne, hogy optimális megoldást ad az algoritmus, de biztosan mintamenteset, és szerintem közel optimálisat. Bár a mohó algoritmusok nem mindig jók. A program egyébként a példára azt fogja kiírni, hogy a 4-et és 5-öt hagyd el, ami jó és optimális is (legalább 2 elemet el kell hagyni). Mivel kezdetben N-mintaterjedelem+1 (mintaterjedelem a legnagyobb, vagyis utolsó mintaelem) mintasorozat van és egy elem kihúzásával legfeljebb nm (minta elemszáma) mintasorozatot rontunk el, ezért legalább (N-mintaterjedelem+1 )/nm felső egész része számú elem kihúzására van szükség. A példában ez 5/3=2, tehát legalább 2 elemet ki kell húzni.
-
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
EQMontoya #3080 üzenetére
Azt hiszem, te ragaszkodsz egy koncepcióhoz, miszerint a különálló funkciókat külön osztályokban kell megvalósítani. Ez nálam is így van. Azonban létre kell hozni egy (és nem több) objektumot, ami ezeket a funkciókat egyben látja el. Tehát itt különbség van a funkciók implementálása (külön osztályokban), és azok használata között. A külön implementált funkciókat (a már említett okok miatt) egyben kell felhasználni. Erre én a származtatást használom, te pedig az egyébként máshol nem használt funkciókat unique_ptr-en keresztül "beinjektálod" a használt objektumba (ami innentől kezdve egyedül létezik, és szintén minden szükséges (egyébként külön implementált) funkciót ellát). Nálam egyik osztály sem tud önállóan létezni, csak a származtatás által összekapcsoltan, ahogy ez is a cél. Nálad a SourceReadFile ogjektum önállóan is tud létezni (sőt példányosítanod is kell), holott ennek semmi értelme, mert úgy is csak a másik osztályban tudod használni.
-
jattila48
aktív tag
válasz
EQMontoya #3080 üzenetére
Persze elég. Amit most példának írtam, abban is így van. Nem egészen erre gondoltam, rosszul fogalmaztam. Ettől függetlenül a fenit megállapításaimat fent tartom. Bonyolultabb, több hibalehetőséget magában rejtő megoldás, mint az enyém.
"Az a bajom az egybe pakolt megoldással, amiért nem is akar sézpen működni: teljesen különböző funkciókat szeretnénk bepakolni egy logikai csoportba."
A teljesen különböző funkciókat teljesen külön is választom: a blob feldolgozás az ősosztályba, a forrás kezelés/beolvasás a leszármazott osztályba. Persze amikor már példányosítom a leszármazott osztályt, akkor ezek a funkciók egy objektumban működnek, mint ahogy ez is a cél. (ld, ugyanaz a hatókör, a blob-ot vissza kell tudni írni, egységes RAII). A te megoldásodban sem fog külön önállóan létezni a SourceReadFile objektum (legalábbis nem sokáig, mert bele teszed egy unique_ptr-be), hanem rögtön "beinjektálod" a BlobProcess objektumba. Innentől kezdve, pedig ugyanúgy a BlobProcess objektum fogja ellátni az összes feladatot (blob feldolgozás, forrásba visszaírás). A SourceReadFile objektum önálló létének nincs is értelme, sőt a legjobb, ha minél előbb megszabadulunk tőle (RAII miatt), ezért legjobb, ha önállóan létre sem hozzuk. Persze a tényleg szükséges objektum leszármazott részeként marad.
Egyébként az "egybepakolt" megoldásom szépen működik. Miből gondolod, hogy nem akar? A te megoldásodat így gondoltad, ahogy a példában írtam? -
jattila48
aktív tag
válasz
EQMontoya #3078 üzenetére
Más a fájlból olvasás paraméterezése, más a registry olvasásé, más a resource olvasásé, más a hálózatról olvasásé, stb. De még ha lehetne is egységes read-et írni, akkor is nálad 3 osztály kell hozzá, amiből kettőt példányosítasz is, majd az elsőt "beinjektálod" shared_ptr-en keresztül a másodikba (hogy rendesen működjön a RAII), és tovább erre az objektumra a létrehozás helyén nincs is szükséged. Továbbá BlobProcess ctor-ában ugyanúgy meg KELL hívnod egy "külső" fv.-t, mint nálam az ősosztály init_blob-ját. Ráadásul nálam ez nem is annyira külső fv., hiszen az ős protected tfv.-e. És mivel a forrás, a blob számára lefoglalt memória és maga a BlobProcess objektum teljesen összetartozóak (ugyanakkor keletkeznek, ugyanaz az érvényességi körük, ugyanakkor kell megszűnniük), ezért tök fölösleges azon görcsölni (legalábbis szerintem), hogy szétválasszuk őket, aztán valahogy mégis csak egybe gyúrjuk (shared_ptr-rel). Erre sokkal természetesebb az én megoldásom. Azt hiszem ez is egy olyan eset, amikor nem biztos hogy vakon kell követni a "kőbe vésett szabályokat" (osztályt a ctor-a teljesen kell hogy inicializálni tudjon), persze érdemes tudni ezekről, de még fontosabb hogy az okát/értelmét ismerjük, és ha az adott feladatban ennek nem felel meg, akkor el lehet tekinteni tőle. Szerintem!
-
jattila48
aktív tag
válasz
jattila48 #3076 üzenetére
Végül is ez is a BlobProcess kétfázisú inicializálása, mert a ctor-ában meg KELL hívni az sp->read() fv.-t a blob_ptr és blob_length tagok inicializálására. Ez persze nem baj, hiszen a tagokat a ctor-ban inicializálni kell. Logikailag azonban nem különbözik az én megoldásomtól, ahol az ős osztályt (és itt tekintheted tagnak, mivel nem példányosítható) szintén egy fv. hívással KELLETT inicializálni.
-
jattila48
aktív tag
válasz
EQMontoya #3075 üzenetére
Végül is tudná. Ahogy említettem, nem tudok minden forrásra egységes virtuális read_blob-ot írni. Egyébként erre gondoltál?
Az utolsó néhány sor így módosul:
class BlobProcess{
public:
BlobProcess(std::shared_ptr<SourceRead> sp):source_read_shared_ptr(sp){sp->read();process_blob(sp->get_blob_ptr(),sp->get_blob_length);}
private:
void process_blob(void *,DWORD);
std::shared_ptr<SourceRead> source_read_shared_ptr;
};
void main(){
auto source_read_file=std::make_shared<SourceReadFile>(new SourceReadFile("blob_file"));
BlobProcess blob_process(source_read_file);
} -
jattila48
aktív tag
válasz
EQMontoya #3073 üzenetére
"Amivel olvasol (legyen source), az legyen egy külön osztály, valami interface-szel. Ebből lehet leszarmaztatni sok félét"
Ezzel ugyan az a gond, mint az én megoldásomban. Az interface hogyan kapja meg a forrástól a blob-ot? A leszármazott osztály ctor-ának ugyanúgy meg kell hívni az intarface egy nem virtuális init_blob tfv.-ét, amivel ezt megteszi. Az interface-nek nem lehet a blob beolvasásra virtuális tfv.-e, mert a források különböző paraméterezést igényelnének erre (másrészt az interface ctor-ából nem szokás virtuális tfv.-t hívni). De ha lehetne is virtuális tfv.-e a forrás beolvasására, akkor is előbb létre kéne hozni az objektumot, majd meghívni rá a virtuális read-et (kétfázisú inicializálás). Gondolom az interface itt sem lenne példányosítható (hiszen ezért interface).
Valahogy így gondoltad?class SourceRead{
public:
SourceRead():blob_ptr(0),blob_length(0){}
bool read(){
return read_blob(&blob_ptr,&blob_length);
}
virtual ~SourceRead()=0{}
void * get_blob_ptr(){return blob_ptr;}
DWORD get_blob_length(){return blob_length;}
private:
virtual bool read_blob(void **blob_ptr,DWORD *blob_length)=0;
void *blob_ptr;
DWORD blob_length;
};
class SourceReadFile : public SourceRead{
public:
SourceReadFile(const std::string & source_name):source_name(source_name){}
~SourceReadFile(){/*megnyitott fajl lezarasa, allokalt memoria felszabaditasa*/}
private:
virtual bool read_blob(void **blob_ptr,DWORD *blob_length){
//source_name forras megnyitasa,beolvasando meret meghatarozasa, *blob_ptr=allokalt memoria cime,blob_length=merete
return true;
}
const std::string & source_name;
};
class BlobProcess{
public:
BlobProcess(std::shared_ptr<SourceRead> sp):source_read_shared_ptr(sp){process_blob(sp->get_blob_ptr(),sp->get_blob_length);}
private:
void process_blob(void *,DWORD);
std::shared_ptr<SourceRead> source_read_shared_ptr;
};
void main(){
auto source_read_file=std::make_shared<SourceReadFile>(new SourceReadFile("blob_file"));
source_read_file->read(); //ketfazisu inicializalas
BlobProcess blob_process(source_read_file);
}És itt már nem 2, hanem 3 osztály van, meg shared_ptr (tényleg! miért nem unique_ptr), amit nagyon nem szívesen használok. Ezzel szembem nálam 2 osztály van, amiből csak 1-et kell példányosítani, és nincs shared_ptr (sem_unique_ptr).
-
jattila48
aktív tag
-
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
jattila48 #3064 üzenetére
Arra minden esetre jó volt ez az eszmefuttatás, hogy legalább magamat sikerült meggyőzni (remélem titeket is. Vagy nem?) a megoldásom helyességéről. Vagyis ha egy osztályból csak származtatni lehet és őt magát nem lehet példányosítani, akkor nem koncepcionális hiba az osztály kétfázisú inicializálása protected init tfv.-nyel. Sőt úgy gondolom, hogy az ilyen osztályból származtatott osztály teljes értékű RAII kezelése (a ctor. foglalja le a példány "létezéséhez"/működéséhez szükséges erőforrásokat, a dtor szabadítja fel azokat) adott esetben (ha az ősosztálynak szüksége van az erőforrásokra) nem is valósítható meg másként. A free/static fv.-ek alkalmazása szükség szerűen megsérti a RAII koncepciót, hiszen már az objektum létrejötte előtt le kell foglalnia az erőforrásokat. Ezen az sem segít (sőt...), ha a felszabadítást már a dtor végzi.
-
jattila48
aktív tag
válasz
dobragab #3062 üzenetére
Ezzel csak az a bajom, hogy ez a megoldás lényegében nem különbözik attól, hogy "kívül" megnyitod a fájlt, memória területet foglalsz, oda beolvasod a fájlt, majd ezt a memória területet átadod a D ctor.-ának. Tehát a fájl megnyitás, és a memória terület foglalás nem a D ctor-ában történik, viszont az erőforrás felszabadítást (legalábbis a fájl lezárást) a dtor-ban akarod megoldani. Ez egy felemás RAII lenne, ami szerintem koncepcionálisan hibás. Az, hogy ezt az egészet bele tetted egy statikus factory fv.-be, a lényegen nem változtat. Sőt az allokált memória terület már a factory fv. lefutásával felszabadulhat (bár itt mivel const & hivatkozás van rá, valószínűleg kitart annak élettartamáig. Egyébként a const & nekem nem lesz jó, mert változni fog a memória terület, amit az objektum "elhalásakor" vissza szeretnék írni a fájlba. nem const referencia pedig tudtommal nem tartja életben a temporális objektumot. Lehet hogy tévedek!). Ha közvetlenül a D ctor-ával hozod létre az objektumot, akkor a helyzet ugyanez. Tehát a statikus factory fv. használata semmit nem tesz jobbá, a zárójeles megjegyzésem értelmében még esetleg rosszabb is lehet.
Ellenben az én megoldásommal a D ctor-ába lehet tenni mind a fájl, mind pedig a blob allokálását, és ezáltal ezeket az erőforrásokat RAII módon lehet kezelni, ahogy "kell". Ennek az ára a B kétfázisú inicializálása, ami azért nem hibás koncepcionálisan, mert sem félig, sem teljesen konstruált B objektumot nem lehet létrehozni. Mindössze a D konstruálása során van olyan pont, amikor a D B része félig konstruált, de ekkor a D még kész sincs. A protected init fv.-t kell meghívni kötelezően a D ctor.-ában, ennek elmulasztása azonban nem különbözik attól, mintha "elfelejtenénk" D valamelyik tagját inicializálni (végül is az ős osztály is egy "tagnak tekinthető"). A tagok inicializálása pedig nem csak inicializációs listában, hanem a ctor törzsében is megtörténhet, pont akkor, ha az inicalizáláshoz számításokat kell végezni. A lényeg, hogy ezzel a módszerrel D-ből csak teljesen konstruált objektumot lehet létrehozni, B-ből pedig semilyet. -
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. -
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.
-
jattila48
aktív tag
A kétfázisú inicializálást általában nem kívánatosnak (esetleg tervezési hibának) tartják. De mi a helyzet olyan bázis osztály (B) esetén, amit nem lehet példányosítani (pl. pure virtual tfv. miatt), hanem csak leszármaztatni lehet belőle? A B-nek van egy protected nem virtuális init tfv.-e, ami elvégzi a B teljes inicializálását. A leszármaztatott D osztály konstruktorából ezt az init-et kell meghívni az éppen most kiszámított argumentumokkal. Sajnos az init meghívását nem lehet kikényszeríteni. Szerintetek elfogadható-e a B kétfázisú inicializálása (szerintem igen), vagy valami elegánsabb (idiomatikusabb) megoldást kéne alkalmazni?
-
jattila48
aktív tag
válasz
jattila48 #3025 üzenetére
Kissé off topic, de azért érdekes:
A legkisebb dobás meghatározásakor EQMontoya kifogásolta, hogy fölöslegesen vezettem be a legkisebb_dobas változót, mert tömb indexeléssel is megoldható lett volna. Ez igaz, azonban ahogy írtam, valószínűleg hatékonyabb így, mert a feltétel kiértékelésben megspórolom az egyébként költséges tömb indexelést, ezzel szemben az if törzsben "bevásárolok" egy értékadást. Ha az értékadások össz költsége kisebb mint a tömbindexelések össz költsége, akkor ez megéri (itt most figyelmen kívül hagyom az egyetlen tömb indexelés, és értékadás közötti költségek viszonyát). Mivel a feltétel jóval ritkábban értékelődik igazra, ezért az értékadás is jóval ritkábban lesz végrehajtva. Nos, kiszámoltam, hogy mennyi is az annyi. A konkrét feladatban [0,10] intervallumból generáltunk egyenletes eloszlással egy 10 elemű tömböt. Ekkor nyilván a generált sorozatok eloszlása is egyenletes. Kiszámoltam, hogy a konkrét feladatban mi a várható értéke a feltétel igazra értékelődésének, és 2,55 adódott, bele számolva a legkisebb_dobas kezdő értékadását is. A tömb indexelős megoldással ezzel szemben 10 indexelés adódik. Tehát tényleg hatékonyabb az értékadós megoldás. -
jattila48
aktív tag
-
jattila48
aktív tag
válasz
EQMontoya #3024 üzenetére
Részben igazad van, viszont az if feltételben megspóroltam egy tömb indexelést. Talán ez a megoldás hatékonyabb, és jobban átlátható. A gi-t sem kellett volna bevezetni, de ugyanezért lett az is. Nem tartozik ide, de szoktál assembly kódot debuggolni? Ott látszik, hogy egy tömb indexelés költségesebb mint egy sima változó elérés. Az értékadás pedig csak az if törzsében történik meg, ami feltehetően jóval ritkábban fut le, mint a feltétel kiértékelése.
-
jattila48
aktív tag
válasz
EQMontoya #3022 üzenetére
Nem szoktatom én sehogy, de hát a GURIT és TAR neveket nem én adtam, pedig szerintem magyarul vannak. Egyébként fogd fel úgy, hogy komment helyett adtam ilyen neveket. Vagy magyarul kommentezni is főbenjáró bűn?
"jobb helyen hirtelen felindulásból leggépuskáznak érte!" Ilyen helyekre én nem járok."A legkisebb dobás és a sorszámának külön eltárolása meg elég béna, ha egy egyszerű tömbindexeléssel elérhető hozzá az adat."
Ezt nem értem. Elmagyaráznád? -
jattila48
aktív tag
válasz
Mikeboy666 #3019 üzenetére
srand(time(NULL)); // véletlen generátor inicializálása
int GURIT[10], TAR[11] = { 0 }, i; // A TAR[] -ban számoljuk, hogy az egyes dobásokból mennyi volt
for (i = 0; i<10; i++) //ellenőrzött dobás bekérés
{
GURIT[i] = rand() % 11 ;
}
int dbparos = 0, guritasok_osszege=0,legkisebb_dobas=GURIT[0],legkisebb_dobas_sorszama=0;
//kezdetben feltetelezed, hogy az elso guritas a legkisebb
for (i = 0; i<10; i++) // eredmények megszámlálása
{
int gi=GURIT[i]; //segedvaltozo, hogy ne kellje mindig kiindexelni az i. guritast (hatekonyabb)
++TAR[gi];
if (gi % 2 == 0)dbparos++;
guritasok_osszege+=gi;
if(gi<legkisebb_dobas){
//a most vizsgalt guritas kisebb, mint az eddig megtalalt legkisebb, most mar ez lesz a legkisebb
legkisebb_dobas=gi;
legkisebb_dobas_sorszama=i;
}
}
for (i = 0; i<11; i++) // eredmények kiíratása
cout << "\nA(z) " << i << "-es dobasok szama:\t" << TAR[i];
cout << endl;
cout << "A paros dobasok szama:\t" << dbparos;
cout << endl;
cout << "A dobasok osszege:\t" << guritasok_osszege;
cout << endl;
cout << "Az (elso) legkisebb dobas erteke:\t" << legkisebb_dobas << "sorszama: " << legkisebb_dobas_sorszama+1;
cout << endl; -
jattila48
aktív tag
válasz
EQMontoya #3011 üzenetére
Nekem úgy tűnt, hogy a kérdező erre a konkrét feladatra volt kíváncsi, nem pedig az STL rejtelmeire. Szerintem ez számára nem könnyíti meg a dolgot. Annál is inkább, hogy alapvető dolgokkal van problémája (ld. case szerkezet), inkább ezekben kéne segíteni neki. Itt többen, többször elkövetitek azt a hibát (akinek nem inge ne vegye magára), hogy nem a kérdésre válaszoltok, hanem egyfajta összekacsintós bennfentes módon csillogtatjátok a magas szintű tudásotokat, ami a kérdezőnek ugyan nem segít, viszont fellengzőssé és lekezelővé teszi a hozzászólásotokat.
-
jattila48
aktív tag
válasz
EQMontoya #3008 üzenetére
"Erre való a map"
Hogyne! Mindjárt ágyúval verébre. Így lehet egy nagyon egyszerű, jó esetben pár száz byte-ra leforduló programból több megás lomha bloatware-t csinálni. Sajnos manapság tényleg ez a tendencia, és ez nem kis részben a "mindenre jó" template könyvtáraknak köszönhetö. Semmi funkcionalitású pimfli kis programok akkorák, hogy nem is olyan régen egy op. rendszer kisebb volt. Ide pl. nagyon nem kell a map a maga red-black tree megvalósításával, erősen túllövése a dolognak. -
jattila48
aktív tag
Ok, ebben nem értünk egyet. Nyilván elsősorban a programom logikájára figyelek, de tőlem abszolút nem igényel semmiféle "görcsölést", hogy alapból a pre op-ot használjam. Persze mások vagyunk, lehet hogy neked ez fölöslegesen elvonja a figyelmed az alkotástól. Mindenesetre szerintem általában javasolható a pre op használata.
Egyébként viszonylag ritkán van szükség kifejezetten a post op alkalmazására. Én leginkább konténer kiürítésénél használtam. -
jattila48
aktív tag
Én csak azt nem értem, hogy szerinted miért olyan nagy fáradság figyelni rá, mi nem ér annyit? Mennyit? Ha tudod, hogy a pre verziót is lehetne használni, akkor "véletlenül" nem fogod a post-ot. Vagy annyira erős koncentrációt igényel tőled, hogy erre figyelj, ezért random hol ezt, hol azt használod? Ha meg tudod, hogy elvileg mindegy melyiket használod, akkor "csak azért is" néha a post-ot?
"Ha code review-n megjelenik okoska, hogy hé, véletlenül másikat használtad, 5 billentyű leütés kijavítani."
Ha kijavítani csak ennyi, akkor "jól" írni még ennyi sem. -
jattila48
aktív tag
"Így most azt mondod, hogy a fordító egy függvényhívást lecserélhet egy másik függvény hívására, ha bizonyos feltételek teljesülnek"
Végül is igen. Persze csak akkor, ha post verzió a "szokásos módon" mellékhatások nélkül van megvalósítva.
"Ha másik fordítási egységben van definiálva, akkor teljesen más dolgot csinálhat a kettő, és erről a fordító SEMMIT nem tud."
Hát ez valóban probléma! Igazából úgy lehetne ezt elképzelni, ha megadható lenne, hogy a fordító automatikusan generáljon post verziót a pre verzióból. Ilyen persze tényleg nincs.
-
jattila48
aktív tag
Az általam írt optimalizáció megvalósításában valószínűleg annak eldöntése lehet a legnehezebb (esetleg lehetetlen), hogy van-e a post verziónak mellékhatása. A szokásos megvalósításban ez a mellékhatás csak a copy ctor-ban lehet. Elképzelhető, hogy a szabvány nem engedi meg a mellékhatás kioptimalizálását (ellentétben az RVO-val, ahol a copy ctor mellékhatását engedi figyelmen kívül hagyni). Ez esetben tényleg nem ez a járható út. Ha viszont a post verziónak van mellékhatása, akkor a programozónak főleg foglalkoznia kell azzal, hogy melyiket használja.
-
jattila48
aktív tag
Ha már a Google-nál dolgozol, nyilván ismered:
Google C++ Style Guide
Preincrement and Predecrement
Use prefix form (++i) of the increment and decrement operators with iterators and other template objects.
When a variable is incremented (++i or i++) or decremented (--i or i--) and the value of the expression is not used, one must decide whether to preincrement (decrement) or postincrement (decrement).
When the return value is ignored, the "pre" form (++i) is never less efficient than the "post" form (i++), and is often more efficient. This is because post-increment (or decrement) requires a copy of i to be made, which is the value of the expression. If i is an iterator or other non-scalar type, copying i could be expensive. Since the two types of increment behave the same when the value is ignored, why not just always pre-increment?
The tradition developed, in C, of using post-increment when the expression value is not used, especially in for loops. Some find post-increment easier to read, since the "subject" (i) precedes the "verb" (++), just like in English.
For simple scalar (non-object) values there is no reason to prefer one form and we allow either. For iterators and other template types, use pre-increment.
Úgy látszik, hogy a céged mégiscsak elvárja, hogy foglalkozz ezzel.
-
jattila48
aktív tag
Nem feltétlenül iterátorra gondoltam. Más bonyolultabb objektumnak is lehet inkrementáló operátora, amit esetleg tényleg nem lehet inline-olni (más kérdés, hogy van-e értelme). Az RVO-t csak arra írtam, hogy ha azt a fordító (és nem külön menetben az optimalizáló) végzi, akkor akár ezt is elvégezheti, amit írtam, Egyáltalán nem biztos hogy megcsinálja, de számomra ez még egyszerűbb feladatnak tűnik mint az RVO. Persze az igaz, hogy csak akkor, ha a ctor, dtor-nak nincs mellékhatása.
"Ahhoz, hogy úgy működjön a fordító, hogy detektálja, hogy most ki lehet cserélni a pre-t post-ra, úgy, hogy minden esetben működjön, ahogy te leírod, nagyon okosnak kell lennie"
Ahol nem használja a post visszatérő értékét ott kicseréli, máshol meg nem. Vagy ez nem ilyen egyszerű?
Akárhogy is van, azzal hogy "akkor ne foglalkozz vele, mert nem ér annyit", nem értek egyet. Szerintem ez nem különösebben teszi próbára a programozót, hogy adott esetben el tudja dönteni, tényleg szüksége van-e a post verzióra, vagy elég a pre is. Vagyis igenis foglalkozzon vele , mert megér annyit. Szerintem ezt hogy a post lassabb lehet, illik tudni egy programozónak. Kód review-ban én szóvá tenném, míg azt a valóban szerencsétlen bool értékadást nem.
Más, nem csak neked címezve:
Makrók és template metaprogramming használatával olyan programot szeretnék írni, amit újra fordítva, az előzőleg fordítottal funkcionálisan megegyező, azonban bináris kódjában erősen különböző kódot kapok. Ez egy kicsit bonyolult mondat lett, tehát a lényeg: makrókkal és TMP-vel bináris kód obfuszkáció. Hatékonyság nem lényeg, lehet bele (sőt...) redundáns kódot injektálni, hadd legyen minél nehezebben követhető a kód. Olyanokra gondoltam mint értékadás, függvényhívás, bool kifejezések bonyolítása, loop unrolling, stb. Ha van ezzel kapcsolatban ötletetek, ne tartsátok magatokban. Ja, és ne akarjatok lebeszélni róla, hogy ugyan minek ez nekem. Köszi előre is.
-
jattila48
aktív tag
Ez még csak OK lenne, ha inlineolható az operátor és a copy ctor. De mi van, ha fv. hívásokként kell befordítani? Akkor ez már nem működik. Viszont az könnyen eldönthető, hogy valójában nincs is szükség a post inkrement verzióra, ezért egyszerűen a pre verziót fordítja be. Ez is egyfajta optimalizáció. Természetesen, ha post verziónak mellékhatása van (akár a copy ctor miatt), akkor a fordító úgy ítéli meg, hogy igenis szükség van rá, ezért valóban a post verziót fordítja be. Ez akkor is működik, ha a post verzió nem inlineolható, míg amit te írtál, az kénytelen lesz az egyébként szükségtelen post verziót használni, ha máshol pedig okkal használják a post verziót. Nem tudom, hogy így vagy úgy van-e, ki kéne próbálni. Valahogy biztos meg lehet tiltani az inline-olást, abból kiderülne. Azt írod, hogy a "generált kódra rámozdul az optimalizáló", ami egész biztos, hogy jórészt így is van. Azonban pl. pont az RVO-nál gondolom már a fordító generál optimalizált (akár köztes) kódot, amivel a köztes kódon operáló optimalizációnak már nem lesz dolga (legalábbis nem az RVO tekintetében). Most úgy gondolom (nem biztos hogy így van), hogy az RVO-t nem is lehet másként, mint elve a fordítóra bízni (nem pedig a külön menetes optimalizálóra), akkor pedig miért ne végezhetné el az általam írt optimalizációt is maga a fordító?
-
jattila48
aktív tag
De hát pont arról volt szó, hogy nem változtatja meg ezzel program működését, hiszen nem használja az operátor visszatérő értékét, így nyugodtan használható a pre inkrement verzió. Igen csodálkoznék, ha úgy lenne, ahogy írod, mert akkor más fordítási modulban, ahol szükségszerű a post inkrement verzió ott mit csinál? A kód optimalizálók általában nem globálisan, hanem csak fordítási egységenként működnek. Vagy ha másik modulban post inkrement kell, akkor oda is azt fordít, ahova nem kéne, de azt használják? Ez számomra nagyon nem tűnik logikusnak, és jóval egyszerűbb így megvalósítani, ahogy írtam. Némileg magadnak is ellent mondasz ("annyira nem eszik forrón a kását"), mert ha így lenne ahogy írod, akkor igenis figyelni kellene, hogy tényleg csak a szükséges helyeken használjuk a post inkrement verziót ("Szóval
nemérdemes ebbe energiát befektetni").
BTW elárulnád, hogy mi a baj azzal, ha a post inkrement op-ot pre inkrementtel valósítom meg? -
jattila48
aktív tag
"Ha nem használod a visszatérési értéket (mint pl. a for ciklusban), akkor erősen valószínű, hogy optimalizálás után pontosan ugyanazt a kódot generálja akkor is, ha saját operátorokat definiálsz"
Úgy tűnik, hogy ilyenkor egyszerűen a pre inkrement verziót fordítja be akkor is, ha post inkrementet használsz. Ettől persze még igaz, hogy maga a post inkrement verzió kb. egy copy ctor-ral költségesebb.
-
jattila48
aktív tag
válasz
MasterMark #2955 üzenetére
Nincs ezzel baj. Itt nem lehet a break feltételt betenni a ciklusfeltételbe, mert break esetén kiírsz.
-
jattila48
aktív tag
Elképzelhető, hogy így van. Természetesen mindig a logikának megfelelőt kell használni, de magam részéről ha logikailag mindegy, mindig a pre inkrement verziót használom. Nem POD-ra gondoltam, ezt rosszul írtam, hanem csak az alaptípusokra. Ha nincs optimalizálás, akkor szerintem kb. úgy van, ahogy írtam. Legalábbis én így írtam post inkrement operátort, de valószínűleg nem is lehet ettől nagyon különböző módon.
-
jattila48
aktív tag
válasz
dobragab #2952 üzenetére
Hát, ez lényegében ugyanúgy egy lineáris keresés, de nekem mindegy! Megkeresed a vége előtt az első nem beolvasható elemet, és közben csinálsz valamit. A lényeg nálam is ez volt: Adott hosszúságú adathalmaz (tömb) elemein sorban haladva egy bizonyos feltétel bekövetkeztéig csinálok valamit. Ha a feltétel bekövetkezik, abbahagyom a feldolgozást (break). Ez a for-break legkézenfekvőbb alkalmazása. Végül is mi volt a baj a lineáris kereséssel?
-
jattila48
aktív tag
válasz
dobragab #2948 üzenetére
Félreértetted (vagy félre akartad érteni). Egyáltalán nem utasítom el funkcionális dekompozíciót, természetesen én is használok fv.-eket. Én a kérdésre válaszoltam, és nem jutott jobb példa eszembe, mint a lineáris keresés (és őszintén szólva most sem. Mondj egy példát, ahol a for ciklus break kombo helyénvaló, és az nem a lineáris keresés!). Magam részéről így csinálnám meg, de lehetőleg break nélkül a ciklusfeltételbe építve. Van azonban olyan eset, amikor nem lehet a ciklusfeltételbe beépíteni, ekkor használható a break (fv. hívás helyett is). Egyébként mint kiderült, a kérdező is a lineáris keresésre gondolt. Egy kezdőnek meg azzal sem kell tömni az agyát, hogy mindenáron túl általános sablonos c++ megoldást válasszon, és a hatékonyságot is illik szem előtt tartani. Erről írt Linus is.
-
jattila48
aktív tag
válasz
dobragab #2945 üzenetére
A kérdező azt kérdezte, hogy for ciklusban OK-e a break.A válasz: igen. Lehet, hogy neked maga a for ciklus (főleg break-kel) már pattintott C korszak, de azért kíváncsi lennék, hogy írnál meg egy find and replace-t szövegbufferben amúgy elegánsan, modern C++ stílusban. Milyen konténerben tárolnád a szöveget, milyen standard fv.-eket használnál, és hogy kerülnéd el a for ciklusokat (break-kel együtt)? Egyébként biztos ismered Linus Torvalds véleményét a C++ nyelvről. Magam részéről ezzel persze jórészt nem értek egyet, de az általa "substandard programmers"-nek nevezett tagokról hasonló a véleményem. Ja, akkor a linux szerinted pattintott C korszaki, ugye?
-
jattila48
aktív tag
válasz
dobragab #2941 üzenetére
Egy kicsit vegyél vissza az arcodból! "Szerintem itt a return a ciklus közepéből semmivel sem jobb mint a break (egyik sem struktúrált megoldás)" idézted tőlem. Az itt szócska talán elkerülte a figyelmedet. Én azt írtam, hogy van ahol jó a break (persze nem mindenhol), te meg azt mondod, hogy mindig van jobb megoldás. A példában jobbnak tartom, mint ezt a függvényhívás megoldást. Jelen esetben a fv. hívás maga túlságosan költséges az elvégzett feladathoz képest. Nem kell ide keverni olyan nyilvánvaló eseteket, ahol nincs létjogosultsága a break-nek, mert nem erről volt szó. Kezdünk elkanyarodni az eredeti kérdéstől. Fölösleges lenne most itt RAII-vel meg mélyebb C++ és OOP koncepciókkal dobálódzni. Nem ez volt a kérdés!
-
jattila48
aktív tag
válasz
dobragab #2936 üzenetére
Szerintem itt a return a ciklus közepéből semmivel sem jobb mint a break (egyik sem struktúrált megoldás), ráadásul beépítesz egy fölösleges függvény hívást. És, ha nem kell végigmenni, hanem csak az első két előfordulást kell megtalálni? Akárhogy csűrjük, csavarjuk, szerintem van ahol kifejezetten jó megoldás a break for ciklusból. Aztán persze van ahol, van jobb megoldás.
-
jattila48
aktív tag
válasz
MasterMark #2934 üzenetére
Jó a break for ciklusban. A lineáris keresést csak példaként írtam. Ezek szerint neked az kell. Esetleg, ahogy írtam, a break feltételét (negálva) beépítheted a ciklusfeltételbe, és akkor nincs break.
-
jattila48
aktív tag
válasz
EQMontoya #2930 üzenetére
Nem azt írtam, hogy lassú, hanem hogy lassabb. Pl. egy szövegszerkesztőnél ez lényeges szempont. Szövegszerkesztőt (marmint a szövegbuffert) nem írnék konténerekkel, iterátorokkal, standard és lambda függvényekkel (pl. szövegben egy sima find and replace az összes előfordulásra, hogy nézne ki nem "pattintott C" stílusban? Belegondolni is rossz). Szerintem szép számmal vannak olyan esetek, ahol a break nemhogy nem felesleges, hanem kifejezetten jobb megoldás, mint az elkerülésére tett C++ bűvészkedés. Ha lehet, én is elkerülöm, pl. a ciklusfeltételbe való beépítéssel.
-
jattila48
aktív tag
Na, erre mondom, hogy "túldizájnolás". A tomb maris egy konténer, aminek van iterátora. Az iterátorokat át kell adni a find függvénynek. Mit csinálsz, ha a tomb-ben a következő megfelelő elemet is meg kell találnod (elem=find(++elem, end(tomb), feltetel))? A példát arra írtam, hogy vannak olyan egyszerű esetek, amiket egyszerűen kell megoldani, és akkor nyugodtan használható a break. Ha egy sima karaktertömbben kell elvégezni ezt a feladatot, akkor a konténerek, iterátorok, és standard függvények használata, biztos, hogy lassabb (még inlineolva is) és nagyobb kódot eredményez.
-
jattila48
aktív tag
válasz
ToMmY_hun #2926 üzenetére
Itt teljesen mindegy, hogy pre vagy post inkrementálást használsz. Alaptípusoknál (POD) nincs sebességbeli különbség. Általad definiált típusok esetén viszont valóban lassabb a post inkrementálás:
MyClass &operator++(){
//elvegzed a pre inkrementalashoz szukseges muveleteket
return *this; //hivatkozas szerinti visszaadas
}
MyClass operator++(int){
//post inkrementalas. Az int parameter csak ennek a jelzese, nem hasznalt.
MyClass tmp(*this); //temporalis objektumot hozunk letre
operator++(); //ez a pre inkrementalas az aktualis objektumon
return tmp; // a nem inkrementalt temporalist adjuk vissza ertek szerint
}Amint látod, minimum egy copy ctor hívással (és plusz egy fv. hívással) több a post inkrementálás. Hogy ez van-e kétszer annyi idő, vagy nincs, az az inkrementálás műveletigényétől függ.
-
jattila48
aktív tag
válasz
MasterMark #2911 üzenetére
Nem gáz a break. Egyszerű dolgot nem kell túlbonyolítani. Adott elemszámú tömbben lineáris keresésre tökéletes. Ha pl. az a feladat, hogy egy karaktertömbben sorban keresd ki az A betűket, akkor ez a legjobb megoldás. Ha függvényt használnál, akkor minden következő kereséshez nemcsak a tömböt, hanem a most megtalált indexet is át kell adni. Ez fölösleges bonyolítása a feladatnak. Ha nagyon nem akarsz break-et, akkor elképzelhető, hogy a kiugrási feltétel betehető a ciklusfeltételbe.
int i;
char tomb[100];
int hossz=sizeof(tomb);
for(i=0;i<hossz && tomb[i]!=feltetel;++i);
if(i<hossz){
//megtalaltad a kersett elemet, indexe i.
}for(i=0;i<hossz;++i){
if(tomb[i]==feltetel)break;
}helyett.
C++ programozóknál gyakori hiba, hogy hajlamosak "túldizájnolni" a programot. Egy ilyen egyszerű feladathoz pl. nem biztos, hogy az STL-t kell használni. Nagy, és lassabb lesz tőle a kód.
-
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? -
jattila48
aktív tag
Ez alapján valóban így lehet, ahogy írod. Én a Visual Studio 2012-őt használom, és még debug módban sem hívja a copy ctort T s=T() -re. Ellenben a my_matrix-ot érték szerint visszaadó operátoroknál debug módban nincs RVO (meghívódik a copy ctor), release módban pedig van RVO, annak ellenére, hogy a copy ctor-ban logolás van. Mondjuk azt nem értem, hogy a T x=t (ahol t temporális (és T típusú, bár talán ez sem fontos)) inicializálásnál mégis miért hívnák meg a copy ctort? Egyáltalán van ilyen fordító? A T s{} inicializálást a VS2012 nem ismeri, ezért nem azt használtam.
Ú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!
- Windows 10
- AMD Ryzen 9 / 7 / 5 9***(X) "Zen 5" (AM5)
- Kerékpárosok, bringások ide!
- Windows 11
- Fejhallgató erősítő és DAC topik
- exHWSW - Értünk mindenhez IS
- Megfizethető, Pulse szériás Sapphire alaplap DDR5-ös Ryzenek alá
- Külföldi rendelések: boltok, fizetés, postázás
- Xiaomi 15 - kicsi telefon nagy energiával
- Milyen billentyűzetet vegyek?
- További aktív témák...
- Dell Latitude 7410 Strapabíró Ütésálló Profi Ultrabook Laptop 14" -80% i7-10610U 16/512 FHD IPS MATT
- Eladó Lian Li O11D MINI-X gépház
- Lenovo ThinkPad P17 Tervező Vágó Laptop -50% 17,3" i7-10750H 32/512 QUADRO T1000 4GB
- FSP DAGGER PRO ATX3.0(PCIe5.0) 850W Sfx tápegység
- Eladó PNY GeForce RTX 4070 Ti SUPER 16GB OC XLR8
- Huawei P20 Lite 64GB, Kártyafüggetlen, 1 Év Garanciával
- Lejárt a gyártói garancia? Mi tovább támogatjuk az IT infrádat!
- ÁRGARANCIA! Épített KomPhone i5 13400F 32/64GB RAM RX 7700 XT 12GB GAMER PC termékbeszámítással
- BESZÁMÍTÁS! Asus TUF F15 FX506HM Gamer notebook - i5 11400H 16GB DDR4 RAM 512GB SSD RTX 3060 6GB W10
- BESZÁMÍTÁS! HP Victus 16-D0655NG notebook - i5 10400H 16GB DDR4 512GB+1TB SSD RTX 3060 6GB WIN10
Állásajánlatok
Cég: Promenade Publishing House Kft.
Város: Budapest
Cég: PCMENTOR SZERVIZ KFT.
Város: Budapest