Template / Dziedziczenie - Przechowywanie wielu instancji klasy w "Bankach" obiektów.
Ostatnio zmodyfikowano 2019-03-02 09:57
rafallauterbach Temat założony przez niniejszego użytkownika |
Template / Dziedziczenie - Przechowywanie wielu instancji klasy w "Bankach" obiektów. » 2019-03-02 05:52:50 Witam, od dłuższego czasu unikałem jak ognia próby nauki templateów, ale dzisiaj stwierdziłem, że ich użyję, bo trzeba się ich nauczyć. Mam jednak problem. Chciałbym wybierać element po "std::string name" [nazwie] elementu w tablicy, którą sobie z templateami utworzyłem. Nie wiem jakie zapytanie w przeglądarce wpisać, bo strasznie ciężko mi to wytłumaczyć, a jak patrzę na kursy całe z templateami, to nie mogę ich zrozumieć. Pomyślałem, że powinno się chyba dać wymóc na klasie, która jest używana, aby ta miała określone właściwości... Ale nie wiem jak. Rozumiem, że używam w moim templacie klasy T, ale nie mogę zrobić jej tak, żeby dziedziczyła z innej klasy, bo jak czytałem już na forach template'y i dziedziczenie się nie lubią i są w zupełnie innych momentach w kompilowaniu, więc nie mogą ze sobą współpracować. Wrzucę kod, może będzie jaśniej niż z mojego tłumaczenia tutaj : / template < class T > Bank < T >::Bank() { T * first = nullptr; T * last = nullptr; } template < class T > T * Bank < T >::get_by_name( std::string name ) { first->find_by_name( name ); } template < class T > T * Bank < T >::get_first() { return first; } template < class T > T * Bank < T >::get_next( T * obj ) { return obj->next; } template < class T > void Bank < T >::remove_by_name( std::string removed_name ) { if( first != nullptr ) { T * start = first; first = nullptr; last = nullptr; start->rm_by_name( removed_name ); } else ; } template < class T > void Bank < T >::add( T * obj, std::string n ) { obj->name = n; obj->next = nullptr; if( last != nullptr ) { last->next = obj; } else { first = obj; } last = obj; } template < class T > void Bank < T >::rm_by_name( std::string removed_name ) { rm_by_name( next ); if( name == removed_name ) delete this; else { next = first; if( next == nullptr ) last = this; first = this; } }
Konkretnie - nie mogę użyć "name" z klasy T, bo nie wiem jak wytłumaczyć kompilatorowi, że T MUSI mieć name, żeby mogło wystąpić w templacie. Coś jak pure virtual dla metod, jeśli nie będzie "name" to nie można tego użyć w tym templacie. |
|
jankowalski25 |
» 2019-03-02 07:52:49 template < class T > Bank < T >::Bank() { T * first = nullptr; T * last = nullptr; } |
Taki kod nie ustawia składowych klasy, tylko zmienne lokalne, które po opuszczeniu konstruktora przestają istnieć. nie mogę użyć "name" z klasy T, bo nie wiem jak wytłumaczyć kompilatorowi, że T MUSI mieć name, żeby mogło wystąpić w templacie |
Nie musisz robić niczego poza odwołaniem się do odpowiedniej metody/składowej. Jeśli klasa T nie będzie miała potrzebnych metod/pól, to kod się po prostu nie skompiluje. Szablony są rozwijane w czasie kompilacji. Prosty przykład: #include <iostream> #include <string>
template < class T > class Foobar { T t; public: void setName( const std::string & name ); void printName() const; };
template < class T > void Foobar < T >::setName( const std::string & name ) { t.name = name; }
template < class T > void Foobar < T >::printName() const { std::cout << t.name; }
struct Field { int foo; };
struct Named { std::string name; };
int main() { Foobar < Named > foobar; foobar.setName( "Hello world!" ); foobar.printName(); } Co więcej, typ też musi być zgodny. Czyli przykładowo poniższa struktura również się nie skompiluje: struct Field { int name; }; Oczywiście zgodność typu wymaga tylko tyle, aby użyty typ był dozwolony w podanej operacji. W przykładzie wyżej nie musi to być koniecznie std::string , równie dobrze można użyć na przykład poniższej klasy Wrapper: class Wrapper { std::string str; public: Wrapper( const std::string & str ) : str( str ) { } friend std::ostream & operator <<( std::ostream & out, const Wrapper & wrapper ) { return out << wrapper.str; } };
struct Field { Wrapper name; Field() : name( "cokolwiek" ) { } }; |
|
rafallauterbach Temat założony przez niniejszego użytkownika |
» 2019-03-02 09:15:15 //quote nie działa /[quote/] nie mam pomysłu jak cytować Nie musisz robić niczego poza odwołaniem się do odpowiedniej metody/składowej. Jeśli klasa T nie będzie miała potrzebnych metod/pól, to kod się po prostu nie skompiluje. Szablony są rozwijane w czasie kompilacji. //---- quote Ok, tego właśnie nie wiedziałem i pomogło mi to. Nie wiem czy w ogóle potrzebuję template do rozwiązania mojego problemu , ale gdybym po prostu dziedziczył z interfaceu do banku, który by służył do łatwego przechowywania/przekazywania wskaźników np : class IBank { protected: std::string name; IBank * next; }; class Texture : public IBank {...}
to trochę też nie osiągnąłbym tego o co mi chodzi chyba. Potrzebuję, żeby wszystko co jest w banku miało nazwę i następny element koniecznie - i to miałbym spełnione. Przy czym następny element powinien móc wskazać tylko na subtyp taki sam, jak subtyp w któtym jest przechowywany, lub nullptr. I idąc za ciosem gdybym tylko w klasie textura dodał pole "std::string name" i pole "Texture* next" wszystko mógłbym doprowadzić do działania (chyba). [ w klasie teksture używam banku tekstur ] Ale miałbym pole "name" i "next" w "Texture.h" i każdej innej klasie, która chciała by mieć swój / swoje bank(i). Czy gdybym chciał rozwiązać to dziedziczeniem, mógłbym jakoś z interfaceu IBank odwołać się do konkretnej instancji Texture, która go używa? Wydaje mi się to niemożliwe i trochę absurdalne (skąd IBank miałby wiedzieć gdzie jest użyty). Dlatego pomyślałem o Template(-ach), ale w nich nie ma możliwości spakowania tego ładnie w klasę, z której bym dziedziczył z tego co czytałem. template < class T > class Bank { public: Bank(); T * get_by_name( std::string ); T * get_first(); T * get_next( T * ); void remove_by_name( std::string ); void add( T *, std::string ); private: void rm_by_name( std::string ); T * next; T * first; T * last; };
|
|
pekfos |
» 2019-03-02 09:50:36 To co w ogóle ma robić ta klasa..? Czemu nie używasz po prostu std::map<>? bo jak czytałem już na forach template'y i dziedziczenie się nie lubią i są w zupełnie innych momentach w kompilowaniu, więc nie mogą ze sobą współpracować. |
Bzdury. |
|
rafallauterbach Temat założony przez niniejszego użytkownika |
» 2019-03-02 09:57:20 Klasa ma mi w ogóle pozwolić próbować wołać obiekty po nazwie. Nie ważne czy istnieją, czy nie - np w jednym miejscu dodaję i ładuję tekstury a w innym miejscu chcę móc wczytać dowolną teksturę, bez wcześniejszego uzyskiwania pointera do niego używając nazwy.
Wcześniej zrobiłem już taką klasę, ale była specyficzna dla konkretnego typu i trzymałem poszczególne elementy w zwykłym wektorze.
Spróbuję użyć map, nie wiem jak działają, ale pewnie próbuję wynaleźć koło, bo nie wiem jak nazwać mój problem dla googla :/
Edit : mapy, to chyba dokładnie to o co mi chodziło. Chyba usunę kompletnie moją klasę, bo jest niepotrzebna, w zasadzie tłumaczy już tylko moje stare nazwy na nazwy z <map>.
Zmienione "w 99 miejscach" i wszystko działa tak jak chciałem! Map spenił się idealnie. |
|
« 1 » |