Frage

Ich habe eine einfache Container-Klasse mit einem Copy-Konstruktor.

Empfehlen Sie Getter und Setter, oder die Elementvariablen Zugriff direkt?

public Container 
{
   public:
   Container() {}

   Container(const Container& cont)          //option 1
   { 
       SetMyString(cont.GetMyString());
   }

   //OR

   Container(const Container& cont)          //option 2
   {
      m_str1 = cont.m_str1;
   }

   public string GetMyString() { return m_str1;}       

   public void SetMyString(string str) { m_str1 = str;}

   private:

   string m_str1;
}
  • Im Beispiel ist der gesamte Code inline, aber in unserem realen Code gibt es keine Inline-Code.

Update (29. September 09):

Einige dieser Antworten sind gut geschrieben jedoch scheinen sie den Punkt dieser Frage zu erhalten fehlt:

  • Das ist einfach konstruiertes Beispiel Getter / Setter vs Variablen zu diskutieren mit

  • initializer Listen oder private Validator Funktionen sind nicht wirklich Teil dieser Frage. Ich frage mich, wenn entweder Design wird der Code leichter zu pflegen und erweitern machen.

  • Einige ppl in diesem Beispiel auf der Saite konzentrieren aber es ist nur ein Beispiel ist, vorstellen, dass es ein anderes Objekt ist, statt.

  • Ich bin nicht besorgt über die Leistung. wir Programmierung nicht auf der PDP-11

War es hilfreich?

Lösung

Haben Sie antizipieren, wie die Zeichenfolge zurückgegeben wird, zB. Leerraum getrimmt, null geprüft, usw.? Das Gleiche gilt für SetMyString (), wenn die Antwort ja ist, sind Sie besser dran mit Zugriffsmethoden, da Sie Ihren Code in zig Stellen nicht ändern müssen, sondern nur diese Getter und Setter-Methoden ändern.

Andere Tipps

EDIT Die Beantwortung der bearbeiteten Frage:)

  

Das ist einfach konstruiertes Beispiel zu    diskutieren mit Getter / Setter vs    Variablen

Wenn Sie eine einfache Sammlung von Variablen, die nicht jede Art von Validierung benötigen, noch eine zusätzliche Verarbeitung dann könnten Sie stattdessen einen POD in Betracht ziehen. Aus Stroustrup FAQ :

  

Eine gut gestaltete Klasse stellt eine saubere   und einfache Schnittstelle für die Benutzer,    versteckt seine Darstellung und Speichern   seine Benutzer mit etwa wissen   dass Darstellung. wenn die   Darstellung sollte nicht ausgeblendet werden -   sagen, weil Nutzer in der Lage sein sollten,   Ändern Sie alle Daten Mitglied irgendeiner Weise sie   wie - man kann dieser Klasse denken als   „Nur eine einfache alte Datenstruktur“

Kurz gesagt, ist dies nicht JAVA. Sie sollten nicht schlicht Getter / Setter schreiben, weil sie so schlecht sind, wie die Variablen sie selbst ausgesetzt wird.

  

initializer Listen oder private Validator Funktionen sind nicht wirklich   Teil dieser Frage. ich frage mich   wenn entweder Design wird den Code machen   leichter zu pflegen und zu erweitern.

Wenn Sie ein anderes Objekt der Variablen kopieren, dann sollte das Quellobjekt in einem gültigen Zustand sein. Wie hat sich die schlecht gebildete Quellobjekt wurde in erster Linie gebaut ?! Sollte nicht Konstrukteuren die Aufgabe der Validierung tun? sind nicht die Modifizierung der Klasseninvariante durch Eingabe der Aufrechterhaltung der Validierung Mitgliederfunktionen verantwortlich? Warum würden Sie ein „gültiges“ Objekt in einem Copykonstruktor validieren?

  

Ich bin über die Leistung nicht betroffen. wir Programmierung nicht auf der PDP-11

Es geht um die eleganteste Art, wenn auch in C ++ der eleganteste Code in der Regel die besten Leistungseigenschaften hat.


