Das Konzept von 'Nil' in C ++

7

Sie erinnern sich an Ihre Vorlesungen in Algorithmen, dass es sehr praktisch ist, ein Konzept von Nil zu haben, dem etwas zugeordnet oder mit dem verglichen werden kann. (Übrigens habe ich nie einen Bachelor in Informatik gemacht.) In Python können wir None ; In Scala gibt es Nothing (was ein Subobjekt von allem ist, wenn ich es richtig verstehe). Aber meine Frage ist, wie können wir Nil in C ++ haben? Unten sind meine Gedanken.

Wir könnten ein Singleton-Objekt mit dem Singleton-Entwurfsmuster definieren, aber Mein derzeitiger Eindruck ist, dass die meisten von euch bei dem Gedanken zusammenzucken würden.

Oder wir könnten eine globale oder eine statische definieren.

Mein Problem ist, dass ich in keinem dieser Fälle eine Möglichkeit finde, irgendeine Variable irgendeines Typs Nil zuzuweisen oder irgendein Objekt irgendeines Typs mit Nil vergleichen zu können. . Pythons None ist nützlich, weil Python dynamisch typisiert wird; Scala's Nothing (nicht zu verwechseln mit Scala Nil , was eine leere Liste bedeutet) löst das elegant, weil Nothing ein Unterobjekt von allem ist. Gibt es also eine elegante Möglichkeit, Nil in C ++ zu haben?

    
Ray 09.04.2015, 18:29
quelle

6 Antworten

9

Nein, in C ++ gibt es keinen universellen nil -Wert. Verbatim, C ++ - Typen sind nicht verwandt und teilen keine gemeinsamen Werte.

Sie können einige -Formen für gemeinsam nutzbare Werte erreichen, indem Sie Vererbung verwenden, aber Sie müssen dies explizit tun, und dies kann nur für benutzerdefinierte Typen erfolgen.

    
Angew 09.04.2015, 18:31
quelle
9

Es gibt zwei verwandte Konzepte, die Sie als Nil beschreiben: den Einheitentyp und den Optionstyp .

Einheitentyp

Das ist NoneType in Python und nullptr_t in C ++, es ist nur ein spezieller Typ, der einen einzelnen Wert hat, der dieses spezifische Verhalten vermittelt. Da Python dynamisch typisiert ist, kann jedes Objekt mit None verglichen werden, aber in C ++ können wir das nur für Zeiger tun:

%Vor%

Dies ist semantisch identisch mit Pythons:

%Vor%

Optionstyp

Python hat (oder braucht) keine solche Funktion, aber alle ML-Familien tun es. Dies ist in C ++ über boost::optional implementierbar. Dies ist eine typsichere Kapselung der Idee, dass ein bestimmtes Objekt vielleicht einen Wert haben kann oder nicht. Diese Idee ist in der funktionalen Familie ausdrucksvoller als in C ++:

%Vor%

Obwohl es einfach genug ist, um es auch in C ++ zu verstehen, sobald Sie es sehen:

%Vor%

Der Vorteil hier ist, dass der Optionstyp ein Werttyp ist und Sie einfach einen "Wert" von nichts ausdrücken können (z. B. kann Ihr optional<int*> nullptr als Wert speichern, getrennt von "Nil"). Auch das kann mit jedem Typ funktionieren, nicht nur mit Zeigern - es ist nur, dass Sie für die zusätzliche Funktionalität bezahlen müssen. Ein optional<T> wird größer sein als ein T und teurer in der Benutzung sein (wenn auch nur ein bisschen, obwohl das wenig wichtig ist).

A C ++ Nil

Wir können diese Ideen kombinieren, um tatsächlich ein Nil in C ++ zu machen, mindestens eins, das mit Zeigern und Optionen funktioniert.

%Vor%

(Beachten Sie, dass wir dies sogar auf alles verallgemeinern können, das operator!

implementiert %Vor%

, aber es wäre besser, uns auf die klaren Fälle zu beschränken, und ich mag die explizite Aussage, die Ihnen Zeiger und Optionen geben)

Also:

%Vor%     
Barry 09.04.2015 19:03
quelle
3

