2016-12-27 2 views
3

Ich bin mir ziemlich sicher, dass es nicht, aber ich werde auf jeden Fall fragen ...
Hier ein Beispiel: lassen Sie uns sagen, dass ich eine Klasse Elipse genannt:C++ ist es möglich, ein Mitglied nicht zu erben?

class Elipse 
{ 
private: 
    double d_m_lAxis; // the length of the large Axis of the elipse 
    double d_m_sAxis; // short axis 
//Point is a struct of 2 doubles (x and y) 
    Point P_m_focal1; //focal point 1 
    Point P_m_focal2; //focal point 2 
protected: 
    Point P_m_center; 

public: 
    Elipse(Point _P_lowerLeft,Point _L_upperRight) 
    { 
    //draw elipse in a rectangle, just like in paint 
    } 
} 

Jetzt möchte ich Circle erben von Elipse (weil Circle ein Elipse mit beiden Axis von gleicher Länge ist):

class Circle : public Elipse 
{ 
private: 
    double d_m_radius; 
public: 
    Circle(Point _P_lowerLeft,double _d_diameter) : 
    Elipse(_P_lowerLeft,/*get the upper ight*/ 
     Point(_P_lowerLeft.x + _d_diameter,_P_lowerLeft.y + _d_diameter)) 
     { 
     // draw a circle in a square, just like in paint 
     } 
} 

So, jetzt habe ich die Klasse ich wollte, und es hat nicht den Zugang zu einem t er private Mitglieder der Elipse, die in Circle redundant werden würde.
Allerdings, soweit ich weiß, sind diese Mitglieder noch geerbt und die Kindklasse hat einfach keinen Zugriff auf sie. Dies macht die Klasse unnötig groß und es ist auch möglich, auf diese Mitglieder über von Elipse geerbte Funktionen zuzugreifen, sofern sie nicht überschrieben werden.

Gibt es einen Weg zu nicht erben ein Mitglied überhaupt?

+5

Dies ist ein Missbrauch der Vererbung denke ich, ein Kreis kann eine Ellipse mathematisch sein, aber nicht unbedingt programmatisch – StoryTeller

+0

StoryTeller i s richtig: Ihre eigenen Bedenken zeigen deutlich, dass für Sie die Verwendung von Vererbung in der Anwendungsdomäne falsch ist. – user268396

+5

Wenn Sie nicht alle Eigenschaften einer Klasse vollständig erben können, sollten Sie Ihr Design überdenken – WhiZTiM

Antwort

2

Die mathematische Formulierung "ein Kreis ist eine Ellipse, wo [...]] Eine Ellipse Für jeden Kreis“ sollte auf die weitere Art bewusst umformuliert werden‚ gibt es in der [...], dass die gleiche Menge von Punkten‘ beschreibt.

, die mit daran, Ihre Umsetzung sollte ermöglichen die Erstellung von Ellipse Objekte aus Circle Objekte, aber nicht polymorph Substitution
eine solche Möglichkeit ist eine explizite Konvertierung Betreiber:..

class Elipse 
{ 
    // as you defined 
}; 

class Circle 
{ 
    // As you defined 
    explicit operator Ellipse() 
    { 
    Point lower_left, upper_right; 
    // calculate the two points 
    return Ellipse(lower_left, upper_right); 
    } 
}; 
+0

Hmm das ist eigentlich eine sehr gute Erklärung. Und eine gute Lösung auch. – AlanKalane

3

Was Sie mit dieser Vererbung erreichen möchten, verstößt gegen das Liskov-Substitutionsprinzip. In deinem Fall sollte Ellipse eigentlich keine Basis für Circle sein. Siehe weitere Details z.B. Wikipedia:

https://en.wikipedia.org/wiki/Liskov_substitution_principle#A_typical_violation - hier ist es auf sehr ähnliches Beispiel (Rechteck und Quadrat)

+2

verwenden https://en.wikipedia.org/wiki/Circle-ellipse_problem erklärt es mit einem Kreis und einer Ellipse. – emlai

+0

Ich glaube nicht, dass es das Liskov-Prinzip verletzt, da Circle keine Methoden erbt, die es als Kreis ungültig machen würden. In der Tat, auch wenn es so etwas wie stretchX (Doppel val) mit dem richtigen Implementierung erben haben Sie das Problem, indem sie auch in der anderen Richtung strecken, vermeiden könnten oder das Objekt beendet und die Rückkehr eine Ellipse – AlanKalane

