È accettabile utilizzare fusione dinamica per converstion dinamica all'interno di un albero di classe?

StackOverflow https://stackoverflow.com/questions/9319235

  •  26-10-2019
  •  | 
  •  

Domanda

Per un incarico all'Università Sto costruendo una struttura di classe che comprende, in parte, diverse classi di Pixel, ciascuna con uno specifico spazio colore (ad esempio 8-bit in scala di grigi a 24 bit RGB, ecc).

La maggior parte del lavoro è fatto da Image::Base&, che verrà utilizzato Pixel::Base&s, quindi non si sa che tipo specifico di Pixel è su ogni lato di eventuali incarichi Pixel.

Quindi, per consentire la conversione tra i sottotipi indeterminati che sto facendo uso di costruttori di conversione e operator=, tramite funzioni virtuali dalla classe base. Ho visto due opzioni, o ogni classe deve implementare to_Grey8(), to_RGB() e così via - rendendo i costruttori di conversione e piccola operator= a costo di avere un sacco di funzioni di conversione separati - o io ottenere l'oggetto ricevente fare la conversione in sé (con il rovescio conseguenze).

Per qualche ragione, ho inizialmente andato con quest'ultimo scelta.

Il problema è che la conversione di RGB (per esempio) vogliono copiare i valori interni da altro oggetto se sembra essere anche un oggetto RGB, ma non può farlo se l'altro oggetto è solo un Base&, così come risultato ho finito per usare dynamic_casts per controllare - potenzialmente un sacco di loro come il numero di sottotipi cresce -. per ogni diverso tipo di Pixel che ha bisogno di un trattamento speciale

Ho la sensazione questo approccio potrebbe essere "cattivo", così:
Si tratta di una corretta / utilizzo valida / sicura di dynamic_cast?
Se no, perché? E che cosa è una ragionevole alternativa per raggiungere gli stessi obiettivi?


Proprio concentrandosi su operator=(), ecco i bit importanti delle definizioni:

class Base
{
public:
    virtual Pixel::Base& operator=( Pixel::Base const& rhs ) = 0;
}

class RGB24 : public Base
{
private:
    unsigned char r, g, b;
public:
    virtual Pixel::RGB24& operator=( Pixel::Base const& rhs );
}

class Grey8: public Base
{
private:
    unsigned char i;
public:
    virtual Pixel::Grey8& operator=( Pixel::Base const& rhs );
}

Con le implementazioni cercando in questo modo:

Pixel::Grey8& Grey8::operator=( Pixel::Base const& rhs )
{
    i = rhs.intensity();
    return *this;
}

Pixel::RGB24& RGB24::operator=( Pixel::Base const& rhs )
{
    try {
        auto castrhs = dynamic_cast<Pixel::RGB24 const&>(rhs);
        r = castrhs.r;
        g = castrhs.g;
        b = castrhs.b;
        return *this;
    } catch (std::bad_cast& e) {}

    //TODO other casting blocks will go here as other Pixel classes are added

    //no specific handler worked, so just effectively greyscale it
    r = rhs.intensity();
    g = rhs.intensity();
    b = rhs.intensity();
    return *this;
}
È stato utile?

Soluzione

Se non avete bisogno di migliore in assoluto-efficienza, allora si potrebbe prendere in considerazione la rottura della conversione in due fasi: convertire il tipo di pixel da T1 a un certo tipo "universale intermedio", che ha la più alta risoluzione a colori (lo chiamerò RGB48) , e poi convertire da quel tipo di T2. In questo modo, avete solo bisogno di scrivere 2 funzioni di conversione * N, piuttosto che N ^ 2 funzioni di conversione. Faresti finisce con qualcosa del tipo:

Pixel::RGB24& RGB24::operator=( Pixel::Base const& rhs )
{
    Pixel::RGB48 rgb48pixel = rhs.toRGB48();
    r = rgb32pixel.r/256; // downsample
    g = rgb32pixel.g/256; // downsample
    b = rgb32pixel.b/256; // downsample
}

Dove tutti i tipi di pixel deve definire toRGB48 () -. È dichiarato come un metodo virtuale astratto nella classe base

In risposta alla tua domanda iniziale (è dynamic_cast ok / sicurezza / etc) ?: A volte è opportuno utilizzare, ma è spesso considerato un "codice di odore." Ma nei casi in cui si ha vera e propria spedizione multipla - vale a dire, avete bisogno di qualche operazione che dipende da due tipi in un modo che non può essere scomposto in due fasi, con un po 'di rappresentanza comune intermedia, allora è davvero l'unica opzione. Ci sono altri casi in cui è OK per fare pure. E 'certamente sicuro / valida, è solo a volte un segno di progettazione non ottimale.

Se fate la spedizione dinamica uso, quindi mi consiglia di usarlo nel seguente modo:

 if (auto casthrs = dynamic_cast<Pixel::RGB24 const>(&rhs)) {
     r = castrhs->r;
     g = castrhs->g;
     b = castrhs->b;
     return *this;
 }

(vale a dire, dynamic_cast di un puntatore, invece di un riferimento - questo restituirà 0 se non riesce, piuttosto che un'eccezione.) In questo modo si evita il sovraccarico di gestione delle eccezioni, e trovo più facile da leggere, anche .

Altri suggerimenti

Mi sembra che gli strumenti in uso (ignorando eguali, facendo equivale a una funzione virtuale e la colata dinamico) non sono una buona misura per questo progetto. Se si sta convertendo un tipo di pixel ad un altro, allora presumibilmente si sta convertendo potenzialmente bitmap di grandi dimensioni. Le prestazioni sono chiave in questo caso. Sarei implementando la conversione su un per bitmap piuttosto che una base per pixel. Se il casting è potenzialmente costoso allora non dovrebbe accadere per pixel.

propongo di scartare le caratteristiche del linguaggio che mi riferisco a sopra per un po '. Penso che non stanno aggiungendo valore in questo caso e in effetti stanno facendo la problematica soluzione. Ripensare la soluzione utilizzando strutture di programmazione più primitive.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top