Frage

Hintergrund

Ich habe eine Container-Klasse, den Vektor verwendet intern. Ich habe eine Methode AddChar (std :: string) auf diese Wrapper-Klasse zur Verfügung gestellt, die funktioniert ein push_back () in den internen Vektor. In meinem Code, muss ich einige Zeit mehrere Artikel in den Behälter hinzufügen. Dafür habe ich verwenden,

container.AddChar("First");
container.AddChar("Second");

Das macht den Code größer. So macht es einfacher, ich plane Betreiber zu überlasten <<. So dass ich schreiben kann

container << "First" << "Second"

und zwei Elemente zu Grunde liegenden Vektor hinzukommen.

Hier ist der Code, den ich für die verwendeten

class ExtendedVector
{
private:
    vector<string> container;

public:
    friend ExtendedVector& operator<<(ExtendedVector& cont,const std::string str){
        cont.AddChar(str);
        return cont;
    }

    void AddChar(const std::string str)
    {
        container.push_back(str);
    }

    string ToString()
    {
        string output;
        vector<string>::iterator it = container.begin();
        while(it != container.end())
        {
            output += *it;
            ++it;
        }
        return output;
    }
};

Es funktioniert wie erwartet.

Fragen

  1. Ist Betreiber Überlastung richtig geschrieben?
  2. Ist es eine gute Praxis Operatoren in Situationen wie diese zu überlasten?
  3. Wird es Performance-Probleme oder andere Probleme mit diesem Code?

Alle Gedanken?

Bearbeiten

die ausgezeichneten Kommentare Nach Anhörung, habe ich beschlossen, die Überlastung zu <<, da es hier nicht sinnvoll. Ich entfernte die Betreiber Überlastung Code und hier ist der endgültige Code.

class ExtendedVector
{
private:
    vector<string> container;

public:

    ExtendedVector& AddChar(const std::string str)
    {
        container.push_back(str);
        return *this;
    }

         .. other methods
}

Dies ermöglicht es mir hinzuzufügen

container.AddChar("First").AddChar("Second")

In C # kann ich unter Verwendung des Schlüsselwort params dies leichter tun. Code wird wie sein

void AddChar(params string[] str)
{
    foreach(string s in str)
       // add to the underlying collection
}

Ich weiß, in C ++, können wir ... angeben variable langth von Parametern verwenden. Aber AFAIK, ist es nicht sicher geben. So ist es eine empfohlene Praxis, dies zu tun? So dass ich schreiben kann

container.AddChar("First","Second")

Danke für die Antworten.

War es hilfreich?

Lösung

  

Ist Betreiber Überlastung richtig geschrieben?

Es ist, aber man kann es besser machen. Wie jemand anders erwähnt, kann Ihre Funktion vollständig aus bestehenden, öffentlichen Funktionen definiert werden. Warum machen es nicht nur diejenigen verwenden? Gerade jetzt, es ist ein Freund, das heißt, es auf die Details der Implementierung gehört. Das gleiche gilt, wenn Sie Operator setzen << als Mitglied in Ihrer Klasse. Stellen Sie jedoch Ihren Operator << ein Nichtmitglied nicht-Freund Funktion.

class ExtendedVector {
    ...
};

// note, now it is *entirely decoupled* from any private members!
ExtendedVector& operator<<(ExtendedVector& cont, const std::string& str){
    cont.AddChar(str);
    return cont;
}

Wenn Sie Ihre Klasse ändern, werden Sie nicht sicher sein, dass Ihre Betreiber << wird immer noch funktionieren. Aber wenn Ihr Operator << ganz nur auf öffentliche Funktionen abhängt, dann können Sie sicher sein, dass es funktionieren wird, nachdem Änderungen nur für Implementierungsdetails der Klasse gemacht wurden. Yay!

  

Ist es eine gute Praxis Operatoren in Situationen wie diese zu überlasten?

Als ein anderer Mann wieder gesagt, das ist fraglich. In vielen Situationen wird Betreiber Überlastung „ordentlich“ auf den ersten Blick aussehen, aber im nächsten Jahr wie die Hölle aussehen wird, weil Sie keine Ahnung haben mehr, was du im Sinn hatte, als einige Symbole besondere Liebe. Im Fall des Operators <<, ich glaube, dies ein OK-Gebrauch ist. Seine Verwendung als Einführhilfe Operator für Strom ist bekannt. Und ich weiß von Qt und KDE-Anwendungen, die es weitgehend in den Fällen wie

verwenden
QStringList items; 
items << "item1" << "item2";

Ein ähnlicher Fall ist boost.format die auch operator% für das Bestehen Argument für Platzhalter in seiner Zeichenfolge wieder verwendet:

format("hello %1%, i'm %2% y'old") % "benny" % 21

Es ist natürlich auch arguable es dort zu verwenden. Aber seine Verwendung für printf Format spezifiziert sind gut bekannt und so seine Verwendung ist es auch in Ordnung, imho. Aber wie immer, ist Stil auch subjektives es so nimmt mit einem Körnchen Salz:)

  

Wie ich mit variabler Länge Argumente in einer Art und Weise typsichere akzeptieren kann?

Nun, es gibt die Möglichkeit, einen Vektor zu akzeptieren, wenn Sie für eine homogenes Argument suchen:

void AddChars(std::vector<std::string> const& v) {
    std::vector<std::string>::const_iterator cit =
        v.begin();
    for(;cit != v.begin(); ++cit) {
        AddChar(*cit);
    }
}

