Tässä isompaa projektia C++:lla tehdessä tuli moniperinnän puitteissa vastaan pieni asia.
Oletetaan, että meillä on seuraavanlainen abstrakti luokka:
class A { private: int _a; protected: A() : _a(0) {} void aseta(int a) { _a = a; } public: static A* staattinen(); virtual A* metodi() const = 0; };
Metodin staattinen
pitää palauttaa uusi instanssi tästä luokasta, erilaisella _a
:n arvolla. Jos se implementoidaan näin:
A* A::staattinen() { A* a = new A; a->aseta(3); return a; }
Niin ei onnistu, koska A on abstrakti luokka, jolloin sille ei voida varata muistia.
Tätä pohtiessani tulin tulokseen, että templaateillahan tämä onnistuisi esimerkiksi näin:
template <class Peritty_T> class A { // ... public: static Peritty_T staattinen(); // ... };
Jolloin implementaatio tulisi muotoon:
template <class Peritty_T> Peritty_T A<Peritty_T>::staattinen() { Peritty_T a; a.aseta(3); return a; }
Tämä tarkoittaisi siis sitä, että kun tämä luokka peritään niin se tapahtuisi näin:
class B : public A<B> { /* ... */ };
Pienen Googletuksen jälkeen havaitsin, että tälle tavalle on ihan nimikin, CRTP. Tässä tavassa ei tarvitse käyttää rumia castauksia, eikä dynaamista muistia.
Tosin tässä tavassa voi tehdä vaikka mitä typerää, kuten:
class B : public A<int> { /* ... */ };
Tein kuitekin tuon instassin luomisen siten (se on jätetty tästä pois), että tuollainen ei onnistu ja siitä tulee käännösaikainen virhe. Muuten tämä vaikuttaa ihan hyvältä tavalta, mutta kuitenkin jokin siinä minua vielä epäilyttää ja siksi tämän viestinkin tänne lähetin. Kertokaa siis mielipiteenne.
Itsekin taisin pyöriä saman homman parissa jonkun aikaa pari viikkoa sitten, kun mietin singleton-luokkaa jonka voisi suoraan periä. Päädyin muistaakseni samaan ratkaisuun.
trilog kirjoitti:
... Tein kuitekin tuon instassin luomisen siten (se on jätetty tästä pois), että tuollainen ei onnistu ja siitä tulee käännösaikainen virhe. Muuten tämä vaikuttaa ihan hyvältä tavalta ...
Tämä tiivistää oman käsitykseni template-kikkailuista aika hyvin. Vaikuttaa hyvältä, mutta jos yrität oikeasti soveltaa näitä johonkin, niin olet ihan suossa.
No tuota...
(Sarjassa vähän hölmöjä kysymyksiä)
Sulla on (täysin) abstraktiluokka, josta ei voi luoda instanssia kuten c++ syntaksi sanoo, niin miksi yrität luoda tästä abstraktistaluokasta instanssin?
Keskittyisin ennemmin muokkaamaan sitä suunnitelmaa joka sanoo, että abstraktista luokasta täytyy luoda instanssi, ennenkuin yrittäisin väkisin luoda sen instanssin, minkä ei pitäisi edes olla mahdollista (meitin mielipide).
Vai olisiko antaa jotain lisävinkkiä mitä ajat staattinen-metodin käytöllä takaa?
Tarkoitus on luoda peritystä luokasta se instanssi, ei siis siitä abstraktista luokasta. Voisihan sen toki toteuttaa jokaiseen perittävään luokkaan erikseen, mutta kun staattinen metodi ei voi olla virtuaalinen, ja tulisi koodin toistoa muutenkin.
Enkä sitä edes ajatellut toteuttaa noin, tämä vain tuli mieleen asian yhteydessä, jolloin päätin kysellä hieman mielipiteitä täältä asiasta. Menee nimittäin sen verran kikkailuksi, että on parempi varmaan keksiä jotakin muuta.
trilog kirjoitti:
Tarkoitus on luoda siitä peritystä luokasta se instanssi, ei siis siitä abstraktista luokasta...
Ei nyt heti hoksaa että mitä koodia joutuu kopioimaan mutta, jos tarkoituksena oli vain countterin luominen A-luokan instansseille niin määrittele A:n konstruktori jotenkin näin
A() { static int l=0; _a=l++; };
Tällöin jokainen A:n instanssi on numeroitu eri numerolla.
Määrittele metodi-staattinen template:na jotenkin näin
template<class T>static A* staattinen(){return (A*)new T;};
Ja sitten kaivat vielä jostain kuinka määritellään/tarkistetaan, että tyyppi T periytyy luokasta A (ajonaikainen typeid-operaattori? vaiko oliko tuohon joku suora inherits-määre, ei muista enään).
Auttoiko ongelmaan?
Tuohan tekee oikeastaan saman, mitä tuo edellä mainitsemani CRTP-malli. :)
Yritetäänkö tässä ratkaista jotakin itseaiheutettua ongelmaa?
Jos tarkoitus on luoda A:sta johdetun luokan B olio, miksei ihan simppeli new B käy?
Templaatit ovat kyllä oikein näppäriä ja käytännöllisiä. Suurimmat vaikeudet niiden kanssa tuntuvat tulevan silloin, kun niillä yritetään ratkaista vääriä ongelmia. Voi niitä kyllä käyttää vaikka varmistamaan käännösaikana, että luokka B on johdettu A:sta. Ei siihen typeid:tä tarvita.
Aihe on jo aika vanha, joten et voi enää vastata siihen.