C ++ folgt dem Prinzip, dass Sie nicht für das bezahlen, was Sie nicht verwenden. Wenn Sie beispielsweise eine 32-Bit-Ganzzahl speichern möchten, um den gesamten Bereich der 32-Bit-Werte und zu speichern, benötigen Sie mehr als 32 Bit Speicherplatz. Natürlich können Sie clevere Klassen erstellen, um dieses Verhalten darzustellen, aber es ist nicht "out of the box" verfügbar.

    
Neil Kirk 09.04.2015 18:35
quelle
3

Sie können das nicht in C ++ haben, ohne sich auf einen Container verlassen zu müssen, der Ihren Typ einkapselt (zB boost::optional ).

Tatsächlich gibt es eine Kombination von Gründen, denn C ++ ist ein:

  • statisch typisierte Sprache: Sobald Sie eine Variable mit einem Typ definiert haben, bleibt sie bei diesem Typ (dh Sie können keinen Wert None zuweisen, der nicht zu den verschiedenen Typen gehört, die Sie in anderen Typen darstellen könnten , dynamisch typisierte Sprachen wie Python)
  • Sprache mit eingebauten [1] Typen und kein gemeinsames Basisobjekt für seine Typen: Sie können nicht die Semantik von None plus "Alle Werte, die dieser Typ sonst repräsentieren könnte" in einem gemeinsamen Objekt für alle haben deine Typen.

[1] die nicht wie integrierte Java-Typen sind, deren Klassen alle von java.lang.Object geerbt haben.

    
JBL 09.04.2015 18:53
quelle
2

In den meisten dieser Sprachen sind Variablen nicht als Name von Objekten implementiert, sondern als Griffe für Objekte.

Solche Handles können so eingestellt werden, dass sie "nichts halten" mit sehr wenig zusätzlichem Overhead. Dies ist analog zu einem C ++ - Zeiger, wobei der nullptr -Zustand "auf nichts zeigt" entspricht.

Oft tun solche Sprachen voll mit der Garbage Collection. Sowohl die Speicherbereinigung als auch die erzwungene indirekte Referenzierung von Daten haben in der Praxis erhebliche Leistungseinbußen zur Folge und sie beschränken die Art von Operationen, die Sie ausführen können.

Wenn Sie eine Variable in solchen Sprachen verwenden, müssen Sie zuerst dem Zeiger folgen, um auf das reale Objekt zuzugreifen.

In C ++ bezieht sich ein Variablenname normalerweise auf das tatsächliche Objekt, nicht auf eine Referenz. Wenn Sie eine Variable in C ++ verwenden, greifen Sie direkt darauf zu. Ein zusätzlicher Zustand (entsprechend "nichts") würde zusätzlichen Aufwand in jeder Variablen erfordern, und eines der Prinzipien von C ++ ist, dass Sie nicht für das bezahlen, was Sie verwenden.

Es gibt Möglichkeiten, einen "Nullable" -Datentyp in C ++ zu erstellen. Diese variieren von der Verwendung von rohen Zeigern, von intelligenten Zeigern oder von etwas wie std::experimantal::optional . Alle von ihnen haben einen Zustand für "es gibt nichts da", normalerweise erkennbar, indem das Objekt genommen und in einem bool -Kontext ausgewertet wird. Um auf die tatsächlichen Daten zuzugreifen (vorausgesetzt, dass sie existieren), verwenden Sie unary * oder -> .

    
Yakk 09.04.2015 20:01
quelle
-1

In C ++ 11 gibt es einen Wert nullptr, der jedem beliebigen Zeigertyp zugewiesen werden kann.

Wenn es kein Zeigertyp ist, muss es da sein. Für bestimmte Werte könnten Sie spezielle "Ich-existiere nicht" Werte definieren, aber es gibt keine universelle Lösung.

Wenn es nicht existiert, sind Ihre Auswahlmöglichkeiten:

  • Zeigen Sie darauf und verwenden Sie nullptr, geben Sie einen nicht vorhandenen Wert oder
  • an
  • Behalte ein separates Flag, um Existenz oder Nicht-Existenz anzugeben (was boost::optional für dich bedeutet), oder
  • Definieren Sie einen speziellen Wert für diese spezielle Verwendung, um einen nicht vorhandenen Wert darzustellen (beachten Sie, dass dies nicht immer möglich ist.)
Dale Wilson 09.04.2015 18:34
quelle

Tags und Links