Műveletek osztály összes példányán

 ( Kuvik | 2009. július 27., hétfő - 21:53 )

Egy osztály összes példányának egy (nem statikus) adattagján szeretnék végrehajtani egy műveletet, amihez egy olyasmi függvényre lenne szükségem, amit nem egy adott példányra, hanem magára az osztályra hívhatok.

Alapesetben ilyenek ugyan az osztály static tagfüggvényei, azokkal viszont nem tudom az egyes példányok !static adattagjait módosítani.

Megoldható ez valami hasonló formában?

Hozzászólás megjelenítési lehetőségek

A választott hozzászólás megjelenítési mód a „Beállítás” gombbal rögzíthető.

Direkt nyelvi támogatás ehhez nem hiszem hogy lenne.

Esetleg csinálsz egy factory static osztályt ami nyomonköveti milyen példányokat gyártott és amikor kell, akkor a factoryban meghívod a static függvényt.

Egy másik megoldás hogy maguk a példányok regisztrálják magukat egy másik static osztályban (ami csak azért létezik hogy nyomonkövesse a példányokat), amikor létrejönnek (constructor). Ennek az az előnye is meglehet hogy ha példányokat megszüntetsz menet közben, akkor azok kiregisztrálhatják magukat (destructor), azaz bármikor hívod a függvényed, mindig az összes példányra meg tudod hívni.

Kicsit képszerűbben:
static class Tracker() {
static void register(ToBeTracked instance);
static void deregister(ToBeTracked instance);
}

class ToBeTracked() {
constructor( Tracker.register(this); )
destructor( Tracker.deregister(this); )
}

Valamivel butább formában, de ilyesmin gondolkodtam már, de egyelőre ez túl bonyolult nekem, főleg, hogy ennyit nem ér az egész. Valószínűleg workaroundolom példányonkénti hívogatással, mivel nincs sok példány. Csak azt hittem, van valami triviális megoldás. Azért köszönöm, ha valamiért mégse tudnám kikerülni, akkor ilyesmivel próbálkozok.

Ez tökugyanaz mint amit a másik emberke mondott és tökugyanaz amit te mondasz, hogy véighivogatod a példányokat :-) Annyi előnye lehet hogy automatikus, de persze feladattól függ, mire kell :-)

igen, közben már észrevettem, csak már nem tudtam szerkeszteni. :)

Azzal együtt már nagyjából értem, hogy hogy működik.

Szerintem ez a megoldás egyszerű, mint a faék. Kb 20-30 sorban meg lehet valósítani. Ennél egyszerűbben nem nagyon fog menni.

Pl. a konstruktorban regisztrálsz az osztály egy statikus (privát) konténerében az adattagra egy mutatót. Utána bármely statikus függvényből hozzáférsz az összes példány összes adattagjához. Viszont az esetleges konkurenciára nagyon kell figyelni. Ill. nem elfelejteni a destruktorban deregisztrálni.

--
The Net is indeed vast and infinite...
http://gablog.eu

:-)

Ezt nem egészen értem. Az odáig rendben, hogy egy statikus mutatót használahatok az adattag eléréséhez, de akkor még azt nem értem, hogy egy függvényhívással hogyan tudom módosítani az összes példány adattagjait.

Ha jól értem, akkor a leírásod csak arra megoldás, hogy hogyan érjem el az adattagot a statikus függvényből.

Pl úgy, hogy a static osztályod ami nyomonköveti a példányokat tartalmaz egy static fgv-t amit meg fogsz hívni amikor módosíani akarod a tagokat. Amikor azt meghívod, az végigdarálja az addig összegyűlt pointereket és módosítod ami ott van. Mondjuk én fgv-en keresztül módosítanám, nem közvetlenül, de kinek a pap kinek a c++ :-)

jaaa, most látom, hogy ez gyakorlatilag ugyanaz, mint az első hsz. :)

Ha már így pingpongozunk, akkor ide írom: igen, ugyanaz :-D

