Java's Final vs. C ++ 's const

151

Das Tutorial für Java für C ++ - Programmierer besagt Folgendes (Highlight ist mein eigenes):

Das Schlüsselwort final entspricht ungefähr const in C ++

Was bedeutet in diesem Zusammenhang "grob"? Sind sie nicht genau gleich?

Was sind die Unterschiede, wenn überhaupt?

WinWin
quelle

Antworten:

195

In C ++ bedeutet das Markieren einer Mitgliedsfunktion, constdass sie für constInstanzen aufgerufen werden kann. Java hat kein Äquivalent dazu. Z.B:

class Foo {
public:
   void bar();
   void foo() const;
};

void test(const Foo& i) {
   i.foo(); //fine
   i.bar(); //error
}

Werte können später in Java nur einmal zugewiesen werden, z.

public class Foo {
   void bar() {
     final int a;
     a = 10;
   }
}

ist in Java legal, aber nicht in C ++, wohingegen:

public class Foo {
   void bar() {
     final int a;
     a = 10;
     a = 11; // Not legal, even in Java: a has already been assigned a value.
   }
}

Sowohl in Java als auch in C ++ können Mitgliedsvariablen final/ sein const. Diese müssen einen Wert erhalten, wenn eine Instanz der Klasse fertig erstellt ist.

In Java müssen sie festgelegt werden, bevor der Konstruktor fertig ist. Dies kann auf zwei Arten erreicht werden:

public class Foo {
   private final int a;
   private final int b = 11;
   public Foo() {
      a = 10;
   }
}

In C ++ müssen Sie Initialisierungslisten verwenden, um constMitgliedern einen Wert zu geben :

class Foo {
   const int a;
public:
   Foo() : a(10) {
      // Assignment here with = would not be legal
   }
};

In Java kann final verwendet werden, um Dinge als nicht überschreibbar zu markieren. C ++ (vor C ++ 11) tut dies nicht. Z.B:

public class Bar {
   public final void foo() {
   }
}

public class Error extends Bar {
   // Error in java, can't override
   public void foo() {
   }
}

Aber in C ++:

class Bar {
public:
   virtual void foo() const {
   }
};

class Error: public Bar {
public:
   // Fine in C++
   virtual void foo() const {
   }
};

