"Rejestrowanie" klas pochodnych

0

Witam,
Mam świadomość, iż moje pytanie może wydać się dosyć dziwne - bo w zasadzie sam jakoś mam co do tego mieszane uczucia - ale podobno da się coś takiego dość składnie zrobić w C++ie.
O co chodzi (upraszczam tu cały problem, co by nie rozpisywać się odnośnie całego zadania* a jedynie jego części która sprawia mi kłopot)...
Wyobraźmy sobie, że mamy pewną klasę np. taką

 
class Glowna{
 virtual foo(){ cout << "Unknown"; }
};

Po tej klasie dziedziczy ileś tam klas pochodnych z których każda przedefiniowuje metodę foo().

Do tego zestawu klas chcemy dopisać program, który będzie tworzył vector typu Glowna, taki że jego size jest równy ilości klas pochodnych a każdy jego element jest obiektem innej klasy pochodnej (choć zapisanym pod typem wskaźnik do pamięci bazowej) a następnie wywoływał na każdym elemencie tego wektora metodę foo.

W czym problem?

Jak stworzyć wektor w taki sposób, by dodanie do programu kolejnej klasy pochodnej dało się zamknąć w klamerkach definicji tej klasy. Czyli innymi słowy żeby wpisać w miejsce wielokropka w poniższym kodzie takie "magiczne coś" by vector gdzieś tam w programie zawierał również element nowo powstałej klasy (tu: NowkaSztuka):

 
class NowkaSztuka{
...
};

Niestety koncepcja programowania jest narzucona* i nie można jej zmienić.
Wszelkie wskazówki mile widziane.

Z góry dziękuję i pozdrawiam
fiedukow

    • to jest bardzo mały fragment całego zadania, ale właściwie jedyny który sprawia mi kłopot (właściwie to nie wiem jak do tego podejść) - cała reszta mimo że dosyć złożona jest w kategoriach "do zrobienia" i raczej sobie z tym poradzę, stąd nie widzę sensu w zanudzaniu tym forumowiczów.
0

A znasz język który potrafi takie cuda robić?
Możesz napisać Fabrykę (patrz wzorzec AbstractFactory) który będzie się zajmował tworzeniem obiektów.

0

PHP ale tam jest eval :>
C++ też się podobno da - tak twierdzi doktor inżynier prowadzący zajęcia z programowania obiektowego na politechnice warszawskiej - więc ufam, że ma rację...

0

W takim razie skorzystaj ze wzorca projektowego FactoryMethod i powinno ci to wystarczyć.

0

W Javie daloby sie (teoretycznie) przeiterowac po wszystkich klasach z okreslonych pakietow i sprawdzic, czy dziedzicza po okreslonej klasie - w ten sposob uzyskaloby sie liste klas dziedziczacych po danej.

0

Nie mam do dyspozycji Javy :-( A szkoda.

0

Przekombinowane, ale działa. Przeniesienie do C++ nie powinno być trudne (jak zdążę to przeniosę):
Inna sprawa że pewnie to sztuka dla sztuki, bo po co komu takie mieszanie ;)

edit: A, no właśnie, żeby nie było że .NET to gorszy od Javy - tu też możemy przeiterować po klasach z określonych pakietów żeby sprawdzić czy dziedziczą po określonej klasie. :P

class Program
{
    static void Main(string[] args)
    {
        new Bbb();
        new Ccc();
        new Bbb();

        Aaa.DoFoos();

        Console.ReadLine();
    }
}

abstract class Aaa
{
    static HashSet<FooDlg> foos;

    public Aaa(FooDlg myFoo)
    {
        if (foos == null)
            foos = new HashSet<FooDlg>();

        if (!foos.Contains(myFoo))
            foos.Add(myFoo);
    }

    public static void DoFoos()
    {
        foreach (FooDlg foo in foos)
            foo();
    }
}

class Bbb : Aaa
{
    public Bbb()
        :base(Foo)
    {
    }