Azt még nem értem, hogy pontosan hova gyűlnek a pointerek (ill. miért darálja végig a stat. fv.), mert, ha jól értem, a pointer a tracker osztály statikus adattagja kell, hogy legyen, ami viszont szintén csak egy példányban létezik.

Mivel egy statikus (privát) változóban tárolod a hivatkozásokat, bármely statikus tagfügvényből el tudod érni. Azaz A< T>::change(); a keresett "egy függvényhívás". Persze elhagyhatod a típusparamétert.

#include < list>

template class<_T>
class A { 
private:
 static std::list<_T*> members;

 _T x;

public:
 A() { members.push_back(&x); }
 ~A() { members.remove(&x); }

 static void change() { /* members-en keresztul el tudod erni es meg is tudod valtoztatni az osszes x-et */ }
};

--
The Net is indeed vast and infinite...
http://gablog.eu

Jár a túrórudi a házifeladat megoldásáért :-)

ha csak házi volna, nem igényeskednék ennyit :P

Ja, hogy list van benne :) Erre írtam korábban, hogy "gondolkodtam ilyesmin csak butábban", de ezek szerint mégsem volt annyira rossz elképzelés :) Azthittem létezik valami még kompaktabb forma is, bár, így a template class-al már elég tömör és tetszetős (én máshogy próbáltam).

Egyébként köszönöm, működik :)

#include <list>
#include <algorithms>
#include <functional>

class A { 
private:
 static std::list<A*> objects;

 void changeMe();

public:
 A() { objects.push_back(this); }
 ~A() { objects.remove(this); }

 static void change() { for_each(objects.begin(),objects.end(),mem_fun(&A::changeMe); }
};

Szerintem ez így szebb, általánosabb...

"...handing C++ to the average programmer seems roughly comparable to handing a loaded .45 to a chimpanzee."
-- Ted Ts'o

Ha igényeskedni kell, akkor inkább java ;-)

Hát őőőő... Inkább nem kezdek meddő vitába. :)

"...handing C++ to the average programmer seems roughly comparable to handing a loaded .45 to a chimpanzee."
-- Ted Ts'o

Szerintem nekem szánta, csak mellément :)

Amúgy szerintem annyiban igaza van, hogy Javaban a nyelvi elemek lehetővé tesznek egy-két plusz dolgot, amitől szebb lehet a kód, csak kár, hogy ennek az árát a JRE-nél általában csúnyán meg kell fizetni.

Egyébként szerintem már a C++-nál is gondot okozhat a sok absztrakciós réteg (bár, nem tudom mennyire képesek optimalizálni a fordítók). Legszívesebben C-ben dolgoznék, de nem vagyok annyira gyakorlott, hogy ugyanazt a funkcionalitást ugyanolyan vagy még jobb minőségben C-ben előállítsam, mint most C++-ban. A ráfordított időről meg már nem is beszélve.

Ez még okosabb, de:
- nem biztos, hogy a for_each egy megfelelő feldolgozási stratégia
- konstruktorban veszélyes this-t használni, míg a member változóról már biztosan tudhatjuk, hogy inicializálva van (pl. mert mi inicializáljuk egy sorral előbb, minthogy push_back()-elünk egy hivatkozást rá)

--
The Net is indeed vast and infinite...
http://gablog.eu

csak elmenti a pointert, nem dereferalja, miert veszelyes?

Az nem is veszélyes, viszont az objects-nek még nem biztos, hogy lefutott a konstruktora. Viszont így már ezzel sem lehet probléma:

std::list<A*>& objects() { static std::list<A*> ls; return ls; }

Ha pedig nem frankó az O(n) -es remove, akkor lehet használni intruzív listát (ahol az előre és hátra mutató pointerek magában az objektumban vannak) így az insert/remove mindkettő O(1) lesz.

"Az nem is veszélyes, viszont az objects-nek még nem biztos, hogy lefutott a konstruktora."

De, biztos. (Persze kell a std::list<A> A::objects; az osztályhoz tartozó cpp-be...) Különböző forrásfájlokban definiált statikus adattagok között nincs előre meghatározott sorrend, de fordítási egységen belül van.
Azaz az osztály statikus adattagját lehet használni az osztály fv-eiből...