Sie sollten eine initializer list verwenden. In Ihrem Code, m_str1 ist standardmäßig aufgebaut und zugewiesen einen neuen Wert. Ihr Code so etwas wie dies sein könnte:

class Container 
{
public:
   Container() {}

   Container(const Container& cont) : m_str1(cont.m_str1)
   { }

   string GetMyString() { return m_str1;}       
   void SetMyString(string str) { m_str1 = str;}
private:
   string m_str1;
};

@cbrulak Sie sollten IMO nicht cont.m_str1 im copy constructor validieren. Was ich tue, ist, die Dinge in constructors zu validieren. Validierung in copy constructor bedeutet, dass Sie Ihnen ein schlecht gebildetes Objekt an erster Stelle kopieren, zum Beispiel:

Container(const string& str) : m_str1(str)
{
    if(!valid(m_str1)) // valid() is a function to check your input
    {
        // throw an exception!
    }
}

Sie sollten eine Initialisiererliste, verwenden und dann wird die Frage sinnlos, wie in:

Container(const Container& rhs)
  : m_str1(rhs.m_str1)
{}

Es gibt einen großen Abschnitt in Matthew Wilson Imperfect C ++ , die alle über Elementinitialisierung Listen erklärt, und darüber, wie Sie können sie in Kombination mit konst und / oder Hinweise Ihren Code sicherer zu machen.

Bearbeiten : ein Beispiel dafür, Validierung und konst:

class Container
{
public:
  Container(const string& str)
    : m_str1(validate_string(str))
  {}
private:
  static const string& validate_string(const string& str)
  {
    if(str.empty())
    {
      throw runtime_error("invalid argument");
    }
    return str;
  }
private:
  const string m_str1;
};

Wie es jetzt geschrieben (ohne Qualifikation des Ein- oder Ausgang) Ihre Getter und Setter (Accessor und Mutator, wenn Sie bevorzugen) vollbringen absolut nichts, so könnte man genauso gut nur die Zeichenfolge öffentlich machen und mit getan werden es.

Wenn der eigentliche Code wirklich die Zeichenfolge qualifiziert, sind dann die Chancen ziemlich gut, dass mit dem, was Sie zu tun überhaupt nicht richtig ein String ist - statt, es ist nur etwas, das viel wie ein String aussieht. Was Sie wirklich in diesem Fall zu tun ist, die Art System zu missbrauchen, zu sortieren einer Reihe von Belichtungs, wenn die reale Art ist nur etwas ein bisschen wie eine Kette. Sie sind dann die Setter-Bereitstellung zu versuchen, was Einschränkungen der realen Art zu einem echten String verglichen hat zu erzwingen.

Wenn man es betrachtet aus dieser Richtung wird die Antwort ziemlich offensichtlich: anstatt einer Schnur, mit einem Setter der String Akt zu machen wie einige andere (eingeschränkter) Art, was Sie stattdessen tun sollten, ist die Definition einer tatsächlichen Klasse für den Typ, den Sie wirklich wollen. richtig, dass die Klasse definiert haben, machen Sie eine Instanz davon öffentlich. Wenn (wie hier der Fall zu sein scheint) es sinnvoll ist es zuweisen einen Wert, der als String beginnt, dann sollte diese Klasse einen Zuweisungsoperator enthalten, die einen String als Argument. Wenn (wie auch hier der Fall zu sein scheint) ist es sinnvoll, diese Art in einen String unter Umständen zu konvertieren, kann es auch Cast-Operator enthält, die einen String als Ergebnis erzeugt.

Dies ergibt eine echte Verbesserung gegenüber einem Setter und Getter in einer Umgebung Klasse. In erster Linie, wenn Sie die in einer Umgebung Klasse setzen, ist es einfach für Code innerhalb dieser Klasse die Getter / Setter zu umgehen, verlieren Durchsetzung von was auch immer die Setter sollte erzwingen. Zweitens macht es eine normal aussehende Notation. Mit Hilfe eines Getter und Setter zwingt Sie Code zu schreiben, zu lesen, einfach nur hässlich und schwer ist.

