Ist es akzeptabel, dynamisches Casting für die dynamische Konvertierung innerhalb eines Klassenbaums zu verwenden?

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

  •  26-10-2019
  •  | 
  •  

Frage

Für einen Universitätsauftrag baue ich eine Klassenstruktur auf, die teilweise mehrere Klassen von umfasst Pixel, jeweils mit einem bestimmten Farbraum (z. B. 8-Bit-Graustufen, 24-Bit-RGB usw.).

Der größte Teil der Arbeit wird durch erledigt Image::Base&, was verwendet wird Pixel::Base&s, nicht weiß, was bestimmte Art von Art von Pixel ist auf jeder Seite von jeder Pixel Zuordnungen.

Um die Konvertierung zwischen den unbestimmten Subtypen zu ermöglichen, benutze ich Conversion -Konstruktoren und operator=, über virtuelle Funktionen aus der Basisklasse. Ich habe zwei Optionen gesehen, entweder jede Klasse muss implementiert werden to_Grey8(), to_RGB() und so weiter - die Konversionskonstruktoren machen und operator= Klein auf Kosten für viele separate Konvertierungsfunktionen - oder ich erhalte das Empfangsobjekt die Konvertierung selbst (mit den umgekehrten Folgen).

Aus irgendeinem Grund habe ich mich zunächst mit der letzteren Wahl entschieden.

Das Problem ist, dass das Konvertieren in RGB (z. B.) die internen Werte aus dem anderen Objekt kopieren möchte wenn Es ist zufällig auch ein RGB Base&, Daher habe ich verwendet dynamic_casts prüfen - möglicherweise viele von ihnen, wenn die Anzahl der Subtypen wächst - für jede verschiedene Art von Art von Pixel Das braucht eine besondere Handhabung.

Ich habe das Gefühl, dass dieser Ansatz "schlecht" sein könnte, also:
Ist dies eine korrekte / gültige / sichere Verwendung von dynamic_cast?
Wenn nicht, warum? Und was ist eine vernünftige Alternative, um die gleichen Ziele zu erreichen?


Ich konzentriere mich nur auf operator=(), Hier sind die wichtigen Teile der Definitionen:

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 );
}

Mit den Implementierungen wie folgt:

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;
}
War es hilfreich?

Lösung

Wenn Sie keine absolut Beste Effizienz benötigen, können Sie die Umwandlung in zwei Schritte unterbrechen: Konvertieren Sie den Pixeltyp T1 in einen "universellen Intermediate" -Typ, der die höchste Farbauflösung hat (ich nenne es RGB48), und dann und dann Konvertieren Sie von diesem Typ in T2. Auf diese Weise müssen Sie nur 2*N -Konvertierungsfunktionen anstelle von n^2 -Conversion -Funktionen schreiben. Sie würden mit so etwas enden:

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
}

Wenn alle Pixeltypen TORGB48 () definieren müssen - wird es als abstrakte virtuelle Methode in der Basisklasse deklariert.

Beantwortet auf Ihre ursprüngliche Frage (ist dynamic_cast ok/safe/etc) ?: Manchmal ist es angemessen zu verwenden, aber es wird oft als "Code -Geruch" angesehen. In Fällen, in denen Sie einen echten Multiple -Versand haben, benötigen Sie jedoch eine Operation, die von zwei Typen auf eine Weise abhängt, die kippen in zwei Schritte mit einer mittleren gemeinsamen Darstellung zerlegt werden, dann ist es wirklich die einzige Option. Es gibt andere Fälle, in denen es auch in Ordnung ist. Es ist sicherlich sicher/gültig, es ist nur ein Zeichen für suboptimales Design.

Wenn Sie einen dynamischen Versand verwenden, würde ich empfehlen, ihn auf folgende Weise zu verwenden:

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

(dh dynamic_cast ein Zeiger anstelle einer Referenz - dies wird 0 zurückgegeben, wenn er fehlschlägt, anstatt eine Ausnahme zu werfen.) Auf diese Weise vermeiden Sie den Overhead der Ausnahmehandhabung, und ich finde es auch leichter zu lesen.

Andere Tipps

Es scheint mir, dass die von Ihnen verwendeten Tools (übergeordnet sind, gleichzeitig eine virtuelle Funktion und dynamisches Casting zu machen) nicht gut zu diesem Projekt passen. Wenn Sie einen Pixeltyp in einen anderen konvertieren, wandeln Sie vermutlich potenziell große Bitmaps um. Die Leistung ist in diesem Fall der Schlüssel. Ich würde die Konvertierung eher auf Bitmap als auf einer Pixelbasis implementieren. Wenn das Casting möglicherweise teuer ist, sollte es nicht pro Pixel passieren.

Ich schlage vor, die Merkmale der Sprache zu verwerfen, auf die ich mich oben für eine Weile beziehe. Ich denke, sie verleihen in diesem Fall keinen Wert und machen die Lösung tatsächlich problematisch. Überdenken Sie die Lösung mithilfe primitiverer Programmierstrukturen.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top