    public static void Foo()
    {
        Console.WriteLine("foo in bbb");
    }
}

class Ccc : Aaa
{
    public Ccc()
        : base(Foo)
    {
    }

    public static void Foo()
    {
        Console.WriteLine("foo in ccc");
    }
}

delegate void FooDlg();
0

Nie do końca rozumiem ten kod bo z "dot not" nigdy nie miałem szerszej styczności, ale z tego co widzę, to są tam linijki:

new Ccc();
new Bbb();

A takich właśnie linijek poza kodem samych klas chciałbym uniknąć.
Pozdrawiam

1

C++ to nie Python, klasy nie są tutaj obiektami... Dlatego wydaje mi się, że bez utworzenia obiektów klas dziedziczących się nie obejdzie.
(Zawsze możesz to zrobić "na brzydko":
class B : public A { /* ... */ } b; // i konstruktor B czyni magię ;) )

0

To że się nie obejdzie bez tworzenia obiektów jest raczej pewne - ale podobno da się to jakoś zautomatyzować. Chociaż ja o osobiście nie widzę takiej możliwości :-(
To co zaproponowałeś przed chwilą to już krok w dobrą stronę, ale jeszcze nie to (wiem, że to nie jest zbyt ładne - ale najbliższe temu co mam zrealizować).

0

Singletony?

0

Mi się wydaje, że nie da się dokładnie zrobić tego w sposób jaki chce autor, tj. by rejestracja zawierała się w definicji klasy. Ale da się zrobić, by zawierała się w pliku z naszą klasą! Wykorzystamy do tego globalne, statyczne obiekty, których konstruktory zrobią całą magię.

Mamy klasę bazową Owoc, po niej dziedziczą Pomidor i Gruszka. Nasz rejestr będzie się znajdował w pliku z klasą Owoc:
Owoc.hpp

#pragma once

#include <iostream>
#include <vector>

using namespace std;

class Owoc
{
public:
	Owoc();
	virtual string KimJestes();
};

vector<Owoc*>& Rejestr();

Owoc.cpp

#include "Owoc.hpp"

Owoc::Owoc()
{
}

string Owoc::KimJestes()
{
	return "Owocem";
}

vector<Owoc*>& Rejestr()
{
	static vector<Owoc*> *rejestr = new vector<Owoc*>();
	return *rejestr;
}

Pomidor.hpp

#pragma once

#include <iostream>

#include "Owoc.hpp"

using namespace std;

class Pomidor : public Owoc
{
public:
	Pomidor(void);
	string KimJestes();
};

Pomidor.cpp

#include "Pomidor.hpp"

class DodajPomidor
{
public:
	DodajPomidor()
	{
		Rejestr().push_back(new Pomidor());
	}
};

static DodajPomidor dodajPomidor;

Pomidor::Pomidor(void)
{
}

string Pomidor::KimJestes()
{
	return "Pomidorem";
}

Gruszka jest identyczna jak Pomidor.

Program.cpp

#include "Owoc.hpp"

#include <iostream>

using namespace std;

int main()
{
	if(Rejestr().size() > 0)
	{
		vector<Owoc*>::iterator it;
		
		for(it = Rejestr().begin(); it != Rejestr().end(); it++)
			cout << "jestem " << (*it)->KimJestes().c_str() << endl;
	}

	return 0;
}

Wynik:

Jestem Pomidorem
Jestem Gruszka

Dzieje się tak dlatego, że, według standardu, mamy pewność, że globalne, statyczne obiekty w głównym namespace zostaną stworzone przed wywołaniem metody main. Nie mamy jednak pewności który utworzy się najpierw, stąd musieliśmy użyć globalnej funkcji, żeby zwrócić nasz rejestr. A w ten sposób zostanie utworzony wtedy, gdy zostanie użyty po raz pierwszy.

0

O tym rozwiązaniu też myślałem, ale nie jest to dokładnie to, czego mi potrzeba :-(

1 użytkowników online, w tym zalogowanych: 0, gości: 1