Eine der wichtigsten Stärken einer String-Klasse in C ++ verwendet Betreiber Überlastung so können Sie so etwas wie ersetzen:

strcpy(strcat(filename, ".ext"));

mit:

filename += ".ext";

zur Verbesserung der Lesbarkeit. Aber schauen Sie, was passiert, wenn diese Zeichenfolge Teil einer Klasse ist, die uns zwingt, durch einen Getter und Setter zu gehen:

some_object.setfilename(some_object.getfilename()+".ext");

Wenn überhaupt, ist der C-Code tatsächlich besser lesbar als dieses Durcheinander. Auf der anderen Seite, überlegen, was passiert, wenn wir den Job richtig, mit einem öffentlichen Objekt einer Klasse zu tun, die einen Operator-String und Operator definiert =:

some_object.filename += ".ext";

Schön, einfach und lesbar, so wie es sein sollte. Noch besser ist es, wenn wir etwas über die Schnur erzwingen müssen, dass wir nur die kleine Klasse inspizieren können wir wirklich nur ein oder zwei bestimmte, bekannte Orte (für diese Klasse Operator =, möglicherweise ein Ctor oder zwei) aussehen müssen, um wissen, dass es immer durchgesetzt hat -. eine ganz andere Geschichte aus, wenn wir einen Setter, um zu versuchen, den Job zu tun verwenden

Fragen Sie sich, was die Kosten und Nutzen sind.

Kosten: höhere Laufzeit-Overhead. Der Aufruf virtueller Funktionen in ctors ist eine schlechte Idee, aber Getter und Setter sind unwahrscheinlich virtuell sein.

Vorteile: Wenn der Setter / Getter etwas kompliziert macht, sind Sie nicht Code zu wiederholen; wenn es etwas nicht intuitiv tut, Sie vergessen nicht, das zu tun.

Die Kosten / Nutzen-Verhältnis für verschiedene Klassen unterscheiden. Sobald Sie dieses Verhältnis ermittelt sind, verwenden Sie das Urteil. Für unveränderliche Klassen, natürlich, müssen Sie nicht Einrichter, und Sie müssen nicht Getter müssen (als const Mitglieder und Verweis öffentlich sein können, wie niemand kann sich ändern / neu einsetzen).

Es gibt keinen Königsweg, wie man den Kopierkonstruktor zu schreiben. Wenn Ihre Klasse hat nur Mitglieder, die einen Copykonstruktor schaffen, schafft Instanzen, den Staat nicht teilen (oder zumindest nicht angezeigt werden, dies zu tun) unter Verwendung einer Initialisierungsliste eine gute Art und Weise ist.

Ansonsten musst du eigentlich denken.

struct alpha {
   beta* m_beta;
   alpha() : m_beta(new beta()) {}
   ~alpha() { delete m_beta; }
   alpha(const alpha& a) {
     // need to copy? or do you have a shared state? copy on write?
     m_beta = new beta(*a.m_beta);
     // wrong
     m_beta = a.m_beta;
   }

Beachten Sie, dass Sie rund um die potenziellen segfault unter Verwendung smart_ptr bekommen -. Sie können aber eine Menge Spaß haben die daraus resultierenden Fehler Debuggen

Natürlich kann es noch witziger.

  • Mitglieder, die auf Wunsch erstellt werden.
  • new beta(a.beta) ist falsch , falls Sie irgendwie Polymorphismus einführen.

... eine Schraube der sonst -. Bitte immer denken, wenn eine Kopie Konstruktor schreiben

Warum brauchen Sie Getter und Setter überhaupt?

Einfach :) - Sie Invarianten erhalten - das heißt garantiert Ihre Klasse macht, wie „MyString immer eine gerade Anzahl von Zeichen“

.