1

Ein Kreis ist ein besonderer Fall von elipse, kein Subtyp erklärt, basiert, so würden Sie nicht tun müssen, Dies. Stattdessen initialisiere deine Elipse mit dem gleichen Wert für jede Achse.

Über die Vererbung sind private, geschützte Mitglieder für diesen Zweck da. Sie mögen private, die geerbt werden. Die Klasse wird aufgrund der privaten Mitglieder der Basisklasse nicht groß sein. Der Compiler wird den endgültigen Code optimieren.

+0

wird es? Ist das bestätigt? Wenn es keine Möglichkeit gibt, ein Mitglied zu verwenden, wird es dann weg optimiert? Wird die Erinnerung frei werden? – AlanKalane

+1

Dies kann in der Regel nicht gerechtfertigt sein, aber jeder guter optimiser nicht verwendeten Code entfernen würde, solange es tatsächlich nicht verwendet wird und kann niemals durch öffentliche oder geschützte Methoden verwendet werden. Allerdings finde ich es auch besser, nur eine Ellipsenklasse mit einer isCircle() -Methode zu verwenden. Sie können auch eine Ellipse angeben, um das Achsenlängenverhältnis bei der Größenänderung zu sperren und den Kreis so zu definieren, dass keine Größenänderungen möglich sind, die das Verhältnis ändern. Offensichtlich brauchen Sie kein Radiusfeld mehr. – Juan

3

Dies macht die Klasse in der Größe unnötig groß und es ist auch möglich die Mitglieder über Funktionen von Elipse geerbt zuzugreifen, es sei denn, sie sind außer Kraft gesetzt. Gibt es eine Möglichkeit, ein Mitglied NICHT unter zu erben?

Obwohl, können Sie Ihr Design zu überdenken, Sie Vorteil von EBO nehmen können (leeren Basisklasse-Optimierung), wird es keine Größe Kopf sein.

haben eine leere Art wie:

class EllipticalType {}; 

Dann wie Ihre Ellipse definieren

class Elipse : public EllipticalType 
{ 
private: 
    double d_m_lAxis; // the length of the large Axis of the elipse 
    double d_m_sAxis; // short axis 
//Point is a struct of 2 doubles (x and y) 
    Point P_m_focal1; //focal point 1 
    Point P_m_focal2; //focal point 2 
protected: 
    Point P_m_center; 

public: 
    Elipse(Point _P_lowerLeft,Point _L_upperRight) 
    { 
    //draw elipse in a rectangle, just like in paint 
    } 
} 

Dann tun Sie:

class Circle : public EllipticalType 
{ 
      private: 
      double d_m_radius; 
      public: 
    Circle(Point _P_lowerLeft,double _d_diameter) : Elipse(_P_lowerLeft,/*get the upper right*/Point(_P_lowerLeft.x + _d_diameter,_P_lowerLeft.y + _d_diameter)) 
     { 
     // draw a circle in a square, just like in paint 
     } 

} 

Sie können auch gemeinsame Elemente extrahieren und sie in EllipticalType

+0

Ja, das ist wahrscheinlich der einfachste Weg, dies zu tun. Ich mag einfach nicht die Tatsache, dass a) du eine zusätzliche Klasse brauchst und b) der Kreis keine Ellipse ist. Trotzdem ist es wahrscheinlich der nächste, den ich bekommen kann. – AlanKalane

2

Nein. Sie können nicht. Vererbung in C++ impliziert eine gewisse Binärkompatibilität in der Praxis.

Eine Problemumgehung folgt. Aber zuerst:

Ein Kreis ist keine Ellipse.

Um genauer zu sein, ist ein veränderbarer Kreis in vielen nützlichen Weisen keine veränderbare Ellipse. Es gibt Operationen, die Sie an einer veränderbaren Ellipse ausführen können, deren Nachbedingungen nicht mit den gleichen Operationen auf einem veränderbaren Kreis übereinstimmen.

Zum Beispiel angenommen, es gibt get/set Haupt/Nebenachse. Eine sinnvolle Nachbedingung auf der Hauptachse besteht darin, dass kleinere Achsen auf einer veränderbaren Ellipse unverändert bleiben. Während Sie die Haupt-/Nebenachse auf einem Kreis setzen können, kann diese Nachbedingung nicht halten!