Es ist nicht wirklich komfortabel es aber passieren. Sie haben Ihre Vektor manuell zu konstruieren und dann passieren ... Ich sehe schon haben Sie das Recht das Gefühl über die Vararg Stil Funktionen. Man sollte sie nicht für diese Art von Code verwenden, und nur dann, wenn mit C-Code Schnittstelle oder Debugging-Funktionen, wenn überhaupt. Ein anderer Weg, um diesen Fall zu behandeln ist Präprozessor Programmierung anzuwenden. Dies ist ein fortgeschrittenes Thema und ist ziemlich hacky. Die Idee ist, automatisch Überlastungen bis zu einem gewissen Obergrenze zu erzeugen in etwa wie folgt aus:

#define GEN_OVERLOAD(X) \
void AddChars(GEN_ARGS(X, std::string arg)) { \
    /* now access arg0 ... arg(X-1) */ \
    /* AddChar(arg0); ... AddChar(arg(N-1)); */ \
    GEN_PRINT_ARG1(X, AddChar, arg) \
}

/* call macro with 0, 1, ..., 9 as argument
GEN_PRINT(10, GEN_OVERLOAD)

Das ist Pseudo-Code. Sie können an der Boost-Präprozessor Bibliothek hier einen Blick .

Weiter C ++ Version wird weit bessere Möglichkeiten bieten. Initializer Listen können verwendet werden:

void AddChars(initializer_list<std::string> ilist) {
    // range based for loop
    for(std::string const& s : ilist) {
        AddChar(s);
    }
}

...
AddChars({"hello", "you", "this is fun"});

Es ist auch möglich, in der nächsten C ++ zu beliebig viele (Mischtyp) Argumente unterstützen mit variadische Vorlagen . GCC4.4 für sie haben Unterstützung. GCC 4.3 bereits unterstützt sie teilweise.

Andere Tipps

1) Ja, außer da AddChar Öffentlichkeit gibt es keinen Grund gibt, braucht es eine friend sein.

2) Dies ist fraglich. << ist eine Art in der Lage zu sein, der Betreiber, deren Überlastung für „weird“ Dinge wenigstens zähneknirschend akzeptiert.

3) Nichts offensichtlich. Wie immer Profilierung ist dein Freund. Möglicherweise möchten Sie vorbei an den String-Parameter prüfen, um AddChar und operator<< durch konstante Referenz (const std::string&) unnötiges Kopieren zu vermeiden.

  

Ist es eine gute Praxis, um Überlastung   Betreiber in Situationen wie diesen?

Das glaube ich nicht so. Es ist verwirrend, wie die Hölle für jemanden, der nicht weiß, dass Sie den Betreiber überlastet haben. Just-Stick zu beschreibende Methode Namen und vergessen Sie die zusätzlichen Zeichen Sie eingeben, es ist einfach nicht wert. Ihr Betreuer (oder Sie sich in 6 Monaten) wird es Ihnen danken.

Ich würde es vorziehen, es nicht zu überlasten, dass die Art und Weise persönlich, weil Vektoren normalerweise nicht eine überladene Verschiebung nach links Betreiber - es ist nicht wirklich Idiom es ist; -)

Ich würde wahrscheinlich eine Referenz von AddChar zurückkehren, anstatt wie folgt:

ExtendedVector& AddChar(const std::string& str) {
    container.push_back(str);
    return *this;
}

so können Sie dann tun

container.AddChar("First").AddChar("Second");

, die nicht wirklich viel größer als bitshift Betreiber ist.

(auch Logans Kommentar zu dem Übergeben Strings in durch Verweis statt von Wert sehen).

Der Bediener in diesem Fall einer Überlastung ist nicht empfehlenswert, da es der Code weniger lesbar macht. Der Standard std::vector hat es nicht entweder für Elemente drängen, aus guten Gründen.

Wenn Sie besorgt über die Anrufer-Code zu lang ist, könnten Sie dies berücksichtigen anstelle des überladenen Operator:

container.AddChar("First").AddChar("Second");

Dies wird möglich, wenn Sie Rückkehr AddChar() *this haben.

Es ist schon komisch, dass Sie diese toString() Funktion haben. In , dass Fall ein operator<< zu einem Strom zur Ausgabe die Standard-Sache sein würde, anstatt zu verwenden! Also, wenn Sie Operatoren verwenden, um die toString() Funktion eines operator<<.

Der Betreiber ist nicht richtig hier überlastet. Es gibt keinen Grund, der Betreiber ein Freund zu machen, da es sich um ein Mitglied der Klasse sein kann. Freund für Funktionen, die keine echten Mitglieder der Klasse sind (wie zum Beispiel, wenn eine Überlastung << für Ostream so das Objekt ausgegeben werden kann oder ofstreams cout).

Was Sie wirklich wollen, dass der sein:

ExtendedVector& operator<<(const std::string str){
    AddChar(str);
    return *this;
}

Es ist in der Regel schlechte Praxis betrachtet Operatoren in einer Art und Weise zu überlasten, dass sie etwas zu tun hat, als sie es normalerweise tun. << ist normalerweise Bit-Verschiebung, so dass es auf diese Weise eine Überlastung kann verwirrend sein. Offensichtlich STL Überlastungen << für „Strom Einfügen“ und so mit, dass es könnte Sinn macht es, um eine Überlastung für Ihren Einsatz in ähnlicher Weise. Aber das scheint nicht wie das, was du tust, so dass Sie wahrscheinlich es vermeiden wollen.

Es sind keine Performance-Probleme, da Betreiber Überlastung das gleiche wie eine regelmäßige Funktionsaufruf, nur der Anruf ist versteckt weil es vom Compiler automatisch durchgeführt wird.

Das wird die Dinge eher verwirrend machen, würde ich die gleiche Syntax wie std :: cin in eine Variable:

std::cin >> someint;

"First" >> container;

So ist es zumindest ein Einfügeoperator. Für mich, wenn alles hat einen überladenen Operator << ich erwarten, dass es etwas sein, ausgibt. Genau wie std :: cout.

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