Liste di elementi eterogenei

di il
10 risposte

Liste di elementi eterogenei

Il mio problema è di creare una lista di puntatori a una classe virtuale, diciamo OggettoBase. gli oggetti reali sono tutti derivati da OggettoBase.

fintanto che i metodi virtuali sono senza argomenti, del tipo:
virtual void *metodoCheFaQualcosa()const = 0
tutto sembra funzionare correttamente; viene chiamato il metodo reimplementato nelle sottoclassi.

se invece definisco metodi con argomenti, del tipo:
virtual void *metodoCheFaQualcosa(OggettoDerivato1 const &oggetto)const = 0
non riesco a far chiamare il metodo corretto se non testando prima l'oggetto e facendo cast di qualche tipo.

Mi chiedo se esistano soluzioni un po' più eleganti.

Grazie per qualunque indizio.

10 Risposte

  • Re: Liste di elementi eterogenei

    Vediamo se ho capito bene, hai bisogno che la funzione virtuale nella classe base accetta un parametro del tipo OggettoDerivato1 ? Perché esattamente hai bisogno di passare un "OggettoDerivato1" alla funzione virtuale, mi sembra che cerchi di complicare troppo il codice, puoi essere più preciso nel quello che vuoi esattamente ottenere
  • Re: Liste di elementi eterogenei

    Supponiamo, per esempio, che la classe virtuale sia FigurePiane; ogni tipo di figura avrà i suoi metodi specifici. Ma supponiamo poi di dover fare operazioni generiche su una lista di FigurePiane: per sesmpio trovare l'area intersezione di tutte le figure della lista. A questo punto mi verrebbe da procedere utilizzando un metodo virtuale di FigureGeometriche (o forse meglio n metodi) che poi verrà interpretato a coppie di elementi nelle classi derivate.
    In pratica mi aspetto di poter chiamare su ogni figura un metodo del tipo:

    triangolo.intersezione(triangolo)
    triangolo.intersezione(quadrato)
    triangolo.intersezione(cerchio)
    ...
    quadrato.intersezione(triangolo)
    quadrato.intersezione(quadrato)
    quadrato.intersezione(cerchio)
    ......

    mentre quadrato.intersezione() non mi rappresenterebbe niente
    e nemmeno quadrato.intersezione(figuraGeometrica).
  • Re: Liste di elementi eterogenei

    Okay ho capito cosa vuoi fare. Per forza da qualche parte devi fare un cast da base* a derived*, se no il compilatore non puo' sapere quale metodo vuoi chiamare.Comunque non penso sia il design piu' "bello", perche' ogni volta che aggiungi un nuovo elemento alla gerarchia dovrai andare a modificare tutti gli elementi che fanno parte di essa.
    Io personalmente sposterei tutta la logica che controlla per l'intersezione (nell'esempio che hai fatto tu) in una classe che non fa parte della gerarchia, dove saranno definite tutte le possibili intersezioni. Questo sempre se non sei obbligato dal design corrente ad avere una funzione virtual per ogni elemento.
  • Re: Liste di elementi eterogenei

    Beh, il mio concetto di classe virtual è proprio di fornire il nucleo fondamentale del progetto (gerarchia) obbligando tutte le derivate a fare determinate cose. L'idea di spostare tutto in una classe al di fuori della gerarchia sa troppo di programmazione funzionale mentre vorrei rimanere il più possibile aderente al paradigma OOP.
    Il mio dubbio nasce dal fatto che altri linguaggi hanno classi astratte o anche interfacce pure che si adattano tranquillamente al tipo di problema ma, purtroppo, a quanto mi dici, il c++ non prevede qualcosa di completamente analogo.

    Ho pensato che, tutto sommato, mi conviene scrivere nella classe virtuale, un metodo del tipo:

    FigurePiane *intersezione(FigurePiane figura)
    {
    if (typeid(figura) == typeid(Triangolo))
    {
    return this->intersezione(static_cast<const Triangolo*>(&figura));
    }
    else if (typeid(figura) == typeid(Quadrato))
    {
    return this->intersezione(static_cast<const Quadrato*>(&figura));
    }
    ...
    ...
    }
    Il vantaggio sarebbe di scrivere una cosa così brutta solo nella classe virtuale e inoltre la coerenza rimane tutta nelle mani del programmatore ma solo in questa classe (più o meno tutte le altre dimenticanze sarebbero segnalate in fase di compilazione).

    Forse utilozzando opportunamente auto si potrebbe pensare a qualcosa che elimini la possibilità di dimenticarsi un cast...
  • Re: Liste di elementi eterogenei

    Solo perche' sposti tutta la logica delle interesezioni fuori dalla gerarchia non significa che magicamente il tuo codice non segue piu' il paradigma OOP.
    Anzi ti aiuta solo a semplificare il tuo codice, perche' per prima cosa il codice che hai postato tu devi duplicarlo per ogni elemento della gerarchia creando cosi codice duplicato e piu' possibilta' di fare errori. Se invece lo fai con una classe esterna che si occupa di controllare per le intersezioni il codice per le interzioni lo devi scrivere solo una volta, anche nel design che proponi tu puoi fare in modo che implementi il controllo solo una volta pero' dovrai passare a sinistra ed a destra "this". Seconda cosa, visto che vuoi usare il paradigma OOP chiediti un triangolo per funzionare correttamente deve per forza conoscere tutta la struttura della gerarchia di cui fa parte ? Alla fine ogni singola classe della gerarchia avra un interface pubblico, se puoi implementare la logica di controllo per le varie intersezioni in base al interface pubblico delle classi perche' non implementare i vari
    controlli come funzioni non membro oppure come membro ad una classe fuori della gerarchia ?
    P.S Non ho mai detto che C++ non supporta le classi astratte, forse non le supporta nel modo in cui vuoi tu pero' le supporta( vedi funzioni puramente virtuali ).
    P.S2 Nella funzione che hai scritto fai object slicing all'inizio !!!
  • Re: Liste di elementi eterogenei

    Scusa la mia ignoranza ma non so cos'è object slicing.

    Mi si pone un nuovo problema nel momento in cui utilizzo shared_ptr.
    normalmente potrei scrivere:
    OggettoBase *obj1 = new OggettoDerivato(...)

    Ma sembra che non si possa scrivere:
    std::shared_ptr<OggettoBase > obj1 = std::make_shared<OggettoDerivato>(...)

    Del resto:
    auto obj1 = std::make_shared<OggettoDerivato>(...)
    produrrebbe un puntatore a oggetto derivato , non a OggettoBase
  • Re: Liste di elementi eterogenei

    L'object slicing e' quando cerchi di assegnare ad una variabile di tipo "Base" un'altra variabile di tipo "Derived", in poche parole tagli completamente tutto cio' che rende la variabile originale derived. Per evitare l'object slicing devi accettare parametri o via pointer o via reference.

    Il codice che hai scritto con make_shared e' giusto, l'ho appena provato su godbolt. Per caso hai dimenticato l'header <memory> ?

    Certo che produrrebbe un puntatore ad un oggetto derivato, l'auto fa solo type deduction non puo' sapere che tu vuoi creare un puntatre di tipo "Base" che punta ad un oggetto di tipo "Derived", per fargli dedure il tipo corretto dovresti dargli una mano cosi
    auto a = static_cast<std::shared_ptr<Base>>(std::make_shared<Derived>());
  • Re: Liste di elementi eterogenei

    In effetti faccio come dici, ma dopo aver verificato di poterlo fare, o intendi che sarebbe comunque un errore di sostanza?

    sul make_shared avevo incluso <memory> probabilmente il problema è altrove....per ora ho abbandonato i tentativi con gli shader_ptr, ci ritornerò più avanti (o magari più avanti includeranno un garbage collector )

    Grazie comunque delle delucidazioni e se hai dei consigli generali per rendere un po' più sicuri i normali puntatori li prenderò volentieri in considerazione (pensavo per esempio a controlli prima di fare delete su puntatori che magari vengono già eliminati dall'uso di liste std....o cose così)
  • Re: Liste di elementi eterogenei

    @piolo se incappato involontariamente ( ) in uno dei problemi della programmazione ad oggetti

    Quello che ti servirebbe, e che esiste in pochissimi linguaggi, e non in C++, e' la possibilita' di definire dei multimethods o, in alternativa, usare il multidispatch.

    http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2003/n1529.html
    http://www.stroustrup.com/multimethods.pd
    https://en.wikipedia.org/wiki/Multiple_dispatc

    Potrebbe essere l'occasione buona per provare ad utilizzare questa libreria:

    https://github.com/jll63/yomm1
    https://www.codeproject.com/Articles/650288/Open-Multi-Methods-for-Cplusplu.

    Comunque, in questo particolare caso, una soluzione potrebbe essere la seguente:

    TUTTE le tue figure geometriche possono essere rappresentate da un POLGONO (compreso il cerchio: semplicemente lo rappresenti come un poligono regolare con 10/20/100/1000 lati, a tua discrezione), che fara' da classe BASE per TRIANGOLO, QUADRATO, CERCHIO, ..

    Quindi, le operazioni di UNIONE, INTERSEZIONE, DIFFERENZA tra due qualunque figure geometriche vengono calcolate direttamente dalla classe base POLIGONO.

    Se ci pensi, IN OGNI CASO devi ragionare in termini di poligoni, perche' l'intersezione di due quadrati, ad esempio, NON E' DETTO che ritorni un quadrato!

    Altra considerazione: NON TI SERVONO delle classi specifiche per il TRIANGOLO, il QUADRATO o il CERCHIO, ma SOLO dei factory methods (metodi statici definiti nella classe POLIGONO) a cui passi i parametri specifici per un TRIANGOLO/QUADRATO/CERCHIO e creano un oggetto di tipo POLIGONO.

    Nota/1: la programmazione funzionale E TUTTA UN'ALTRA STORIA !!!!!!!!!!!!!

    Nota/2: la tua classe POLIGONO, in realta', sara' decsamente complicata per i seguenti motivi:

    1) alcune operazioni possono generare una figura geometrica composta da piu' parti: ad esempio l'unione di due quadrati che non si intersecano
    2) devi gestire non solo poligoni CONVESSI, ma anche CONCAVI (con delle rientranze)
    3) peggio ancora, i tuoi poligoni potrebbero avere dei BUCHI

  • Re: Liste di elementi eterogenei

    Caspita, mi stai proponendo roba ad alto livello per me... non so se svrò mai tempo di approfondire tanto.
    Il mio esempio sulle intersezioni di figure voleva servire solo ad inquadrare un problema generale (e immagino elementare per voi)... puoi rivedere la domanda con insiemi di oggetti operando sui quali si ottengono ancora oggetti dell'insieme.

    Comunque grazie, magari in due o tre vite riesco a leggere anche le tue proposte
Devi accedere o registrarti per scrivere nel forum
10 risposte