Új hozzászólás Aktív témák
-
jattila48
aktív tag
T s=T(); nem hívja az értékadó operátort, ez mindössze egy default ctor hívás. Ennek nincs köze a copy elision-höz és egyéb optimalizációhoz sem, debug módban is csak ennyi. Amit írtál az le sem fordul, lokális fv.-ként próbálja értelmezni. Egyébként a T s; pedig az alaptípusok miatt nem elegendő (pl. int), mert deklarálja az s-t, csak éppen nem inicializálja 0-val. Szóval szerintem jó ez így. Egyéb "hiba"?
-
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.
-
jattila48
aktív tag
válasz
EQMontoya #2847 üzenetére
Az assert valóban nem véd alul indexelés ellen, nem akartam annyit írni. Ez amúgy is csak egy vázlat. Megoldható úgy is ahogy írod, de szerintem így elegánsabb (és talán hatékonyabb). Amúgy nem sok eset van, amikor a const_cast-nak létjogosultsága van, ez az egyik (más most nem is jut eszembe), ezt is szerettem volna bemutatni. Ha teljesen kidolgoznám a példát, akkor pl. a scalar_type-ot is megnézném, hogy triviálisan másolható-e, és ettől függően írnék vagy nem írnék hozzá copy ctort és értékadó operátort. Esetleg a default ctort lenne érdemes átalakítani, hogy az összes elemet adott értékkel feltöltse (ekkor persze már nem default) pl. így:
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;
}
}
} -
jattila48
aktív tag
válasz
EQMontoya #2845 üzenetére
Valóban, az operator+ tagoperator helyesen my_matrix operator+(const my_matrix &mm) const
operator* tagoperator pedig nincs. const_cast azért kell, mert a nem const verzió ugyanaz mint a const verzió, csak nem const objektum egy elemére ad vissza nem const referenciát. Itt most egyszerűbb lett volna beírni ugyanúgy azt az egy sort, azonban ha const verzió változik, változtatni kell a nem const verziót is. Bonyolultabb esetekben is általában ez a megfelelő megoldás. A const_cast-ok csak a fordítónak szólnak, nem generálódik kód hozzá, így aztán hatékony is. -
jattila48
aktív tag
válasz
Boryszka #2831 üzenetére
A matrix_base-től való öröklés szerintem felesleges, főleg private módon. A következő header jó kiindulás lehet:
template<typename T,int n,int m> class my_matrix{
public:
typedef T scalar_type;
/*
my_matrix(const my_matrix& mm){
int i,j;
for(int i=0;i<n;++i){
for(j=0j<m;++j){
matrix[i][j]=mm[i][j];
}
}
}
my_matrix &operator=(const my_matrix &mm){
}
*/
my_matrix(){
int i,j;
for(i=0;i<n;++i){
for(j=0;j<m;++j){
matrix[i][j]=T();
}
}
}
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){
my_matrix t(*this);
int i,j;
for(i=0;i<n;++i){
for(j=0;j<m;++j){
t.matrix[i][j]+=mm.matrix[i][j];
}
}
return t;
}
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;
}
const T & operator()(int i,int j) const{
assert(i<n && j<m);
return matrix[i][j];
}
T & operator()(int i,int j){
return const_cast<T &>(const_cast<const my_matrix *>(this)->operator()(i,j));
}
private:
my_matrix(const my_matrix &a,const my_matrix &b){
int i,j;
for(i=0;i<n;++i){
for(j=0;j<m;++j){
matrix[i][j]=a.matrix[i][j]+b.matrix[i][j];
}
}
}
T matrix[n][m];
};
/*
template<typename T> T operator+(T a, T b){
T c(a);
c+=a;
return c;
}
*/
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;
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*(T A,typename T::scalar_type c){
T t(A);
t*=c;
return t;
}
template<typename T> T operator*(typename T::scalar_type c,T A){
return operator*(A,c);
}Copy ctor-t és értékadó operátort csak akkor kell írnod, ha a scalar_type (a mátrix elemeinek típusa) nem triviálisan másolható. Ezt pl. type trait-tal is ellenőrizheted. A + operator lehet tfv. is, ha nincs más típusról implicit konverzió. Különben free fv.-nek kell lenni. Én most nem írtam 1 paraméteres ctort, így nem lehet implicit konverzió (kivéve, ha más osztálynak van my_matrix-ra konverziós operátora).
-
jattila48
aktív tag
válasz
yossarian14 #2796 üzenetére
A cout << ++j << ": " << fileName << endl; sorban a j honnan jön? Egyébként, ha ez a ciklus lassú, akkor pont ez konzolra kiíró sor lassítja. Oprendszertől (és a runtime librarytól) függően ez meglehetősen lassú tud lenni.
-
jattila48
aktív tag
-
jattila48
aktív tag
A http://nyelvek.inf.elte.hu/leirasok/C++0x/index.php?chapter=5 ELTE jegyzetben a move szemantikánál találtam az alábbi, szerintem hibás példát:
std::vector<int> && function() {
std::vector<int> ret; // ret feltöltése jó sok adattal
return ret;
}Azt akarja bemutatni, hogy hogy kell rvalue referenciát visszaadni, a költséges másolás elkerülése érdekében. Csakhogy a ret lokális változóra vonatkozó referenciát ad vissza, ami megszűnik visszatérés után. Ugyanígy lokális változóra vonatkozó lvalue referenciát, vagy pointert sem szabad visszaadni. Ha mindenképpen a move szemantikát akarja használni, akkor az
std::vector<int> function() {
std::vector<int> ret; // ret feltöltése jó sok adattal
return std::move(ret);
}lenne erre megfelelő. Még jobb, ha egyszerűen érték szerint adja vissza.
std::vector<int> function() {
std::vector<int> ret; // ret feltöltése jó sok adattal
ret;
}A modern fordítók RVO optimalizációt hajtanak végre (se copy, se move ctort nem hívnak), vagy ha ez nem lehetséges, akkor automatikusan a move szemantikát használják (a ret-et std::move nélkül is rvalue-nak tekintik, mert visszatérés után megszűnik). A return std::move(ret) elveszi az RVO optimalizáció lehetőségét, és a költségesebb move szemantikát alkalmazza, ezért ne használjuk. Azt azért furcsállom, hogy egy ilyen súlyos hiba benne maradhat egy ELTE jegyzetben. Egyébként általában is nagyon ritka esetben kell rvalue referenciát visszaadni. SOHA ne adj vissza lokális változóra vonatkozó pointert, lvalue vagy rvalue referenciát!!!
Vélemények? -
jattila48
aktív tag
válasz
Retekegér #2734 üzenetére
A Counter & a fv. visszatérési értékének típusa, vagyis Counter típusra "mutató" referencia. Eddig is így írták, az általad mutatott "ellenpéldában" is. int &rSomeRef = someInt; utasításban az & nem a SomeRef-hez tartozik, hanem az int-hez. Vagyis SomeRef egy int & típusú változó, egy int-re "mutató" referencia.
-
jattila48
aktív tag
válasz
ToMmY_hun #2739 üzenetére
Ha exceptiont dob a konstruktor, akkor nem jön létre az objektum (és így a destruktora sem fog meghívódni). A new lefoglalja a megfelelő tárhelyet, majd mikor a ctor exceptiont dob, fel is szabadítja azt. A factory függvényedben kell lekezelni az exceptiont, és NULL-t (vagy nullptr-t, vagy "üres" smart pointert) visszaadni, ha a konstruktor exceptiont dobott.
class MyClass{
....
};
MyClass * factory(int arg){
MyClass *p=NULL;
try{
p=new MyClass(arg);
}
catch(...){{
return p;
}Az egyszerűség kedvéért írtam raw pointer visszatérő értéket, igazából a factory fv.-nek inkább std::unique_ptr<MyClass> smart pointert célszerű visszaadni.
Egyébként a paraméter ellenőrzést már a factory fv.-ben is elvégezheted, de akkor csak ezzel célszerű objektumot létrehozni, ctor közvetlen hívásával nem, mert az nem ellenőrzi a paramétereket. Ezt úgy lehet elérni, hogy a ctor-t protected-dé teszed, a factory-t pedig az osztály friend-jévé, vagy statikus tfv.-évé.MyClass * factory(int arg){
if(arg>0){
return new(std::nothrow) MyClass(arg);
}
else{
return NULL;
}
} -
-
jattila48
aktív tag
válasz
EQMontoya #2721 üzenetére
Azért az újabb C++ 11 fordítóknak (pl. VS2012) tudtommal tudniuk kell. Főleg az unnamed RVO-t, ami szinte biztosan működik minden "normális" C++ fordítóval.
std::pair<int,int> swap(int x,y){
return std::pair<int,int>(y,x);
}Itt nincs a pair-ből temporális változó (ezért unnamed), ezért szinte biztos , hogy az RVO működik. Tehát körültekintő módon megírva a programot, nyugodtan számíthatsz az RVO-ra. Egyébként kis méretű visszatérő értékek esetén (pl. két int) akkor is a pair célszerűbb, ha netán nincs RVO. Vagyis tényleg ne féljetek a pair/tuple-től, a felvetett problémára szerintem ez a legelegánsabb C++ megoldás.
-
jattila48
aktív tag
válasz
pengécske #2715 üzenetére
Amit te akarsz csinálni, pontosan arra való az std::pair, illetve több visszatérő érték esetén az std::tuple. Ezek ugyan wrapper osztályok, de az STL-ben már készen kapod. Nem kell félni tőle, mert C++ 11-ben az RVO miatt nem kevésbé hatékony mint referenciaként vagy pointerrel átadott output paraméterekben (mint ahogy EQMontoya írta) visszakapni a kívánt értékeket, viszont sokkal átláthatóbb.
"így marad az, h stringbe kodolgatok": na ez az amit ne csinálj! Nagyon nem hatékony, sok hibalehetőséget rejtő, nagyon nem C++ szemléletű amatőr "megoldás".
Ursache: "Nekem még annyi jutott az eszembe, hogy Vector2D": ez tök jó, de mire is? -
jattila48
aktív tag
válasz
pengécske #2713 üzenetére
Nem pontosan értem a kérdésedet. Egy fv. pl. pair-ben adhat vissza két értéket (pl. két int-et), de C++ -ban ez igazából egy db. pair mint visszatérő érték. Ha komplex értéket ad vissza, az sem egy valós és egy képzetes rész float értékként, hanem egy komplex típusú érték, ahol a komplex osztályt te definiálod pl. valós és képzetes rész float típusú adattagokkal:
class komplex{
public:
....
private:
float valos,kepzetes;
};Ahhoz, hogy egy komplex számokat kezelő fv.-t valós számokra is lehessen alkalmazni (ez természetes elvárás), szükség van a valós számot komplex-re konvertáló fv.-re. Ez a komplex osztálynak egyetlen valós paramétert váró konstruktora lesz, amelyet szükség esetén (komplex értéket váró fv.-t valós paraméterrel hívsz) a C++ automatikusan meghív.
Tehát egy fv. egyetlen értéket képes visszaadni, azonban ez lehet alaptípus (int,char,float,...), vagy bármilyen osztály példánya (komplex) (vagy erre mutató pointer illetve hivatkozás). Ugyanolyan nevű fv.-ekből viszont lehet több, ha a szignatúrájuk (paraméter lista elemeinek típusa) különbözik. Ezek C++-ban teljesen különböző fv.-ek, és így különböző értékeket is adhatnak vissza (gondolom ezt tudod), azonban csupán a visszatérési értékük típusa szerint nem lehet így fv.-eket megkülönböztetni (bár a fv. prototípus ekkor is különbözőnek számít).
-
jattila48
aktív tag
válasz
dabadab #2704 üzenetére
Lehet, hogy igazad van, és akkor hasznos külön felhívni a figyelmet a tfv.-ek constságára. Egyébként kár lenne, ha nem mondanák el, hogy a tfv.-ek első paramétere a this, és a név manglálás után semmiben sem különböznek a free függvényektől. Például nem gondolnának sokan olyat, hogy a tfv. bármilyen módon (pl. függvénypointer?) tárolva van az objektumban, mert ez nem igaz, és egy elég lényeges dolog (pl. az objektum mérete nem függ a tfv.-ek számától). Mellesleg az egész const mechanizmus jobban érthető, ha valaki tisztában van azzal, amit írtam (pl. nem const tfv.csak nem const objektumra hívható meg).
-
jattila48
aktív tag
válasz
dabadab #2702 üzenetére
A constság szerintem benne van a kérdező által írt szignatúrában, mivel ez a tagfüggvények első implicit this pointer paraméterére vonatkozik. Tehát nem const tfv. esetén ez a paraméter (a this pointer) T * const típusú, míg const tfv. esetén const T * típusú (T az osztály típusa), ami természetesen különböző szignatúrát jelent.
-
jattila48
aktív tag
BUÉK mindenkinek!
Egy kis agyalgás: Általában rossz ötlet publikusan származtatott osztályban elrejteni az ős valamely tagfüggvényét, mivel ez esetben a származtatott osztályból létrehozott objektum különbözőképpen viselkedik, ha az ős osztályra mutató pointeren/hivatkozáson keresztül hívjuk meg az adott tfv.-t, mint ha közvetlenül. Ez idáig tiszta sor, de mi a helyzet a statikus tfv.-ekkel? A statikus tfv.-ek ugyebár nem az objektumhoz, hanem az osztályhoz kötődnek (vagyis nem függenek/nincsenek hatással egy adott objektum állapotától/ra), így az előző tanács ilyenkor értelmetlen. Sőt, az ilyen osztályok könnyen lehetnek template argumentumok, amikor pontosan az kell, hogy a származtatással létrehozott osztály adott statikus tfv.-e másképp viselkedjen, mint az ős osztályban (leginkább ezért készül belőle származtatott osztály). Mi a véleményetek?
-
jattila48
aktív tag
Sziasztok!
Az alábbi kódrészlet lefordul és le is fut, pedig szerintem nem kéne.
class Q{
private:
friend class P;
static int q(){return 9;}
};
class R : public Q{
//int r(){return Q::q();}
};
class P{
public:
int p(){return R::q();}
};void main(void)
{
P p;
printf("%d\r\n",p.p());
}A probléma az, hogy a P osztály eléri a az R::q-t, holott a q az R ősének (Q-nak) private tagja, és a P a Q-nak barátja, nem az R-nek. A kikommentezett sor természetesen nem fordul le, mert az R számára a private q tag nem elérhető. Úgy tűnik, hogy az R::q() hívás valójában Q::q(), csak nem értem miért.
-
jattila48
aktív tag
válasz
mgoogyi #1921 üzenetére
Ez így is van. A kliens felé biztosított init nem generál exceptiont, hanem bool-lal tér vissza. Viszont ez az init tagobjektumok init-jét hívja, amik generálhatnak exceptiont, de azok itt lekezelésre kerülnek.
Kontruktor-nak meg muszáj exceptiont generálni ha valami hiba történik, hiszen másképp nem tudja jelezni a hibát. A bad_alloc-ot lekezelheti (sőt!), de utána új exceptiont kell generálnia. -
jattila48
aktív tag
válasz
mgoogyi #1917 üzenetére
Biztosan meg lehet oldani másképp is ezt a problémát, pl. ahogy írod. Az én javaslatomnak az az előnye, hogy a konstruktorban nem kell kezelni az exceptiont, sőt az init tfv.-ben sem. Az init tfv. természetesen generálhat exceptiont, de azt lehet kezelni felsőbb szinten, és mikor az objektum kilép a scope-ból a destruktora (illetve a tagobjektumok destruktorai) automatikusan mindig elvegzik a deallokációt. Tehát bármilyen "mélyről" jön egy pointer tag inicializálásával keletkező exception, a végén minden sikeresen allokált pointer tag automatikusan felszabadul.
Nem mondtam, hogy ez lényeges kérdés, viszont szerintem érdekes. Ha nem akarsz, ne válaszolj rá! A kétfázisú inicializálást pedig sok tanulmány ellenzi. Keress rá! -
jattila48
aktív tag
válasz
mgoogyi #1912 üzenetére
Így van, az auto_ptr nem jó tömbre.
Általában nem tanácsolják a kétfázisú inicializálást. Egy olyan helyzetet próbáltam mutatni, ahol mégiscsak célszerű lehet.
Ha a try cathc-et berakom a ctor-ba, ekkor a try-ban auto_ptr-t kéne használni, ami nem jó tömbre.
Az exception-nel az a problémám, hogy ha ctor-ban keletkezik, akkor nem fut le a destruktor (mivel ekkor nem lett teljesen megkonstruálva az objektum), és azok a felszabadítások amiket a destrukdornak kéne elvégezni, nem végződnek el. A kétfázisú inicializálás azon segít, hogy a destruktor mindenképp lefut (mivel a ctor nem dobott exceptiont, hiszen a kritikus inicializálást az init tfv. végzi), és elvégzi a nem NULL pointerek felszabadítását. A ctor-nak természetesen NULL-lal kell inicializálni a szóbanforgó pointereket.
Egyébként éppen tömb inicializálása kapcsán jött ez elő. Remélem így érthetőbb a dolog, persze felhajtást nem szeretnék csinálni, csak megbeszélni, kinek mi a véleménye. -
jattila48
aktív tag
Kétfázisú inicializálás az, amikor az objektumot a konstruktora nem inicializálja teljesen, hanem még egy inicializáló tfv.-t kell meghívni ahhoz, hogy az objektum használható legyen. Akkor szokták használni, ha az inicializálás során virtuális tfv.-t kell meghívni, vagy a konstruktor nem dobhat exception-t. A kétfázisú inicializálást általában kerülendőnek tekintik. Azonban szerintem kifejezetten hasznos is lehet, és nagyban egyszerűsítheti a megfelelő resource kezelést pl. memory leak elkerülésére használható. Ha pl. van egy objektumunk, aminek inicializálása során két memória foglalást végez, előfordulhat hogy az első allokáció sikeres, a második pedig nem. Konstruktorral megoldva ilyenkor auto_ptr-t használhatunk, mert az első pointert fel kell szabadítani, mielőtt a második allokáció sikertelensége miatt a konstruktor exception-t dob. Ilyenkor az objektum destruktora nem fut le, mert csak teljesen megkonstruált objektumra fut le a destruktor, ha az kilép a scope-ból. Na már most kétfázisú inicializálással az objektumot a konstruktora úgy hozza létre, hogy a szóban forgó két pointert NULL-lal inicializálja, majd az inicializáló tfv. elvégzi a szükséges allokációkat. Ha valamelyik allokáció nem sikerül, akkor az init tfv. exception-t dob, vagy false-val tér vissza, de a sikertelen allokáció eredményeként a pointer NULL marad. Az objektum destruktora csak a nem NULL pointert szabadítja fel, a NULL-t békén hagyja.
class A {
public:
A():c(NULL),d(NULL){}
~A();
bool init(int,int);
private:
char * c;
char *d;
}A::~A()
{
if(c!=NULL)delete[] c;
if(d!=NULL)delete[] d;
}bool A::init(int n,int m)
{
c=new char[n];
d=new char;
return true;
}void main(void)
{
try{
A a; //Ez mindenkepp lefut, nem dob exception-t
a.init(10,0x7fffffff); //A::d-nek tul nagy memoriat akarunk foglalni, ezert exception-t dob
}
catch(std::bad_alloc &){
std::cout << "Memory allpc error";
}
//Itt a lefoglalt A::c felszabadult, nincs memory leak
}A lényeg az, hogy a try blokk végén az A destruktora lefut, mert az A a objektum konstruktora mindenképp lefutott. Kicsit olyan ez, mintha az A osztály maga is egy "smart pointer" lenne.
Mit szóltok hozzá? Ugye nem is feltétlenül rossz a kétfázisú inicializálás? -
jattila48
aktív tag
válasz
jattila48 #1873 üzenetére
Ha a Derived **-ról Base **-ra castolást megengedné a fordító, akkor meg lehetne csinálni a következőt:
Derived2 szintén származzon Base-ből.Derived *d=new Derived;
Base **b;
b=&d; //ezt a Derived **-ról Base **-ra castolást nem engedi a fordító, de reinterpret_cast-tal ki lehet erőszakolni
*b=new Derived2; //ez simán megy, mert Derived2 *-ról Base *-ra castolEz után a d pointer egy Derived2 típusú objektumra fog mutatni, holott Derived *-ként van deklarálva. Ez nyilván baj lenne, ezért nem engedi meg ezt a fajta cast-ot a fordító. Azonban más a helyzet pointer tömbökre. Ha b nem Base ** típusú, hanem Base *[], akkor a b=&d értékadást eleve nem lehetne megcsinálni, mivel b nem megváltoztatható (hiszen konstans tömb cím, amit a fordító konstans értékként kezel). Tehát a Derived *[]-ról Base *[]-ra való castolás biztonságos, és esetenként kívánatos is lenne, de sajnos a fordítók nem tesznek különbséget ezek között.
-
jattila48
aktív tag
Azt is lehetne, de így egyszerűbb volt. Igazából a lényegen nem változtat. Ilyenről még nem hallottam, hogy a pointerek mérete különböző lenne (ettől persze lehet), de a Derived **-ról Base **-ra castolás nem is azért veszélyes. Viszont a pointer tömbök közötti castolást nem látom veszélyesnek.
-
jattila48
aktív tag
De, le lehet cserélni a a Derived *[] tömböt Base *[] tömbre, ez történt az átmásolással. A kérdés azonban elvileg is érdekes. Szerintem a Derived *[] tömb Base *[]-ra castolása teljesen legális lehetne, (de a Derived ** Base **-ra castolása már nem), a probléma szerintem az, hogy függvény paraméterként a fordító nem tesz különbséget a pointer és tömb között. Természetesen általában a tömbök közti castolás nem legális, de ebben a példában semmilyen veszélyét nem látom (vagy esetleg tudnál veszélyes példát mutatni?). Egész más a helyzet a Derived **-ról Base **-ra castolással.
-
jattila48
aktív tag
válasz
jattila48 #1868 üzenetére
Ugyanakkor, ha az f függvény Base[] típusú paramétert vár f(Base *), akkor simán meg lehet hívni Derived[] típusú tömbbel, mivel a Derived[] típust Derived *-nak tekinti, a Base[] típust Base *-nak, és az ősosztály pointerére gond nélkül lehet konvertálni. Ez azonban egészen biztosan hibás működést eredményez. Ebből is látszik, hogy a tömb az nem pointer (minden ellenkező hiedlemmel szemben), még ha hasonlóan kezelhető is.
-
jattila48
aktív tag
Sziasztok!
A következő problémára várok megoldási javaslatot:
Van egy függvényem, ami paraméterként Base * pointereket tartalmazó tömböt vár, de szeretném Derived * pointer tömbbel is meghívni. Valahogy így:class Derived : public Base{...}
void f(Base **){...}
Derived *d[10];
....
f(d);A fordító természetesen hibát jelez, hogy nem tud Derived **-ról Base **-ra konvertálni. Ez világos hogy miért veszélyes általában, de nekem nem igazán Base ** típusú az f paramétere, hanem Base *[] tömb, amire teljesen természetes és veszélytelen lenne Derived *[] típusú tömböt konvertálni. reinterpret_cast-tal megoldható, de az elég rondán néz ki. Az a gond, hogy paraméterként átadva a Base *[] típust Base **-nak tekinti, holott nem egészen az. Esetleg valami elegánsabb megoldást tudnátok javasolni? Lehet, hogy a Base *[] tömböt be kéne ágyazni egy Barray osztályba, a Derived *[] tömböt pedig egy ebből származó Darray osztályba, az f-nek Barray * típusú paramétert adni. Ez sem tetszik, akkor már inkább a reinterpret_cast.
Ú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!
- Anime filmek és sorozatok
- Google Pixel topik
- Hálózati / IP kamera
- World of Tanks - MMO
- Mibe tegyem a megtakarításaimat?
- Mielőbb díjat rakatnának a görögök az olcsó csomagokra az EU-ban
- WLAN, WiFi, vezeték nélküli hálózat
- Kerékpárosok, bringások ide!
- GSM / 4G / 5G hálózat erősítő
- HiFi műszaki szemmel - sztereó hangrendszerek
- További aktív témák...
- i7 8700/ 32GB DDR4/ 512GB gen4 SSD/ R5 430 2GBD5/ HP 400G5 SFF/ garancia/ ingyen foxpost
- TUF F15 FX507ZI4 15.6" FHD IPS i7-12700H RTX 4070 32GB 512GB NVMe gar
- AKCIÓ!! i9 9900 , RX 5700 XT , 32GB DDR4 , 512GB NVME , 2TB HDD
- Inspiron 14 7440 2-in-1 14" FHD+ IPS érintő Core 5 120U 16GB 512GB NVMe magyar vbill ujjolv gar
- Garis ZOTAC GeForce RTX 4070 Ti Trinity 12GB GDDR6X videokártya
- Bomba ár! Lenovo Miix 510-12IKB : i5-7G I 8GB I 256GB SSD I 12" Touch I Cam I W11 I Garancia!
- Bomba ár! Fujitsu LifeBook U757 - i3-7GEN I 16GB I 256SSD I 15,6" FHD I HDMI I Cam I W11 I Garancia!
- Bomba ár! HP ZBook Studio G5 - XEON I 32GB I 512SSD I Nvidia I 15,6" 4K DreamColor I Cam I W11 I Gar
- ÁRGARANCIA!Épített KomPhone i5 10600KF 16/32/64GB RAM RTX 3060 12GB GAMER PC termékbeszámítással
- SzoftverPremium.hu
Állásajánlatok
Cég: CAMERA-PRO Hungary Kft
Város: Budapest
Cég: Promenade Publishing House Kft.
Város: Budapest