"...handing C++ to the average programmer seems roughly comparable to handing a loaded .45 to a chimpanzee."
-- Ted Ts'o

Persze kell a std::list<A> A::objects; az osztályhoz tartozó cpp-be.

Úgy már igen, de jobb defenzívnek lenni, szerintem; ezért nem is tartom célszerűnek statikus adattagot használni, pont az említett dolog miatt.

"Úgy már igen, de jobb defenzívnek lenni, szerintem"

Anélkül linkelési hiba van, tehát nagy baj nem történhet.

A privát statikus adattagok veszélytelenek.

"...handing C++ to the average programmer seems roughly comparable to handing a loaded .45 to a chimpanzee."
-- Ted Ts'o

"A privát statikus adattagok veszélytelenek."

... kivéve persze
1. a jól ismert statikus inicializációs crash veszélyt: http://www.parashift.com/c++-faq-lite/ctors.html#faq-10.12
2. windows DLL-eknél a betöltődéskor nem kívánt mellékhatásokat (DLL betöltésekor nem szabad más DLL-t betölteni, de ez a statikus objektumok miatt néha nem kikerülhető, és szép crash-hez vezethet)

Szóval statikus adattagot/változót inkább soha ne használjunk, helyette inkább a fenti linken is leírt függvényt, ami csak igény esetén, a megfelelő pillanatban példányosítja az objektumot.

Szerintem az 1-es a "privát statikus adattagok" esetében nem áll fenn.
(Kivéve persze, ha egy statikus osztálynak van privát statikus adattagja, de ez nyilván nem a statikus adattag hibája...)

A win dll megint más tészta...

"...handing C++ to the average programmer seems roughly comparable to handing a loaded .45 to a chimpanzee."
-- Ted Ts'o

Végülis hülyeséget írtam, de consider this:

A::objects az egyik tu-ban van definiálva,
egy másik osztály statikus példánya pedig egy másik tu-ban.

A kettes tu-ban definiált objektum friend-je A-nak, és a konstruktorból próbál hozzáférni az A::objects-hez.

Nyilván ezzel a listával nem a legjobb példa, de ennyit a private és a veszélytelenségről. Előfordul, hogy kénytelen az ember
a) vagy egyazon tu-ba pakolni a statikus adattagok definícióját, vagy
b) függvényként definiálni őket, és majd a hívások sorrendje eldönti az inicializációt.

Én az egységesség okán a b)-t tartom preferálandónak, szinte minden esetben.

"másik osztály statikus példánya pedig egy másik tu-ban"

Ne legyen statikus osztály. Azt mindenki tudja, hogy a statikus globális objektum maga a gonosz.
Én csak annyit mondtam, hogy nem kell még a privát statikus adattagokat is körbebástyázni...

"...handing C++ to the average programmer seems roughly comparable to handing a loaded .45 to a chimpanzee."
-- Ted Ts'o

"nem biztos, hogy a for_each egy megfelelő feldolgozási stratégia"
Hipotetikus problémára bőven jó lesz. :)

"konstruktorban veszélyes this-t használni, míg a member változóról már biztosan tudhatjuk, hogy inicializálva van (pl. mert mi inicializáljuk egy sorral előbb, minthogy push_back()-elünk egy hivatkozást rá)"

Már miért lenne veszélyes? Semmit nem csinálok vele, csak letárolom. Thread-ek nélkül semmi gond nem lehet (nem több mint a tagra mutató mutatónál), és nem látom miért lenne biztonságosabb egy félig inicializált osztály tagjára mutató mutató, mint magára az objektumra mutató mutató. (Többszálú esetben szvsz egyik megoldás sem alkalmazható ebben a formában...) Arról nem is beszélve, hogy mi van, ha nem csak egy tagot szeretnék toszogatni...

"...handing C++ to the average programmer seems roughly comparable to handing a loaded .45 to a chimpanzee."
-- Ted Ts'o