Nun ist ein unveränderlicher Kreis eine unveränderliche Ellipse. Erst wenn Sie mit der Bearbeitung beginnen, tritt das kovariante/kontravariante Problem auf. Dieses Problem ist nicht auf Rechteck/Quadrat, Ellipse/Kreis, Automobil/LKW-Stil "Spielzeug" OO beschränkt; Eine bearbeitbare Liste von Derived ist keine bearbeitbare Liste von Base, sondern in gleicher Weise. Eine bearbeitbare Liste von Base akzeptiert beliebig Basistyp; Das Gleiche gilt nicht für eine Liste von Derived.

Auf der anderen Seite ist eine unzerstörbare Liste von Derived eine unveränderliche Liste von Base.


Zurück zu Ihrer ursprünglichen Frage, erben die Implementierungen wie diese möglicherweise unklug. Sie können jedoch Daten aus der Logik von der Schnittstelle trennen.

Dann erben Schnittstelle und Logik, während Daten nicht zugeordnet bleiben.

In C++ kann dies mit Vorlagen und CRTP getan werden. Wenn Sie CRTP nicht kennen, ist das folgende unzuverlässig und unlesbar. Ich garantiere keine Gegenleistung.

template<class Self> 
struct ellipse_math { 
    Self* self(){ return static_cast<Self*>(this);} 
    Self const* self() const { return static_cast<Self const*>(this);} 

    double get_area() const { /* math using self()->get_major() and self()->get_minor(); */ } 
}; 
struct circle_state { 
    double m_radius; 
    double get_major() const; 
    double get_minor() const; 
    double get_radius() const; 
}; 
struct ellipse_state { 
    double m_major, m_minor; 
    double get_major() const; 
    double get_minor() const; 
}; 
struct I_ellipse { 
    virtual double major()const=0; 
    virtual double minor()const=0; 
    cirtual double area()const=0; 
    virtual ~I_ellipse(){}; 
}; 
struct I_circle:I_ellipse { 
    virtual double radius()const=0; 
}; 
template<class Self, class I=I_ellipse> 
struct impl_I_ellipse : I { 
    Self* self(){ return static_cast<Self*>(this);} 
    Self const* self() const { return static_cast<Self const*>(this);} 
    virtual double major()const final override { return self()->get_major(); } 
    virtual double minor()const final override { return self()->get_minor(); } 
    virtual double area()const final override { return self()->get_area(); } 
}; 
template<class Self, class I=I_circle> 
struct impl_I_circle : impl_I_ellipse<Self, I> { 
    Self* self(){ return static_cast<Self*>(this);} 
    Self const* self() const { return static_cast<Self const*>(this);} 
    virtual double radius()const final override { return self()->get_radius(); } 
}; 

struct ellipse : 
    ellipse_state, 
    impl_I_ellpise<ellipse>, 
    ellpise_math<ellipse> 
{}; 
struct circle : 
    circle_state, 
    impl_I_circle<circle>, 
    ellpise_math<circle> 
{}; 

Das ist ziemlich lächerlich in diesem einfachen Fall, aber ich habe mit nicht verwendeten Ellipse Felder in meinem Kreis Umsetzung zu vermeiden, und bekam die ellpise Logik wiederzuverwenden.

Das obige wurde auf einem Telefon getippt, enthält also wahrscheinlich tpyos.

Ehrlich, wenn ich so weit gehen würde, würde ich den ganzen Weg zum SBO-Typ löschen Ellipse-ähnliche und kreisförmige Objekte mit freien Funktionen für den Versand gehen. Weil die Vererbung oben und vtable mehr in die Quere kommen als sie helfen.

+0

C++ Maestro @Yakk ... Ich habe die Fehler fixiert und machte es [hier] kompilieren (http://coliru.stacked-crooked.com/a/313d3c4cdfe4d09a). Erlaubnis, den Code in Ihrer Antwort zu korrigieren? .. :-) – WhiZTiM

+0

Wow, das ist beeindruckend, aber wie du gesagt hast, sieht es lächerlich und verworren aus. – AlanKalane

+0

@whiz sicher, das ist toll – Yakk

Verwandte Themen