- Motorola Edge 40 - jó bőr
- Okosóra és okoskiegészítő topik
- Na! Ez egy JÓ utólagos autós fejegység - Minix CP89-HD
- Telekom mobilszolgáltatások
- Motorola Edge 50 Neo - az egyensúly gyengesége
- Mobil flották
- Google Pixel 9 Pro XL - hét szűk esztendő
- Akciófigyelő: Jelentősen olcsóbban nyit az Ulefone új mindenese
- India felől közelít egy 7550 mAh-s Redmi
- VoLTE/VoWiFi
Új hozzászólás Aktív témák
-
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.
-
-
EQMontoya
veterán
válasz
jattila48 #3079 üzenetére
Szerintem a readet (elég, ha csak szignatúrájában egységes) a source-ba kellene tenni, és egyébként csak processzálás van.
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.
-
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.
-
EQMontoya
veterán
válasz
jattila48 #3066 üzenetére
Nem volt időm az egészet végigolvasni, csak úgy nagyjából látom a problémát:
-Különböző forrásokból összeszedsz valami bináris adatot
-Ezt kb. egységesen olvashatod.Szerintem ennek a két funkcionalitásnak nem egy osztályban van a helye. Amivel olvasol (legyen source), az legyen egy külön osztály, valami interface-szel. Ebből lehet leszarmaztatni sok félét, aztán az olvasgató osztályod konstruktorban paraméterként kap egy shared ptr-t egy source-ra. Így lesz raii is, mert az olvasó destruálja a shared ptrt is, ami meg magát az objektumot. A shared ptren keresztül meg hívogathat rajta mindenfélét.
-
dobragab
addikt
válasz
jattila48 #3068 üzenetére
Remek, tényleg félreértettem. Azt eddig nem mondtad, hogy a blob formátuma egységes. Azt hittem, hogy a init az adattagokat kapja paraméternek. Így kicsit félrevezető, én nem init-nek nevezném, hanem read-nek, és tkp. ő végzi a deszerializációt.
Legközelebb inkább példakódot írj, azt hamarabb megértjük, mint fél oldal szöveget
Ebben az esetben viszont rendben van így is. Egyetértek veled, jobb ez a megoldás. Én nem kettős inicializálásnak hívnám, hanem a szerializálást kiraktad a D osztályba a RAII miatt.
-
dobragab
addikt
válasz
jattila48 #3064 üzenetére
Az std::string filename RAII-ra átalakításánál úgy gondoltam, a kissé kreatívabban oldod meg. Pl. kap még egy paramétert a privát ctor, ami átveszi az erőforrást, és felszabadítja. Így effektíven a D-nek a read_from_file a konstruktora. A hármas szabállyal most nem tököltem.
class D : public B
{
std::string filename;
FILE * fp = nullptr;
D(FILE * fp,
std::string const& filename,
std::vector<std::string> const& param1,
std::map<int, int> const& param2) :
B{param1, param2},
filename{filename},
fp{fp}
{ }
public:
static D read_from_file(std::string const& filename)
{
fp = fopen(filename.c_str(), "r+b");
std::vector<std::string> myvec;
std::map<int, int> mymap;
// load data from file to my___
return D{fp, filename, myvec, mymap};
}
virtual void pure_virtual_func() override
{ }
virtual ~D()
{
fclose(fp);
}
};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.
Nem ott van, szándékosan. Ha jól értem, B leszármazottai abban különböznek egymástól, hogy más külső reprezentációja. B-nek az a dolga, hogy ezeken felülemelkedjen, nélkülük legyen teljes értékű. Épp ezért B-nek nem szabad függnie tőle, azt oldják meg a leszármazottak.
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.
Ez egy felemás RAII lenne, ami szerintem koncepcionálisan hibás.
std::shared_ptr és make_shared? Szerintem ez a megoldás pont arra hasonlít. 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. Alapesetben nem kéne, hogy egy osztálynak legyen ctor-ként működő init-je.
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.
-
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.
-
dobragab
addikt
válasz
jattila48 #3061 üzenetére
Mutatok példát egyszerű osztályokkal. Én erre gondoltam, de a te eseted ezek szerint nem pont ilyen.
Kicsit elnéztem én is, ugyanis B-t (absztrakt osztályt) nem adhat vissza érték semmilyen a függvény...
class B
{
std::vector<std::string> member1;
std::map<int, int> member2;
public:
B() = delete;
B(const B& other) = default;
B& operator=(const B& other) = default;
B(B&& old) = default;
B& operator=(B&& old) = default;
B(std::vector<std::string> const& param1,
std::map<int, int> const& param2) :
member1{param1},
member2{param2}
{ }
virtual void pure_virtual_func() = 0;
virtual ~B() { }
};
class D : public B
{
std::string filename;
public:
D(std::string const& filename,
std::vector<std::string> const& param1,
std::map<int, int> const& param2) :
B{param1, param2},
filename{filename}
{ }
static D read_from_file(std::string const& filename)
{
std::ifstream file(filename);
std::vector<std::string> myvec;
std::map<int, int> mymap;
// load data from file to my___
return D{filename, myvec, mymap};
}
virtual void pure_virtual_func() override
{ }
};Ha valamit félreértettem a feladattal kapcsolatban, szólj
Kódból jobban értek -
dobragab
addikt
válasz
jattila48 #3058 üzenetére
Most már nekem is kicsit koncepcióhiba-szagú, persze simán lehet, hogy indokolt.
B ellátja a funkcióját, valamilyen adatokon dolgozik. Adsz neki egy ctort, ami az adattagjait darabonként inicializálja, és nem bűvészkedik a bináris adatokkal.
A bináris memóriaterületből olvasás, feldolgozhatóvá alakítás ilyen formában (pointer + méret) nem egy osztály dolga, hanem egy függvényé. Tipikusan egy statikus függvényé, ami megkapja fájlnevet, akármit, és visszaad érték szerint egy B-t. Én ebből csinálnék többet, attól függően, hogy fájl / registry / memória a forrás. Ezek hívják majd B adattagonként inicializáló konstruktorát. A statikus függvények meg lehetnek protected-ek, hogy a RAII-t ne lehessen kívülről zavarni, és hívhasd B / D ctorából. Írj B-nek move ctort, akár default is jó lehet.
Ezzel különválasztottad a program egyáltalán nem összetartozó részeit. B innentől önállóan is működőképes osztály, és hogy a te programodban mindig fájlból érkezik, az mellékes.
Ezután D konstruktora valahogy így nézhet ki:
D::D(std::string filename, T data) :
B{std::move(D::ReadFrom(filename))},
data{data}
{ }Ha B önmagában nem olvasható be, vagy D-vel együtt kellene, mert B beolvasása után kellenek még az adatok, D-t is adhat vissza a beolvasó függvény.
-
dobragab
addikt
válasz
jattila48 #3055 üzenetére
Írj B-nek konstruktort init helyett. Absztrakt osztálynak simán lehet konstruktora, pont az ilyen esetekre, és default ctor-t tiltsd le. D ctor-ban pedig B{args} az inicializáló listában.
A számításokat pedig szerintem el lehet végezni az inicializáló listában is, az ős-ctor hívása előtt, a paraméterekben, ha máshogy nem, vessző operátor segítségével
Szerk: ha bonyolultabbak a számítások, a statikus függvény jó ötlet
Factory? -
EQMontoya
veterán
-
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. -
-
dobragab
addikt
válasz
jattila48 #3012 üzenetére
Ha észrevetted, kapott már olyan választ, amilyet te elvársz. EQ megmutatta, hogy ezt igazán C++-osan hogyan illik. Pl. tőlem mindenképpen kapott volna egyszerű választ.
Ajánlom figyelmedbe Stroustrup intelmeit az Újszövetségből. (The C++ Programming Language 4th Edition)
[8] Minimize the use of arrays and C-style strings. C++ standard-library strings (§4.2), arrays (§8.2.4), and vectors (§4.4.1) can often be used to write simpler and more maintainable code compared to the traditional C style. In general, try not to build yourself what has already been provided by the standard library.
[10] Do not assume that something laboriously written in C style (avoiding C++ features such as classes, templates, and exceptions) is more efficient than a shorter alternative (e.g., using standard-library facilities). Often (but of course not always), the opposite is true.
-
EQMontoya
veterán
válasz
jattila48 #3009 üzenetére
Ez nem a teljesítményről szólt éppen (nyilván erről 10 elemnél nem lehet beszélni), hanem arról, hogy használjuk kihasználjunk olyan működéséket, amik kényelmesek és megkönnyítik a dolgunkat.
Ha gyors kódot akarsz írni, akkor meg static array, ami alapból zero inites, és csinálod rajta ugyanezt.dobragab: kihasználtam az int zero initjét, amit a static array ugyan tud, de tömb vagy vektor nem. Illetve ha megnézed az utolsó mondatomat, a kulcsok rendezettségét és validálását is ki tudod használni.
A map azért elég jó olyan felhasználásra, amikor a kulcsaid csak az értékkészlet kis részét (<20%) fedik le.
-
LordX
veterán
válasz
jattila48 #2992 üzenetére
Jó, persze ez sarkított példa, én is alapból ++x-et gépelek mindenhol, csak rossz reflexet fejleszt ki a kezdő programozóban, hogy figyelni kell mindenre, különben leharapja a C++ a lábad: "Használj pre-incrementet. Ne adj át érték szerint paramétert függvénynek, adj át const referenciát. Ha lemásolod a paramétert inkább add át érték szerint. Csinálj így, csinálj úgy."
Ha már dolgozol egy éve, akkor amúgy is rájössz, hogy mi az, ahogy elsőre érdemes csinálni.
-
LordX
veterán
válasz
jattila48 #2987 üzenetére
Csak azt mondom, hogy amikor írod a kódot, akkor nem kell görcsölni rajta. Csináld ahogy sikerül, ha hibáztál is, ez soha nem olyan nagy hiba, hogy probléma legyen. Ahol probléma, ott úgyis kiderül az első profilozás után, ahol nem profilozol, ott meg nem probléma.
A program logikájára figyelj, ne arra, hogy itt van egy a kismillió "kötelező" szabály között, ha C++-ban programozol. Ezért van rossz híre a nyelvnek - valójában non-issue az összes ilyen; ha nem akarsz vele foglalkozni, nem kell, semmi nem kötelező.
A programot írjad C++-ban, ne C++ kódot, ami mellékesen megvalósítja a programod.
-
LordX
veterán
válasz
jattila48 #2986 üzenetére
Egyébként én szívem szerint leszedném a publikus verziót, többet árt az egész iparnak, mint használ. Olyan döntések vannak benne (legkirívóbb a kivételek teljes tiltása), amiről már a cégen belül is tudja mindenki, hogy rossz, de a masszív mennyiségű kód miatt nem lehet változtatni.
-
LordX
veterán
válasz
jattila48 #2975 üzenetére
Az operator++() és az operator++(int) csak egy fancy név egy-egy függvénynek. Í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. Ilyen nincs. 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.
Ha postfixet írtál a kódba, hót zicher, hogy postfix lesz belefordítva.
-
LordX
veterán
válasz
jattila48 #2967 üzenetére
Ha az iterátorod inkrementálása nem inline, akkor ott valami nagy gáz van..
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. Van egy rossz hírem: nem az. Követi a kis szabályait, és az alapján generál/módosít kódot.
Kipróbálni egyszerű, mi van, ha nincs inline __attribute__((noinline)) a függvény definíciója elé GCC/Clang esetében - MSVC-ben meg valami force_no_inline?
Itt nincs RVO, se copy elision - nincs objektum, aminek temporálisból való konstruálása van eltüntetve, hanem bármi objektum konstruálása teljesen - ez már túllép azon, amit ez a szabály enged, és csak azért megoldható, mert se a konstruktor, se a destruktor nem tartalmaz mellékhatást. Ha kikapcsolod az optimalizálást, de force-olod az inline-t a példámban (kapcsolókkal kell játszani, most nincs rá időm kitalálni mit kell), akkor szépen látszana, hogy van pár extra MOV a konstruktor miatt a post-nál (a destruktor meg no-op).
-
LordX
veterán
válasz
jattila48 #2965 üzenetére
Nem érted. A fordító azt fordítja bele, amit írtál. Nem tehet mást. Generál valamilyen köztes kódot (mindegyiknek van, csak egyedül a Clang esetében publikus, az LLVM-IL).
Pre esetben a generált kódra rámozdul az optimalizáló, ami először inline-olja a copy construktort, tehát lesz 1 függvényed, ami másol biteket, azt machinál valamit másik biteken. a következő lépés látja, hogy lemásolsz valami biteket, azt nem használod - fölösleges kód, törölhető.
Post esetben meg nincs copy, nincs mit töröljön.
Az egyik esetben a gép szenvedett egy kicsit érte, de az eredmény ugyanaz, ezért ha nem kritikus kód (ami csak az után derül ki, hogy leprofiloztad!), akkor ne foglalkozz vele, mert nem ér annyit.
Elírtam, fordítva: a WTF a pre-inkrement post-inkrementtel megvalósítva.
-
LordX
veterán
válasz
jattila48 #2963 üzenetére
Az nem épp valid működés, hogy másik operátort fordít, nem változtathatja meg a fordítás a program működését (egyedül copy elision / RVO szabad). Post-increment van itt is, csak elég okos a fordító, hogy kioptimalizálja az extra MOV utasításokat, amik a copy construktorral jöttek létre - módosítsd a példát, hogy legyen valami mellékhatása a copy konstruktornak, máris nem optimizálja ki.
-
LordX
veterán
válasz
jattila48 #2928 üzenetére
Annyira nem eszik forrón a kását. 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:
[link]
(Az std::slist iterátora kb. így működik, mint a példám, csak ott van még a struktúrában a tárolt elem is.)Szóval nem érdemes ebbe energiát befektetni. Ahol a program logikájához pre-inkrement kell, ott azt kell használni, ahol post, ott azt, ahol mindegy, ott meg mindegy. (De mint korábban írtam, azért ha lehet ne szopassuk az optimizálást fölöslegesen, és használjunk inkább pre-t, egy idő után úgy is az lesz a természetes.)
(BTW, a POD-nak semmi köze ehhez, a példámban a két osztály is POD, és nyilvánvalóan nem triviális ez a két OP.)
-
dobragab
addikt
válasz
jattila48 #2950 üzenetére
É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!).
Ha jut eszembe, szólok.
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.
-
dobragab
addikt
válasz
jattila48 #2946 üzenetére
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)?
A for ciklussal még mindig semmi bajom.
Valahogy így: [link]. -
dobragab
addikt
válasz
jattila48 #2946 üzenetére
A kérdező azt kérdezte, hogy for ciklusban OK-e a break.
Basszus, ezt elnéztem
Ő egy szóval sem említette, hogy lineáris keresésről van szó. Kezdőnek meg illik azt mondani, hogy nem, mert a végén rászokik. Vagy legalább annyit, hogy speciális esetekben igen.
Lehet, hogy neked maga a for ciklus (főleg break-kel) már pattintott C korszak
A for ciklussal semmi baj. A break-kel már annál több. A pattintott C korszak meg a funkcionális dekompozíció elutasítására vonatkozott.
Egyébként biztos ismered Linus Torvalds véleményét a C++ nyelvről.
Persze. És azt tudod, hogy függvényt Linus is használ?
Ja, akkor a linux szerinted pattintott C korszaki, ugye?
A Linux nem. Linus igen.
-
dobragab
addikt
válasz
jattila48 #2942 üzenetére
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.
Nem kerülte el, szándékosan írtam azt. Ebben az esetben (lineáris keresés) baromi egyszerű a függvény. Erre van a funkcionális dekompozíció, és ez egyáltalán nem C++ vagy OOP. A programot szedjük szét jól elkülöníthető, újra felhasználható részekre. Ez a kifejezés normál esetben a függvény szóval nagyjából egyszerre kerül bevezetésre. C-ben, C++-ban, vagy bármilyen nyelvben, ahol van függvény. Ha figyelted, én is elismertem, hogy van létjogosultsága a break-nek, de itt egyáltalán nincs.
Jelen esetben a fv. hívás maga túlságosan költséges az elvégzett feladathoz képest.
Igazatok volt. Pattintott C korszak.
-
dobragab
addikt
válasz
jattila48 #2937 ü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)
Ez most a heti vicc rovat, vagy komolyan gondoltad? A break-kel az a gond, hogy nehezen követhető, pontosan hova ugrik, és milyen utasításokat lép át, ami a változók további élettartama szempontjából fontos. Gondolj bele egy switch-ben lévő két egymásba ágyazott ciklusba, és a belső ciklusban egy break. Ha már itt tartunk, azt tudod, hogy hova ugrik?
return-nél messze nincsenek ilyen bajok. A return azt jelenti, hogy a függvény teljesítette azt, ami a feladata. Nyilvánvaló, hol folytatódik a végrehajtás. Nem marad utána semmilyen változó, nem probléma, épp mennyi volt az értéke a visszatéréskor. Az erőforrások felszabadítását most ne keverjük ide, mint otthagyott dinamikus memória. Ha nekem nem hiszel, legalább Stroustrup bácsinak higgy.
És, ha nem kell végigmenni, hanem csak az első két előfordulást kell megtalálni?
Ez elég speciális eset, ilyenkor elfogadhatónak tartom a break-et, de nem hinném, hogy itt ez volt a kérdés. Ja és code smell-t érzek, ha ilyen kell. Persze agyf@sz feladatban lehet ilyen, de production kódban szerintem nem jellemző.
Akárhogy csűrjük, csavarjuk, szerintem van ahol kifejezetten jó megoldás a break for ciklusból.
Van. De egy lineáris keresés tipikusan nem ez az eset.
ráadásul beépítesz egy fölösleges függvény hívást
Wut? Na nehogymá' egyetlen függvényhíváson akarjunk spórolni 2015-ben lineáris keresésnél (ami ugye O(n), tehát lassú)!
Mivan? Ne is írjunk függvényt egy teljesen jól elkülöníthető, egyszerű visszatérési értékű, újra felhasználható (!) kódrészlethez? Funkcionális dekompozíció FTW. Akkor nálad mi üti meg a küszöböt, ami már függvényt ér?
-
dobragab
addikt
válasz
jattila48 #2924 üzenetére
Összekeversz két problémát. Ha csak az egyik elemre van szükség, akkor jó a függvény:
int tombben_keres(int tomb[], int meret, int szam)
{
for(int i = 0; i < meret; ++i)
if(tomb[i] == szam)
return i;
return -1;
}Ha viszont mindenképp a teljes tömbön kell végigiterálni (összes nagybetű kiírása, pozitív számok összege, stb), akkor minek break? Simán végigmegy rajta.
for(int i = 0; i < meret; ++i)
if(isupper(str[i]))
std::cout << str[i]; -
EQMontoya
veterán
válasz
jattila48 #2929 üzenetére
Hidd el, az stl cuccok elég jól optimalizáltak, és szerintem a fordítók felé is elég sok hintet tartalmaznak az implementációk. Szóval nem lesz az lassú.
Az meg a pattintott C korszak, hogy mindent megírunk magunknak, főleg a lambda függvények korában. Nyilván lehet fort meg brake-et írni, de a legtöbb esetben felesleges. -
ToMmY_hun
senior tag
válasz
jattila48 #2924 üzenetére
Kezdő kérdés: Ugye sima int típusnál csak azért használsz, illetve használnak a gyakorlott C++ programozók pre-increment operátort, mert megszokták az STL-es iterátor inkrementálásnál? Annyit tudok, hogy iterátor típusnál célszerű a pre-increment, mert futásidőben a post akár kétszer olyan lassú is lehet, viszont ez nem igaz sima integer-nél, ugye?
-
LordX
veterán
válasz
jattila48 #2924 üzenetére
Vagy csak simán
auto elem = find(begin(tomb), end(tomb), feltetel);
if (elem != end(tomb)) { /* *elem-et kerested */ }- Rövidebb (1 függvény 3 paraméterrel)
- öndokumentáló (vajon mit csinál ez a ciklus ezzel a bonyolult feltétellel vs vajon mit csinál a find függvény)
- ha lassabb kódot generál, akkor sürgősen cseréld le a fordítód, mert konkrétan ugyanaz a kód, miután inline-oltad
- gyorsabb is lehet, ha ki tudja használni a konténer / elem típusát
- nem random access konténerrel (set, list..) is működik -
ToMmY_hun
senior tag
válasz
jattila48 #2877 üzenetére
Lehet, hogy túlbonyolítom. Leírom mi a végső cél. Adott egy program, amely számításokat végez és socket-en küldi le ezeknek az eredményét 5 kliensnek. A klienseknek folyamatosan csatlakozva kell lenniük, különben az adatok érvénytelenek. A főprogram alatt gondolom a fő végrehajtási szálat érted. A további szálakat úgy oldottam meg, hogy csináltam egy SocketComm osztályt, abban van egy statikus metódus, ami a kommunikációért felel. Ez a statikus metódus az osztályban lévő attribútumokból olvas és küld, magát a kapcsolatot nem ő építi fel hanem az osztály konstruktora. A thread indításáért felelős függvénynek a statikus függvény pointerét adom át (ezt nevezhetjük callbacknek), amit így futtat a threadben és ezzel azt is megoldottam, hogy a későbbiekben ha változik a kliensek száma, akkor az indítások számát kell csak változtatni.
UI: Egyelőre azért van egy thread, mert csak server -> kliens irányú a kommunikáció, a válaszokat később szeretném beépíteni és ahhoz valóban kelleni fog a kliensenkénti thread.
-
LordX
veterán
válasz
jattila48 #2875 üzenetére
Mivel a Copy elision-t már 12 éve engedi a szabvány, ma már nem nagyon van olyan fordító, ami nem tudná. A 2000-es évek elején még volt.
A szabvány logikáját kell követni: ha valamit copy/direct initialization-el definiálsz, akkor konstruktor overload van. Ez akkor is működik, ha valami kitekert konstruktorod van (const rvalue ref-et váró, leszármazott referenciát váró, stb.). Ha ez megvan, utána jön a "triviális" optimizáció, de ez csak akkor van, ha copy vagy move konstruktor játszik, a fura konstruktorok nincsenek kioptimalizálva - és erősen valószínű, hogy azokat azért csináltad, mert szükséged van rá.
Csak ugye a triviális annyira nem is triviális - mi van, ha van egy olyan kódod, hogy:
T f() { return T(); }
T g() { return f(); }
void h() { T x = g(); }(Tetszőlegesen bonyolult f és g függvényekkel, nem csak ilyen kis Móricka példával.) Hány darab copy/move van itt, amit mellőzni lehet? 1? 2? 4? Erősen attól függ, hogy mit inline-olsz, ami már alapból egy optimizációs kérdés, és másik fordító máshogy dönthet..
-
LordX
veterán
válasz
jattila48 #2871 üzenetére
De van, a szabvány ezt mondja, [dcl.init] szekció:
"The initialization that occurs in the form
T x = a;
as well as in argument passing, function return, throwing an exception, handling an exception, and aggregate member initialization is called copy-initialization. [ Note: Copy-initialization may invoke a move. — end note ]
(...)
If the initialization is direct-initialization, or if it is copy-initialization where the cv-unqualified version of the source type is the same class as, or a derived class of, the class of the destination, constructors are considered. The applicable constructors are enumerated, and the best one is chosen through overload resolution (13.3). The constructor so selected is called to initialize the object, with the initializer expression or expression-list as its argument(s)."Magyarul, ha a változó és az inicializáló típusa ugyanaz (modulo CV), akkor:
1. Kiértékeli az egyenlő jel jobb oldalát.
2. Overload resolution kitalálja melyik konstruktor kell (azonos típus miatt copy vagy move), és azt hívja meg a jobb oldallal, mint paraméterrel.
Tehát, T x = T();
1. T() default-konstruál egy temporális T-t
2. Overload resolution megtalálja a move konstruktort (vagy ha nincs, copy), és meghivja
3. Expression temporálisa destruálásMivel a bekezdés a direkt inicializálás és a copy inicializálás (ahol az inicializáló kifejezés ugyanolyan típusú) között nem tesz különbséget a T x = valami; és T x{valami}; között, a valami helyére behelyettesítve a T()-t, azt kapod, hogy T x{T()};.
Demo Az -fno-elide-constructors kapcsoló kapcsolja ki a copy elisiont.
-
EQMontoya
veterán
válasz
jattila48 #2869 üzenetére
The C++ language standard generally allows implementations to perform any optimization, provided the resulting program's observable behavior is the same as if, i.e. pretending, the program was executed exactly as mandated by the standard.
Szóval ott csak addig lesz copy elision, ameddig a copy ctr. triviális, Ha mondjuk van ott egy logolás, akkor simán le fog futni a default ctr majd a copy, mert nem optimalizálhatja ki.
-
jattila48
aktív tag
válasz
jattila48 #2868 üzenetére
Bocs, értékadó operator helyett természetesen copy ctor-t akartam írni. Szerintem erre gondoltál, hogy T()-vel létrejön egy temporális objektum, amit a copy ctor paraméterként átvéve létrehozza az s-et. De ez nem igaz. Csak a default ctor hívódik meg a deklarációban, és ez független az RVO-tól (copy elision).
-
jattila48
aktív tag
válasz
jattila48 #2852 üzenetére
A szorzás gyorsítása ilyen lehet:
template<typename T,int n,int m,int k> my_matrix<T,n,m> operator*(const my_matrix<T,n,k> &a,const my_matrix<T,k,m> &b){
my_matrix<T,n,m> c(dummy);
int i,j,l;
for(i=0;i<n;++i){
for(j=0;j<m;++j){
T s=T();
for(l=0;l<k;++l){
s+=a.matrix[i][l]*b.matrix[l][j];
}
c.matrix[i][j]=s;
}
}
return c;
}Ehhez csak a my_matrix osztályba fel kell venni friend-nek:
template<typename T,int n,int m> class my_matrix{
template<typename T,int n,int m,int k> friend my_matrix<T,n,m> operator*(const my_matrix<T,n,k> &a,const my_matrix<T,k,m> &b);
public:
typedef T scalar_type;
...Végül is nem bonyolult, és mégsem kell eltárolni a méretet.
-
jattila48
aktív tag
válasz
jattila48 #2851 üzenetére
A némileg optimalizált változat:
#ifndef _MATRIX_
#define _MATRIX_
#include <assert.h>
#include <iostream>
static struct dummy_type{}dummy;
template<typename T,int n,int m> class my_matrix{
public:
typedef T scalar_type;
my_matrix(const dummy_type &){}
my_matrix(const my_matrix& mm){
std::cout << "copy ctor" << std::endl;
int i,j;
for(i=0;i<n;++i){
for(j=0;j<m;++j){
matrix[i][j]=mm.matrix[i][j];
}
}
}
explicit my_matrix(T x=T()){
int i,j;
for(i=0;i<n;++i){
for(j=0;j<m;++j){
matrix[i][j]=x;
}
}
}
my_matrix & operator=(const my_matrix &mm){
int i,j;
for(i=0;i<n;++i){
for(j=0;j<m;++j){
matrix[i][j]=mm.matrix[i][j];
}
}
return *this;
}
my_matrix & operator+=(const my_matrix &mm){
int i,j;
for(i=0;i<n;++i){
for(j=0;j<m;++j){
matrix[i][j]+=mm.matrix[i][j];
}
}
return *this;
}
my_matrix & operator*=(T c){
int i,j;
for(i=0;i<n;++i){
for(j=0;j<m;++j){
matrix[i][j]*=c;
}
}
return *this;
}
my_matrix operator+(const my_matrix &mm) const{
//return my_matrix(*this)+=mm;
my_matrix t(dummy);
int i,j;
for(i=0;i<n;++i){
for(j=0;j<m;++j){
t.matrix[i][j]=matrix[i][j]+mm.matrix[i][j];
}
}
return t;
}
my_matrix operator*(T c) const{
my_matrix t(dummy);
int i,j;
for(i=0;i<n;++i){
for(j=0;j<m;++j){
t.matrix[i][j]=c*matrix[i][j];
}
}
return t;
}
const T & operator()(int i,int j) const{
assert(i<n && j<m && i>=0 && j>=0);
return matrix[i][j];
}
T & operator()(int i,int j){
return const_cast<T &>(static_cast<const my_matrix *>(this)->operator()(i,j));
}
private:
T matrix[n][m];
};
/*
template<typename T> T operator+(T a, T b){
return T(a)+=b;
}
*/
template<typename T,int n,int m,int k> my_matrix<T,n,m> operator*(const my_matrix<T,n,k> &a,const my_matrix<T,k,m> &b){
my_matrix<T,n,m> c(dummy);
int i,j,l;
for(i=0;i<n;++i){
for(j=0;j<m;++j){
T s=T();
for(l=0;l<k;++l){
s+=a(i,l)*b(l,j);
}
c(i,j)=s;
}
}
return c;
}
template<typename T> T operator*(typename T::scalar_type c,T &A){
return A*c;
}
#endifEbben már nincs fölösleges copy ctor hívás. Csak a my_matrix-ot érték szerint visszaadó fv.-ek hívják a copy ctort, legalábbis debug verzióban vagy nem C++11 kompatibilis fordítóval fordítva, de az újabb fordítók az RVO miatt ezt is ki optimalizálják (copy elision. mindig!). A dummy_type típusú dummy változó csak a megfelelő ctor overload kiválasztása miatt lett bevezetve. A my_matrix(const dummy_type &) ctor semmit nem csinál, ez csak a megfelelő méretű inicializálatlan elemű üres mátrix létrehozására kell. Az előző verzióban erre a copy vagy default ctort kellett használni, azonban amikor az így létrehozott mátrix elemei (pl. összeadással) amúgy is felül lesznek írva, fölösleges az elemek inicializálása. Ez a ctor persze nem használható "rendes" objektum létrehozására, mert az így létrehozott objektum önmagában használhatatlan. Célszerű lenne ezért private-ba tenni, de akkor az ezt hívó free fv-eknek friend-nek kéne lenni (TODO). A default ctort is lehetett volna így használni, azonban akkor ezzel nem lehetne objektumot létrehozni. A mátrix szorzáson is lehetne gyorsítani pl. úgy, hogy a * operatort az összes különböző méretű, azonos skalár típusú my_matrix friend-jévé tesszük, és nem az elemkiválasztó operator()(int,int)-t hívjuk, hanem közvetlenül a matrix[j]-ket, de ekkor el kéne tárolni a my_matrix osztályban az n,m méret template argumentumokat. Valamivel gyorsabb lenne, azonban lényegesen rondább is.
-
EQMontoya
veterán
válasz
jattila48 #2745 üzenetére
RVO != move, az egyik a standard része, a másik meg compiler okosíáts, de tény, hogy néha hasonló a végeredménye
A vektoros példa pedig szerintem koncepcionálisan is f@szság, nem a függvénynek kell követnie a move szemantikát, hanem a visszaadott objektumnak kell moveolhatónak lennie. -
EQMontoya
veterán
válasz
jattila48 #2719 üzenetére
RVO-ra azért alapvetően nem bízzunk mindent (mondjuk egy chaininget - avagy avoid the void - simán), elvégre ahány compiler, annyi szokás. Főleg, hogy némelyik hajlamos hibás asm-et generálni, ha magasra veszed az optimalizációs flaget. (g++ 4.7 x86->arm crosscompilerekben láttam már több ilyen hibát is)
-
pengécske
csendes tag
válasz
jattila48 #2714 üzenetére
Úgy értettem, h egy szám(adat)pár legyen a visszatérési érték. A complex szám csak egy példa volt, mert azokat ugy gondoltam, h a valós rész és a komplex rész egy-egy szám (tehát a+bi alak, ahol a és b a két adat amit tárolok). De akkor kösz, szoval nem képes rá
. Sokat könnyített volna, így marad az, h stringbe kodolgatok, vagy több külön függvényt hozk létre
-
proof88
addikt
válasz
jattila48 #2592 üzenetére
Hello,
a mondandód első felére az a válaszom (ősosztály függvényének elfedése), hogy igazad van, és ezért virtuálisként kell definiálni az ősosztályban azt a függvényt, amit a származtatott osztály felüldefiniálhat, így nem lesz gond. Ezért is szoktuk alapból virtuálisként definiálni a destruktort is, hogy mindenképpen lefusson a származtatott osztály destruktora is törléskor.
Statikus tagokat nem lehet felüldefiniálni. Én csak simán átgondolom ilyenkor, hogy biztos statikus tagot akarok-e ebben az esetben. -
válasz
jattila48 #2583 üzenetére
Jah, a lathatosag ellenorzese csak compile-time tortenik, es kizarolag azt nezi meg a fordito, hogy az az osztaly, amin _keresztul_ elered a metodust, az friend-e vagy sem. Ha a fuggveny, amit hivsz, virtualis, akkor igazabol barmi megtortenhet, lenyegeben a leszarmazott osztalyok azonos nevu (virtualis) metodusai mind elerhetoek lesznek a hivo (friend) szamara.
-
válasz
jattila48 #2581 üzenetére
Ugye ez egy statikus fuggveny, forditasi idoben eldol, hogy melyik fuggvenyt hivja. Megnezi az R-t eloszor, latja, hogy nincs ott ilyen nevu fuggveny. Megy felfele az oroklesi hierarchiaban, megtalalja Q::q-t. Ezek utan megnezi, hogy a lathatosagi szabalyokkal mi a helyzet, es latja, hogy Q::q az lathato P-bol, mert P friendje Q-nak, es mar kesz is.
Ha R::q-t is definialnad, akkor mar nem mukodne. Tehat a fordito _eloszor_ keresi meg a fuggvenyt, amit hivni kellene, utana ellenorzi a lathatosagot.
-
mgoogyi
senior tag
válasz
jattila48 #1933 üzenetére
Igazad lehet.
Valszeg jobb a konstruktor-ból hibát dobni, mint kétfázisúan inicializálni.
Viszont ha tényleg exception lehetséges a konstruktorban alapesetben, akkor már inkább elmennék a factory irányba, ami lekreálja az objektumot, visszaad rá egy pointert és azt nézheti a hívó fél, hogy NULL vagy sem.
Ebben az esetben a hívó félnek nem kell semmi meglepetésre számítania. -
mgoogyi
senior tag
válasz
jattila48 #1920 üzenetére
Úgy nézek ki, mint aki nem akar válaszolni?
- Az alapvető szabály (amennyire tudom), hogy ctor-ból és dtor-ból nem jöhet ki exception.
- Kétfázisú inicializálást meg akkor szoktak használni, amikor valamilyen hibakezelés szükséges, amit nem tehetünk meg a ctor-ban.Pl. mint amit te írsz, abban az esetben helytálló a dolog, hogy kétfázisú inicializálás kell. De ha veled kéne dolgoznom és csak az interface-ét látnám annak, amit használnom kell, akkor azt látnám, hogy az init visszatér egy bool-al, nyilván ez kéne, hogy jelezze, hogy sikerült-e az init. Ha teli kell raknom a kódot try - catch-el, mert mindenféle exception-re számítanom kell, akkor nem lesz nagy öröm használni az osztályodat.
És ha 1-nél többször kell használnom, amit csináltál, akkor már jobb, ha te try-catch-ezel az init-ben, az összes mindenki másnak, akik meg hívják az init függvényed, nekik meg elég a visszatérési értéket vizsgálniuk. -
mgoogyi
senior tag
válasz
jattila48 #1913 üzenetére
Én még különösebb iránymutatást nem hallottam kétfázisú inicializálással kapcsolatban, pedig egy ideje már c++-ozok.
A ctor-os try catch dolog:
A felszabadítós dolgokat be lehet rakni egy függvénybe és azt hívod a ctor catch ágában és a dtor-ban is.
Na szóval az a véleményem, hogy ez nem egy lényeges kérdés. -
mgoogyi
senior tag
válasz
jattila48 #1911 üzenetére
- auto_ptr-rel nem tudsz tömböt felszabadítani (ha tudsz is róla, nem biztos, hogy mindenkinek egyértelmű, főleg mert char tömbökkel dolgozol)
- miért lenne baj a kétfázisú inicializálás? azt kell használni, ami célszerű az adott helyzetben.
- az exception dobás elkerülhető a new esetén az std::nothrow paraméterrel: new(std::nothrow), ekkor NULL pointer-t ad vissza a new, ha nem volt elég memória.
- a destruktorodnak muszáj az objektumod által lefoglalt dolgokat felszabadítani, ez nem kérdés. Nem értem ezt a nagy felhajtást.
- a try catchet ennyi erővel berakhatod a ctor-ba is, ha csak az exception a nagy problémád -
mgoogyi
senior tag
válasz
jattila48 #1867 üzenetére
Szia,
Ennek mi lenne a gyakorlati haszna számodra?
Csak a polimorfizmust akarod kihasználni, mert minden ilyen objektumra ugyanazt a virtuális függvényt hívod majd meg az f(Base **)-on belül?
Ezesetben azt tudod mondani, amit előttem is írtak, hogy Base ** tömb-ben közlekedtesd az összes leszármozott objektum-ra mutató pointert.Kooperálnak is ezek az objektumok f()-en belül, vagy csak minden egyes tömbelemre meg kell hívni egy függvényt?
Googyi
b, lehetőség egy makró, aminek a paramétere a tömböd, amit meg csinálni akarsz a makrón belül, azt úgyis csak a Base-ben definiált függvényekkel tudod.
-
Vico87
tag
válasz
jattila48 #1878 üzenetére
Az egyik megoldás, ha Base** példányt vár a függvényed és létrehozáskor is Base**-ba pakolod a Derived példányaidat. Ez akkor jó, ha ki akarod használni a polimorfizmust és a kollekciód heterogén, azaz statikus típusa Base* tömb, de az egyes elemek dinamikus típusa elemenként eltérő.
Ha biztosan csak Derived példányokat akarsz átadni és ez nem fog változni, akkor minek Base* tömb? A megoldás, hogy a függvényed Derived* tömböt kapjon.
Megjegyzés: a fordító számára nincs különbség Base** és Base*[] között. Az, hogy számodra szemantikailag a két dolog eltérő, az jogos, de a szándékodat (hogy ez most egy Base* tömb, vagy pedig egy Base példányra mutató mutatónak mutatója) a tömb technikai megvalósítása (memóriaterületre pointer) miatt nem tudja megvalósítani a fordító. Csak akkor tudná kitalálni a pontos konverziót, ha pontosan tudná, hogy a Base* tömbödben mennyi példány van, de ezt nem tudhatja, mivel ez futási időben derül csak ki és függvényhívásonként nem konstans.
Ez a konverzió olyan esetben veszélyes, amilyet írtál: egyik leszármazottból konvertálás másik ágon lévőbe, például a tömbödben nem csak Derived*-ok vannak, hanem Derived2*-ok is.
-
jattila48
aktív tag
válasz
jattila48 #1873 üzenetére
Ha a Derived **-ról Base **-ra castolást megengedné a fordító, akkor meg lehetne csinálni a következőt:
Derived2 szintén származzon Base-ből.Derived *d=new Derived;
Base **b;
b=&d; //ezt a Derived **-ról Base **-ra castolást nem engedi a fordító, de reinterpret_cast-tal ki lehet erőszakolni
*b=new Derived2; //ez simán megy, mert Derived2 *-ról Base *-ra castolEz után a d pointer egy Derived2 típusú objektumra fog mutatni, holott Derived *-ként van deklarálva. Ez nyilván baj lenne, ezért nem engedi meg ezt a fajta cast-ot a fordító. Azonban más a helyzet pointer tömbökre. Ha b nem Base ** típusú, hanem Base *[], akkor a b=&d értékadást eleve nem lehetne megcsinálni, mivel b nem megváltoztatható (hiszen konstans tömb cím, amit a fordító konstans értékként kezel). Tehát a Derived *[]-ról Base *[]-ra való castolás biztonságos, és esetenként kívánatos is lenne, de sajnos a fordítók nem tesznek különbséget ezek között.
-
Karma
félisten
válasz
jattila48 #1871 üzenetére
Nem úgy értem a cserét, hanem hogy sehol se használsz Derived tömböt, csak Base tömböt és virtuális metódusokat. Élő helyzetben ennek még értelme is lehet
Veszélyességet csak nagyon madártávlatból gondoltam: StackOverflown olvastam, hogy van olyan architektúra, ahol a pointerek mérete nem állandó. Arra nem emlékszem melyik, valószínűleg nem is releváns.
Agyilag ma elég zokninak érzem magam.
-
jattila48
aktív tag
válasz
jattila48 #1868 üzenetére
Ugyanakkor, ha az f függvény Base[] típusú paramétert vár f(Base *), akkor simán meg lehet hívni Derived[] típusú tömbbel, mivel a Derived[] típust Derived *-nak tekinti, a Base[] típust Base *-nak, és az ősosztály pointerére gond nélkül lehet konvertálni. Ez azonban egészen biztosan hibás működést eredményez. Ebből is látszik, hogy a tömb az nem pointer (minden ellenkező hiedlemmel szemben), még ha hasonlóan kezelhető is.
-
Karma
félisten
válasz
jattila48 #1867 üzenetére
Semmilyen környezetben nem legális művelet a tömb átcastolása, még ha véletlenül működik jelen esetben. A Base** és a Derived** között nincs semmilyen kapcsolat, jogos a compiler panasza.
Egyszeri megoldásnak jó amit csináltál, de nem lehetne a Derived*[]-öt teljesen Base*[]-re cserélni?
Ú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!
- Formula-1
- Sega, Nintendo - retro konzolok
- Az évtized végéig maradhatnak a PC-kben a PCI Express 5.0-s SSD-k
- Nem viszi az USA-ba az adatainkat a Microsoft
- Elkészült a PCI Express 7.0-s szabvány
- Hálózati / IP kamera
- Motorola Edge 40 - jó bőr
- PayPal
- Háztartási gépek
- Kompakt vízhűtés
- További aktív témák...
- 35" ASUS ROG Swift PG35VQ curved GAMER monitor
- LG 65C2 - 65" OLED evo - 4K 120Hz 1ms - NVIDIA G-Sync - FreeSync Premium - HDMI 2.1 - PS5 és Xbox!
- Bomba ár! Dell Latitude E6520 - i7-2760QM I 8GB I 256SSD I Nvidia I HDMI I 15,6" HD+ I W10 I Gari!
- ÚJ- Lenovo ThinkVision T24i-10 - 24" monitor - Számla, garancia
- BESZÁMÍTÁS! ASUS ROG STRIX Z390-E GAMING alaplap garanciával hibátlan működéssel
Állásajánlatok
Cég: CAMERA-PRO Hungary Kft
Város: Budapest
Cég: Promenade Publishing House Kft.
Város: Budapest