Frage

Ich lerne C ++ und ich habe mich gefragt, ob ich einen Einblick in die bevorzugte Methode erhalten könnte, um binäre Operatoren zu schaffen, die an Fällen von zwei verschiedenen Typen arbeiten. Hier ist ein Beispiel, das ich gemacht habe, um meine Bedenken zu veranschaulichen:

class A;
class B;

class A
{
    private:
        int x;

    public:
        A(int x);

        int getX() const;

        int operator + (const B& b);
};


class B
{
    private:
        int x;

    public:
        B(int x);

        int getX() const;

        int operator + (const A& A);
};


A::A(int x) : x(x) {}

int A::getX() const { return x; }

// Method 1
int A::operator + (const B& b) { return getX() + b.getX(); }


B::B(int x) : x(x) {}

int B::getX() const { return x; }

// Method 1
int B::operator + (const A& a) { return getX() + a.getX(); }


// Method 2
int operator + (const A& a, const B& b) { return a.getX() + b.getX(); }

int operator + (const B& b, const A& a) { return a.getX() + b.getX(); }


#include <iostream>

using namespace std;

int main()
{
    A a(2);
    B b(2);

    cout << a + b << endl;

    return 0;
};

Wenn ich unter den beiden Typen Symmetrie haben möchte, ist welche Methode der beste Ansatz im obigen Code ist. Gibt es mögliche Gefahren bei der Auswahl einer Methode gegenüber der anderen? Variiert dies mit dem Rückgabetyp? Bitte erkläre! Vielen Dank!

War es hilfreich?

Lösung

Der beste Weg ist zu definieren (außerhalb der beiden Klasse) int operator+ (const A& a, const B& b), und machen Sie es bei Bedarf eine Freundschaft beider Klassen. Darüber hinaus definieren

int operator+(const B& b, const A& a) {return a + b;}

Um es symmetrisch zu machen.

Andere Tipps

Das große Risiko bei diesem Ansatz besteht darin, dass Menschen dazu neigen, + als symmetrischer Operator zu wahren. So wie dies geschrieben wird, ist es nicht (es sei denn, Ihre Implementierungen sind gleich).

Zumindest sollten Sie + als externer binärer Operator (nicht als Mitglied) überlasten und dann mehrmals mit dem Überlastung des Überladens spielen.

Sie müssen jedoch vorsichtig sein, um sicherzustellen, dass nichts mehrdeutig wird.

Können Sie erklären, was Sie versuchen zu tun? Ich kann mir nicht viele Fälle von verschiedenen Typen vorstellen, in denen es sinnvoll ist, die symmetrischen heterogenen Operatoren zu haben.

Das Hauptargument für Methode 2 besteht darin, dass Sie in beiden Operanden eine implizite Typumwandlung erhalten, nicht nur das zweite. Dies könnte Verwirrung irgendwo auf der ganzen Linie retten.

Apropos, Ihr Beispielcode definiert implizite Conversions von int zu a und von int zu b, über die 1-Arg-Konstruktoren in beiden Klassen. Dies könnte später zu Unklarheiten führen. Aber wenn Sie das "explizit" für Kürze ausgelassen haben, fair genug.

Ich stimme der Warnung von Uri zu: Wenn Sie dies tun, schreiben Sie möglicherweise eine API, die andere verwirrend empfinden. Wie kommt es, dass ein Plus A B ein int ist? Ermöglicht es den Benutzern wirklich einfacher, A und B hinzuzufügen, anstatt Getx selbst zu rufen und die Ergebnisse hinzuzufügen?

Liegt es daran, dass Benutzer genau wissen, dass A und B Wrapper für INT sind? In diesem Fall besteht eine andere Option darin, Conversions von A nach int und b über den Operator int () aufzudecken. Dann gibt A+B aus einem vernünftigen Grund eine INT zurück, und Sie erhalten auch alle anderen arithmetischen Operatoren:

#include <iostream>

struct A {
    int x;
    explicit A(int _x) : x(_x) {}
    operator int() {
        return x;
    }
};

struct B {
    int x;
    explicit B(int _x) : x(_x) {}
    operator int() {
        return x;
    }
};

int main() {
    A a(2);
    B b(2);
    std::cout << a + b << "\n";
    std::cout << a - b << "\n";
}

Ich habe in einem Kommentar gelesen, dass Ihre beabsichtigte Verwendung Vektoren und Matrizen hinzugefügt wird. Vielleicht sollten Sie in Betracht ziehen, nur Matrizen zu verwenden, bei denen Vektoren eine dimensionale Matrizen sind. Dann bleiben Sie nur mit einem Typ und einem Satz von Operatoren:

matrix operator*( matrix const& a, matrix const& b );
matrix operator+( matrix const& a, matrix const& b ); // and so on

Wenn Sie die Vektorklasse behalten möchten, sollten Sie prüfen, ob Sie auch einen transponierten Vektor wünschen (möglicherweise ist Transponierung nur eine interne Eigenschaft des Vektors).

Der Satz von Operationen ist nicht wirklich symmetrisch:

vector * matrix = vector
matrix * vector_t = vector_t
matrix * matrix = matrix
vector_t * vector = matrix
vector * vector_t = int

und Sie sollten diese drei Operationen anbieten (unter der Annahme, dass Transponierung eine Eigenschaft von Vektor ist):

vector operator*( vector const& v, matrix const& m );
vector operator*( matrix const& m, vector const& v );
matrix operator*( matrix const& m1, matrix const& m2 );
matrix operator*( vector const& v1, vector const& v2 ); // possibly 1x1 matrix, you cannot overload changing only return value

Alles als kostenlose Funktionen, wenn möglich. Auch wenn das obige Set nicht symmetrisch ist, ist auch die reale Welt und Ihre Benutzer erwarten dies auch.

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