Dies ist in Ordnung, da die Semantik des Markierens einer Elementfunktion constunterschiedlich ist. (Sie können auch überladen, indem Sie nur consteine der Elementfunktionen verwenden. (Beachten Sie auch, dass in C ++ 11 Elementfunktionen als endgültig markiert werden können, siehe Abschnitt zum Aktualisieren von C ++ 11).


C ++ 11 Update:

In C ++ 11 können Sie tatsächlich sowohl Klassen als auch finalElementfunktionen mit identischer Semantik für dasselbe Feature in Java markieren , z. B. in Java:

public class Bar {
   public final void foo() {
   }
}

public class Error extends Bar {
   // Error in java, can't override
   public void foo() {
   }
}

Kann jetzt in C ++ 11 genau wie folgt geschrieben werden:

class Bar {
public:
  virtual void foo() final;
};

class Error : public Bar {
public:
  virtual void foo() final;
};

Ich musste dieses Beispiel mit einer Vorabversion von G ++ 4.7 kompilieren. Beachten Sie, dass dies constin diesem Fall nicht ersetzt , sondern erweitert wird, indem das Java-ähnliche Verhalten bereitgestellt wird, das mit dem nächstgelegenen entsprechenden C ++ - Schlüsselwort nicht angezeigt wurde. Also , wenn Sie wollten beide eine Member - Funktion sein finalund constSie tun würden:

class Bar {
public:
  virtual void foo() const final;
};

(Die Reihenfolge von constund finalhier ist erforderlich).

Bisher gab es kein direktes Äquivalent zu constMitgliedsfunktionen, obwohl virtuales eine potenzielle Option wäre , Funktionen nicht zu machen, ohne jedoch beim Kompilieren einen Fehler zu verursachen.

Ebenso das Java:

public final class Bar {
}

public class Error extends Bar {
}

wird in C ++ 11:

class Bar final {
};

class Error : public Bar {
};

(Bisher waren privateKonstruktoren wahrscheinlich die nächsten, die Sie in C ++ erreichen konnten.)

Interessanterweise ist zur Aufrechterhaltung der Abwärtskompatibilität mit Code vor C ++ 11 final kein Schlüsselwort in der üblichen Weise. (Nehmen Sie das triviale, legale C ++ 98-Beispiel, um struct final;zu sehen, warum das Erstellen eines Schlüsselworts den Code beschädigen würde.)

Flexo
quelle
3
Sie sollten diese Methoden virtuell machen. Ansonsten machst du wirklich nicht das Gleiche
BlueRaja - Danny Pflughoeft
1
In Ihrem letzten Beispiel ist das, was Sie haben, legal, aber es ist erwähnenswert, final int a; a = 10; a = 11;dass dies nicht der finalFall ist (dies ist der Zweck eines Variablenmodifikators). Außerdem können endgültige Mitglieder einer Klasse nur zur Deklarationszeit oder einmal in einem Konstruktor festgelegt werden .
CorsiKa
2
Beachten Sie, dass C ++ 0x den Elementfunktionsdekorator finalgenau für diesen Zweck hinzufügt . VC ++ 2005, 2008 und 2010 haben dies bereits implementiert, wobei das kontextbezogene Schlüsselwort sealedanstelle von verwendet wird final.
ildjarn
@ildjarn - das ist interessant zu wissen und noch eine relativ kleine C ++ 0x Änderung, die mir nicht bewusst war! Ich werde wahrscheinlich irgendwo im Text einen kleinen Kommentar hinzufügen, der darauf hinweist, dass sich dies mit C ++ 0x ändert.
Flexo
1
Anscheinend machen die Leute immer noch s / const / final / g in Codebasen mit dem Finalructor als Ergebnis!
Flexo
30

In Java kann das letzte Schlüsselwort für vier Dinge verwendet werden:

  • auf eine Klasse oder Methode, um es zu versiegeln (keine Unterklassen / Überschreiben erlaubt)
  • auf eine Mitgliedsvariable, um zu deklarieren, dass sie genau einmal gesetzt werden kann (ich denke, das ist es, worüber Sie sprechen)
  • für eine in einer Methode deklarierte Variable, um sicherzustellen, dass sie genau einmal festgelegt werden kann
  • auf einem Methodenparameter, um zu deklarieren, dass er innerhalb der Methode nicht geändert werden kann

Eine wichtige Sache ist: Eine Java-Final-Member-Variable muss genau einmal gesetzt werden! Zum Beispiel in einem Konstruktor, einer Felddeklaration oder einem Intializer. (Sie können jedoch keine endgültige Elementvariable in einer Methode festlegen.)

Eine weitere Konsequenz der endgültigen Festlegung einer Mitgliedsvariablen betrifft das Speichermodell. Dies ist wichtig, wenn Sie in einer Thread-Umgebung arbeiten.

Ralph
quelle
Was meinst du mit "Speichermodell"? Ich verstehe es nicht
Tony
1
@Tony: Java-Sprachspezifikation, Kapitel 17.4. Speichermodell - docs.oracle.com/javase/specs/jls/se8/html/index.html - Googles erster Treffer
Ralph
27

Ein constObjekt kann nur constMethoden aufrufen und wird im Allgemeinen als unveränderlich angesehen.

const Person* person = myself;
person = otherPerson; //Valid... unless we declared it const Person* const!
person->setAge(20); //Invalid, assuming setAge isn't a const method (it shouldn't be)

Ein finalObjekt kann nicht auf ein neues Objekt gesetzt werden, aber es ist nicht unveränderlich - nichts hindert jemanden daran, setMethoden aufzurufen .

final Person person = myself;
person = otherPerson; //Invalid
person.setAge(20); //Valid!

Java hat keine inhärente Möglichkeit, Objekte für unveränderlich zu erklären. Sie müssen die Klasse selbst als unveränderlich gestalten.

Wenn die Variable ein primitiver Typ ist, final/ constdie gleiche Arbeit.

const int a = 10; //C++
final int a = 10; //Java
a = 11; //Invalid in both languages
BlueRaja - Danny Pflughoeft
quelle
3
Dies ist auch eine großartige Antwort (wie viele andere hier). Leider kann ich nur eine Antwort akzeptieren. :)
WinWin
1
Perfekte Antwort!
ADJ
13

Java final entspricht C ++ const für primitive Werttypen.

Bei Java-Referenztypen entspricht das endgültige Schlüsselwort einem const-Zeiger ... dh

//java
final int finalInt = 5;
final MyObject finalReference = new MyObject();

//C++
const int constInt = 5;
MyObject * const constPointer = new MyObject();
James Schek
quelle
„das letzte Schlüsselwort auf einen const Zeiger gleichwertig ist“ , so gut
ADJ
8

Sie haben hier bereits einige gute Antworten, aber ein Punkt, der es wert schien, hinzugefügt zu werden: constIn C ++ wird häufig verwendet, um zu verhindern, dass andere Teile des Programms den Status von Objekten ändern. Wie bereits erwähnt, finalkann dies in Java nicht möglich sein (außer bei Grundelementen). Es verhindert lediglich, dass die Referenz in ein anderes Objekt geändert wird. Wenn Sie jedoch a verwenden Collection, können Sie Änderungen an Ihren Objekten mithilfe der statischen Methode verhindern

 Collection.unmodifiableCollection( myCollection ) 

