Template / Dziedziczenie - Przechowywanie wielu instancji klasy w "Bankach" obiektów.
Panel użytkownika
Nazwa użytkownika:
Hasło:
Nie masz jeszcze konta?
Zarejestruj się!

Template / Dziedziczenie - Przechowywanie wielu instancji klasy w "Bankach" obiektów.

AutorWiadomość
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 : /

C/C++
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; // unsafe solution (!)
        first = nullptr; // TODO :
        last = nullptr; // change it completly...
        start->rm_by_name( removed_name );
    }
    else
        ; // there was no objects
   
}
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; // if last object exist it always have nullptr at its next
}
template < class T >
void Bank < T >::rm_by_name( std::string removed_name )
{
    rm_by_name( next ); // start all... from end to start
    if( name == removed_name )
         delete this;
    else
    { // rearange it, we go from back to start, so we pick new "first" item every time and set "next" for previous
        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.
P-174093
» 2019-03-02 07:52:49
C/C++
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:
C/C++
#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<Field> foobar; // błąd kompilacji
    Foobar < Named > foobar; // ok, "Named" ma pole "name"
    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:
C/C++
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:
C/C++
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" )
    { }
};
P-174094
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 :
C/C++
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.
 
C/C++
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; //chciałbym next dla każdego T, pomyślałem, że teraz jest ono wspólne dla wszystkich obiektów w danym banku.
   
    T * first;
    T * last;
   
    //  std::vector <T* item>;
};
P-174095
» 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.
P-174096
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.
P-174097
« 1 »
 Strona 1 z 1