Wenn, wie beabsichtigt umgesetzt, Ihr Ziel ist es immer in einem gültigen Zustand - so eine Kopie element kann sehr gut die Mitglieder direkt kopieren, ohne Angst Eine Garantie zu brechen. Es gibt keinen Vorteil der Weitergabe bereits validierten Zustand durch eine weitere Runde der Statusüberprüfung.

Wie Arak sagte, wäre die beste eine Initialisiererliste werden.


Nicht so einfach (1):
Ein weiterer Grund Getter / Setter verwenden setzt nicht auf Details der Implementierung. Das ist eine seltsame Idee für eine Kopie CTOR, wenn eine solche Implementierungsdetails Ändern Sie fast immer brauchen CDA sowieso einzustellen.


Nicht so einfach (2):
Für mich als falsch erweisen, können Sie Invarianten konstruieren, die auf der Instanz abhängig sind selbst oder eines anderen externen Faktors. Ein (sehr contrieved) Beispiel: „Wenn die Anzahl der Instanzen selbst ist, ist die String-Länge auch, sonst ist es ungerade ist.“ In diesem Fall würde die Kopie CTOR werfen, oder die Zeichenfolge anpassen. In einem solchen Fall kann es helfen, Setter / Getter zu verwenden - aber das ist nicht die allgemeine cas. Sie sollten keine allgemeinen Regeln von Merkwürdigkeiten abzuleiten.

Ich ziehe es über eine Schnittstelle für Außen Klassen auf die Daten zugreifen, falls Sie die Art und Weise ändern möchten sie abgerufen wird. Wenn Sie jedoch im Rahmen der Klasse und wollen den internen Zustand des kopierten Wertes replizieren, würde ich direkt mit Datenelementen gehen.

Ganz zu schweigen davon, dass Sie wahrscheinlich ein paar Funktionsaufrufe sparen werden, wenn der Getter nicht inlined.

Wenn Ihr Getter ist (inline und) nicht virtual, gibt es keine Pluspunkte noch Nachteile in sie WRT direkten Mitglied Zugang - es sieht einfach doof mir in Bezug auf Stil, aber keine große Sache oder so

Wenn Ihr Getter virtuell ist, dann gibt es ist Overhead ... aber doch genau das ist, wenn Sie sie nennen wollen, falls sie in einer Unterklasse außer Kraft gesetzt sind! -)

Es gibt einen einfachen Test, der für viele Design-Fragen arbeitet, dieser enthalten. Nebenwirkungen hinzufügen und sehen, was bricht

Es sei Setter ordnet nicht nur einen Wert, sondern auch schreibt Audit-Aufzeichnung, protokolliert eine Meldung oder löst ein Ereignis. Wollen Sie dies für jedes Objekt passieren, wenn das Objekt zu kopieren? Wahrscheinlich nicht -. So Setter in Konstruktor aufrufe logisch falsch sind (auch wenn Setter in der Tat sind nur Zuweisungen)

Auch wenn ich mit anderen Plakaten einig, dass es viele Entry-Level C ++ „Nein-Nein“ in Ihrer Probe, das auf der Seite setzen und Ihre Frage zu beantworten direkt:

In der Praxis neige ich viele, aber nicht alle meine Mitgliederfelder * Öffentlichkeit, zu beginnen und sie dann bekommen bewegen / Set, wenn nötig.

Nun werde ich der erste sein zu sagen, dass dies nicht unbedingt eine empfohlene Praxis, und viele Praktiker verabscheuen diese und sagen, dass jedes Feld Setter / Getter haben sollte.

Vielleicht. Aber ich finde, dass in der Praxis ist dies nicht immer notwendig. Zugegeben, es verursacht Schmerzen später, wenn ich ein Feld von öffentlichen zu einem Getter zu ändern, und manchmal, wenn ich weiß, was Nutzung eine Klasse hat, gibt ich es gesetzt / get und das Feld geschützt oder privat von Anfang an machen.

YMMV

RF

  • Sie rufen Felder „Variablen“ - Ich möchte Sie ermutigen, diesen Begriff zu verwenden, nur für lokale Variablen innerhalb einer Funktion / Methode
Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top