Dies gibt eine CollectionReferenz zurück, die Lesezugriff auf die Elemente gewährt, aber eine Ausnahme auslöst, wenn Änderungen versucht werden, was sie ein bisschen wie constin C ++ macht

Pocketdora
quelle
8

Java finalfunktioniert nur mit primitiven Typen und Referenzen, niemals mit Objektinstanzen selbst, in denen das Schlüsselwort const für irgendetwas funktioniert.

Der Vergleich const list<int> melist;mit final List<Integer> melist;der ersten macht es unmöglich, die Liste zu ändern, während die letztere Sie nur daran hindert, eine neue Liste zuzuweisen melist.

josefx
quelle
3

Abgesehen von bestimmten und subtilen Multithreading-Eigenschaften müssen deklarierte Variablen bei der Deklaration finalnicht initialisiert werden!

dh Dies ist gültig in Java:

// declare the variable
final int foo;

{
    // do something...

    // and then initialize the variable
    foo = ...;
}

Dies wäre nicht gültig, wenn mit C ++ geschrieben const.

Antak
quelle
2

Laut Wikipedia :

  • In C ++ ist ein const-Feld nicht nur vor einer Neuzuweisung geschützt, sondern es gibt auch die zusätzliche Einschränkung, dass nur const-Methoden aufgerufen und nur als const-Argument anderer Methoden übergeben werden können.
  • Nicht statische innere Klassen können frei auf jedes Feld der umschließenden Klasse zugreifen, ob endgültig oder nicht.
KevenK
quelle
1
Das Wort "neu zugewiesen" wird in der aktuellen Version dieser Seite nicht angezeigt, und es ähnelt auch nichts Ihrem zweiten Punkt, der entweder falsch oder irrelevant ist, je nachdem, was Sie unter "Zugriff" verstehen. 'Nicht statisches Inneres' ist Doppelrede. Wikipedia ist weder für C ++ noch für Java eine normative Referenz.
Marquis von Lorne
2

Ich vermute, es heißt "grob", weil die Bedeutung von constin C ++ kompliziert wird, wenn Sie über Zeiger sprechen, dh konstante Zeiger vs. Zeiger auf konstante Objekte. Da es in Java keine "expliziten" Zeiger gibt, treten finaldiese Probleme nicht auf.

Dima
quelle
1

Lassen Sie mich anhand eines Beispiels für eine switch / case-Anweisung erklären, was ich verstanden habe.

Die Werte in jeder case-Anweisung müssen Konstantenwerte zur Kompilierungszeit desselben Datentyps wie der Schaltwert sein.

Deklarieren Sie etwas wie das Folgende (entweder in Ihrer Methode als lokale Instanzen oder in Ihrer Klasse als statische Variable (fügen Sie dann statische hinzu) oder eine Instanzvariable.

final String color1 = "Red";

und

static final String color2 = "Green";

switch (myColor) { // myColor is of data type String
    case color1:
    //do something here with Red
    break;
    case color2:
    //do something with Green
    break;
}

Dieser Code wird nicht kompiliert, wenn color1es sich um eine Klassen- / Instanzvariable und nicht um eine lokale Variable handelt. Dies wird kompiliert, wenn color1es als statische endgültige Variable definiert ist (dann wird es zur statischen endgültigen Variablen).

Wenn es nicht kompiliert wird, wird der folgende Fehler angezeigt

error: constant string expression required
Subbu Mahadevan
quelle
-7

Das Schlüsselwort "const" bedeutet, dass Ihre Variable im ROM gespeichert ist (mit Mikroprozessor). Im Computer wird Ihre Variable im RAM-Bereich für Assembly-Code gespeichert (schreibgeschützter RAM). Dies bedeutet, dass sich Ihre Variable nicht im beschreibbaren RAM befindet. Dazu gehören: statischer Speicher, Stapelspeicher und Heapspeicher.

Das Schlüsselwort "final" bedeutet, dass Ihre Variable im beschreibbaren RAM gespeichert ist. Sie werden jedoch vom Compiler darauf hingewiesen, dass Ihre Variable nur einmal geändert wird.

//in java language you can use:
static final int i =10;
i =11; //error is showed here by compiler

//the same in C++ the same as follows
int i =10;
const int &iFinal = i;

iFinal = 11; //error is showed here by compiler the same as above

Ich denke, "const" hat eine schlechte Leistung, daher verwendet Java es nicht.

HungNM2
quelle