Metody wirtualne vs rzutowanie w dół (dynamic_cast)

0

Cześć.

Sytuację przedstawia poniższy pseudokod:

class Bazowa {
protected:
   int jakiespoleWspolne1;
   string jakiespoleWspolne2;
   //itd
};

class Pochodna1 : public Bazowa
private:  
  int mojepole;
public:
  setMojePole(int mojepole);
  int getMojePole();
}

class Pochodna2 : public Bazowa
{
private:  
  int costam;
  string costam2;
public:
  setCostam(int c);
  setCostam2(string c2);
  int getCostam();
  string getCostam2();
}

Jest taka sytuacja: mam klasę bazową z kilkoma polami oraz klasy pochodne (które mają swoje pola, różnych typów, w różnej ilości). I teraz mam pytanie: czy lepiej zastosować funkcje wirtualne (i wypisać te wszystkie funkcje wirtualne - przeważnie settery/gettery - dla wszystkich funkcji pochodnych) w klasie bazowej czy zaoszczędzić na czasie i zrobić taki myk:

    Pochodna1 pochodna;
    pochodna.setMojePole(3445);

    Bazowa * bazowa = &pochodna; //konwerujemy do typu bazowego
    
    zrobCos(bazowa); //funkcja zrobCos przyjmuje jako parametr wskaźnik na bazową

    //i teraz zeby dobrac sie do pola (czy też metody) klasy pochodnej trzeba przecastować
    Pochodna * zPowrotem = dynamic_cast<Pochodna*>(bazowa);

    cout<<zPowrotem->getMojePole(); //wyswietli 3445

Wydaje mi się że dla moich celów ta metoda z dynamic castem jest lepsza ponieważ każda klasa pochodna ma swoje własne pola, które są inne niż w innych klasach pochodnych (ale muszą mieć kilka pól i metod wspólnych z klasy bazowej) i lepiej jest zrobić tak niż wypisywać kilkanaście (dziesiąt) metod wirtualnych. Dane te przesyłam przez socketa, więc i tak identyfikuje typ obiektu przez pole przesłane przed danymi właściwymi w postaci int (np. 1 - Pochodna, 2 - Pochodna2 itd.)

0

Oba pomysly to jakis WTF, moze powiedz po prostu troche wiecej szczegolow o tym do czego Ci to potrzebne.

Btw. jest prosty myk z przesylaniem gdzies klasy:

class ISerializable {
public:
  virtual char* Serialize(/* char* buffer */) = 0;
};
class A : ISerializable { // ...

Zamiast sie cackac w jakies gettery i settery :P

0

W Qt chyba tak nie zserializuje. Mam sporo roznych klas do przeslania i szukam jakiejs dobrej metody. Po drugiej stronie mam tylko ciag danych, musze wyczaic jaki jest ich typ, zdeserializowc z powrotem do klasy wysciowej.

3

Podejść do problemu jest wiele.
Jeśli to mówimy o Qt to można skorzystać z meta danych i zrobić serializację i deserializację "automagicznie".
Jeśli nie korzystamy z meta danych to posiadanie metody wirtualnej coś ala:

class Bazowa {

public:
    virtual ostream &printToStream(ostream &out) const = 0;
};

może być bardzo praktyczne, jeśli trzyma się dane w jakiejś polimorficznej strukturze.
W wielu przypadkach metoda wirtualna nie jest konieczna.

0

@MarekR22, właśnie kombinowałem z czymś takim. Język strasznie rzuca mi kłody pod nogi. Wymyśliłem jak to fajnie i elegancko zrobić. Chciałem zrobić metody wirtualne przeciążania operatorów << i >>. Gdy wyślę wiadomość:

Pochodna pochodna;
pochodna.cos("345");
send(pochodna) //void send(Bazowa * bazowa);

to gdy w funkcji send zserializuję:

out << bazowa

to powinno mi "przeskoczyć" do definicji przeciążania operatrora w klasie pochodnej. Niby pięknie, ale jak się okazuje, nie da się stworzyć wirtualnej metody dla przeciążania operatorów << oraz >> bo okazuje się że trzeba je zadeklarować jako zaprzyjaźnione a konstrukcja

friend virtual void...

jest zabroniona. Jak to obejść (przypominam że moje klasy pochodne się bardzo roznia, wiec tworzenie metod wirtualnych w klasie bazowej do kazdej metody klas pochodnych chyba mija sie z celem)?

2

Stworzyc wirtualna metode na wzor tego co napisal @MarekR22 i wywolywac ja w operatorze<<

1

No zrób sobie jakiąś wirtualną metodę odpowiedzialną za serializację. W operatorze ją po po prostu wywołuj - przekazujesz referencję, więc polimorfizm zadziała. Przecież o tym pisał już @MarekR22 wyżej.

// Ojej troche się spóźniłem.

0

@n0name_l, @Endrju
Ale czy tak właśnie nie zrobiłem? :

//bazowa
class Message
{
public:
    Message();
    virtual ~Message();

    void receiver(qint16 receiverId);

    quint8 type() const;
    quint16 length() const;
    quint16 receiver() const;

protected:
    quint8 _type;
    quint16 _length;
    quint16 _receiver;

    void type(qint16 type);

public:
    virtual QDataStream & operator<<(QDataStream&, Message*);
    virtual QDataStream & operator>>(QDataStream&, Message*);
};
//jedna z pochodnych
class Ping : public Message
{
public:
    Ping();
    ~Ping();

    virtual QDataStream & operator<<(QDataStream &, Message*);
    virtual QDataStream & operator>>(QDataStream &, Message*);
};

Oczywiście tutaj wyrzuci błąd "Too many arguments" (z jednym argumentem w ogóle nie zadziała, wiadomo), który po zguglowaniu sugeruje żeby definicje wyrzucić poza klasę do funkcji zaprzyjaźnionej. Ale wtedy stracę wirtualność.

2

Zrob oddzielna wirtualna metode i wywoluj ja w operatorze.

0

Bardzo sprytne - spróbuję. Czy jest to najlepsze wyjście w moim przypadku?

1

w 99% przypadków najlepiej jest zdefiniować normalną nazwaną metodę, a następnie użyć ją w definicji operatora (operator powinien się składać z 1-2 linijek).
Operator dla mojego przykładu ma wyglądać tak (jest poza jakąkolwiek klasą):

ostream& operator<<(ostream &out, const Bazowa &wartosc) {
    return wartosc.printToStream(out);
}

Żeby cię jeszcze oświecić (namotać w głowie).
Klasa bazowa może pełnić rolę interface'u, czyli robisz taki nagłówek:

class IPrintableClass {
public:
    virtual ostream &printToStream(std::ostream &out) const = 0;
};

ostream& operator<<(ostream &out, const IPrintableClass &wartosc) {
    return wartosc.printToStream(out);
}

A potem, możesz robić takie klasy:

class JakasKlasa : public QObject, public IPrintableClass {
Q_OBJECT
public:
    explicit JakasKlasa(QObject *parent);

public:
   ostream &printToStream(std::ostream &out) const;
};
0

Tak, myslę ze to rozumiem :) Nie wpadłem na to żeby wywołać metodę wirtualną z środka funkcji zaprzyjaźnionej :) Dzięki Wam za pomoc!

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