La finale de Java contre la const de C ++

151

Le didacticiel Java pour les programmeurs C ++ dit que (le point culminant est le mien):

Le mot clé final est à peu près équivalent à const en C ++

Que signifie «grosso modo» dans ce contexte? Ne sont-ils pas exactement les mêmes?

Quelles sont les différences, le cas échéant?

WinWin
la source

Réponses:

195

En C ++, le marquage d'une fonction membre constsignifie qu'elle peut être appelée sur des constinstances. Java n'a pas d'équivalent à cela. Par exemple:

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

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

Les valeurs peuvent être attribuées, une fois, plus tard uniquement en Java, par exemple:

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

est légal en Java, mais pas en C ++ alors que:

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

Dans les deux variables membres Java et C ++ peuvent être final/ constrespectivement. Il faut leur donner une valeur au moment où une instance de la classe est terminée en cours de construction.

En Java, ils doivent être définis avant que le constructeur ait terminé, cela peut être réalisé de deux manières:

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

En C ++, vous devrez utiliser des listes d'initialisation pour donner constune valeur aux membres:

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

En Java, final peut être utilisé pour marquer des choses comme non remplaçables. C ++ (pré-C ++ 11) ne le fait pas. Par exemple:

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

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

Mais en C ++:

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

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

c'est bien, car la sémantique du marquage d'une fonction membre constest différente. (Vous pouvez également surcharger en ne disposant que de l' constune des fonctions membres. (Notez également que C ++ 11 permet aux fonctions membres d'être marquées comme finales, voir la section mise à jour C ++ 11)


Mise à jour C ++ 11:

C ++ 11 vous permet en fait de marquer à la fois les classes et les fonctions membres comme final , avec une sémantique identique à la même fonctionnalité en Java, par exemple en Java:

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

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

Peut maintenant être écrit exactement en C ++ 11 comme:

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

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

J'ai dû compiler cet exemple avec une pré-version de G ++ 4.7. Notez que cela ne remplace pas constdans ce cas, mais plutôt l'augmente, fournissant le comportement de type Java qui n'a pas été vu avec le mot clé C ++ équivalent le plus proche. Donc, si vous vouliez qu'une fonction membre soit à la fois finaletconst vous feriez:

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

(L'ordre de const et finalici est obligatoire).

Auparavant, il n'y avait pas d'équivalent direct de const fonctions membres, bien que rendre les fonctions non - virtualserait une option potentielle bien que sans causer d'erreur au moment de la compilation.

De même le Java:

public final class Bar {
}

public class Error extends Bar {
}

devient en C ++ 11:

class Bar final {
};

class Error : public Bar {
};

(Précédemment private constructeurs étaient probablement les plus proches que vous pouviez obtenir en C ++)

Fait intéressant, afin de maintenir la compatibilité ascendante avec le code pré-C ++ 11, ce final n'est pas un mot clé de la manière habituelle. (Prenons l'exemple trivial et légal du C ++ 98 struct final;pour voir pourquoi en faire un mot clé casserait le code)

Flexo
la source
3
Vous devriez rendre ces méthodes virtuelles; sinon, vous ne faites vraiment pas la même chose
BlueRaja - Danny Pflughoeft
1
Dans votre dernier exemple, vous ce que vous avez est légal, mais il vaut la peine de mentionner que ce final int a; a = 10; a = 11;n'est pas (cela étant le but de en finaltant que modificateur de variable.) De plus, les membres finaux d'une classe ne peuvent être définis qu'au moment de la déclaration, ou une fois dans un constructeur .
corsiKa
2
Notez que C ++ 0x ajoute le finaldécorateur de fonction membre dans ce but précis. VC ++ 2005, 2008 et 2010 l'ont déjà implémenté, en utilisant le mot-clé contextual sealedplutôt que final.
ildjarn
@ildjarn - c'est intéressant à savoir et encore un autre changement relativement petit C ++ 0x dont je n'étais pas au courant! J'ajouterai probablement un petit commentaire quelque part dans le texte indiquant que cela change avec C ++ 0x.
Flexo
1
Apparemment, les gens font toujours s / const / final / g dans les bases de code avec finalructor comme résultat!
Flexo
30

En Java, le mot-clé final peut être utilisé pour quatre choses:

  • sur une classe ou une méthode pour la sceller (aucune sous-classe / remplacement autorisé)
  • sur une variable membre pour déclarer qu'elle peut être définie exactement une fois (je pense que c'est ce dont vous parlez)
  • sur une variable déclarée dans une méthode, pour s'assurer qu'elle peut être définie exactement une fois
  • sur un paramètre de méthode, pour déclarer qu'il ne peut pas être modifié dans la méthode

Une chose importante est: Une variable de membre final Java doit être définie exactement une fois! Par exemple, dans un constructeur, une déclaration de champ ou un initiateur. (Mais vous ne pouvez pas définir une variable de membre finale dans une méthode).

Une autre conséquence de la définition finale d'une variable membre concerne le modèle de mémoire, ce qui est important si vous travaillez dans un environnement threadé.

Ralph
la source
Qu'entendez-vous par «modèle de mémoire»? Je ne comprends pas.
Tony
1
@Tony: Spécification du langage Java, chapitre 17.4. Memory Model - docs.oracle.com/javase/specs/jls/se8/html/index.html - googles first hit
Ralph
27

Un constobjet ne peut appeler que des constméthodes et est généralement considéré comme immuable.

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)

Un finalobjet ne peut pas être défini sur un nouvel objet, mais il n'est pas immuable - rien n'empêche quelqu'un d'appeler des setméthodes.

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

Java n'a aucun moyen inhérent de déclarer les objets immuables; vous devez concevoir la classe comme immuable vous-même.

Lorsque la variable est de type primitif, final/ constfonctionne de la même manière.

const int a = 10; //C++
final int a = 10; //Java
a = 11; //Invalid in both languages
BlueRaja - Danny Pflughoeft
la source
3
C'est aussi une excellente réponse (comme beaucoup d'autres ici). Malheureusement, je ne peux accepter qu'une seule réponse. :)
WinWin le
1
Réponse parfaite!
ADJ
13

Java final équivaut à C ++ const sur les types valeur primitifs.

Avec les types de référence Java, le mot clé final est équivalent à un pointeur const ... ie

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

//C++
const int constInt = 5;
MyObject * const constPointer = new MyObject();
James Schek
la source
"le mot-clé final équivaut à un pointeur const" bien dit
ADJ
8

Vous avez déjà de bonnes réponses ici, mais un point qui semblait valoir la peine d'être ajouté: consten C ++, est couramment utilisé pour empêcher d'autres parties du programme de modifier l'état des objets. Comme cela a été souligné, finalen java ne peut pas faire cela (sauf pour les primitives) - cela empêche simplement la référence d'être changée en un objet différent. Mais si vous utilisez a Collection, vous pouvez empêcher les modifications de vos objets en utilisant la méthode statique

 Collection.unmodifiableCollection( myCollection ) 

Cela renvoie une Collectionréférence qui donne un accès en lecture aux éléments, mais lève une exception si des modifications sont tentées, ce qui en fait un peu comme consten C ++

pocketdora
la source
8

Java ne finalfonctionne que sur les types primitifs et les références, jamais sur les instances d'objet elles-mêmes où le mot-clé const fonctionne sur quoi que ce soit.

Comparer const list<int> melist;avec final List<Integer> melist;le premier rend impossible la modification de la liste, tandis que le second vous empêche seulement d'attribuer une nouvelle liste à melist.

josefx
la source
3

En plus d'avoir certaines propriétés multi-threading subtiles , les variables déclarées finaln'ont pas besoin d'être initialisées lors de la déclaration!

ie Ceci est valable en Java:

// declare the variable
final int foo;

{
    // do something...

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

Ce ne serait pas valide s'il était écrit avec du C ++ const.

antak
la source
2

Selon wikipedia :

  • En C ++, un champ const est non seulement protégé contre la réaffectation, mais il existe une limitation supplémentaire selon laquelle seules les méthodes const peuvent être appelées dessus et il ne peut être passé que comme argument const des autres méthodes.
  • Les classes internes non statiques peuvent accéder librement à n'importe quel champ de la classe englobante, finale ou non.
KevenK
la source
1
Le mot «réaffecté» n'apparaît pas dans la version actuelle de cette page, et ne fait rien non plus qui ressemble à votre deuxième point, qui est soit incorrect, soit non pertinent, selon ce que vous entendez par «accès». «Intérieur non statique» est un double langage. Wikipedia n'est pas une référence normative pour C ++ ou Java.
Marquis of Lorne
2

Je suppose que cela dit "à peu près" parce que la signification de consten C ++ se complique lorsque vous parlez de pointeurs, c'est-à-dire de pointeurs constants par rapport aux pointeurs vers des objets constants. Puisqu'il n'y a pas de pointeurs "explicites" en Java, finaln'a pas ces problèmes.

Dima
la source
1

Laissez-moi vous expliquer ce que j'ai compris avec un exemple de déclaration switch / case.

Les valeurs de chaque instruction case doivent être des valeurs constantes au moment de la compilation du même type de données que la valeur de commutation.

déclarez quelque chose comme ci-dessous (soit dans votre méthode en tant qu'instances locales, soit dans votre classe en tant que variable statique (ajoutez-y alors de la statique), ou une variable d'instance.

final String color1 = "Red";

et

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;
}

Ce code ne sera pas compilé s'il color1s'agit d'une variable de classe / instance et non d'une variable locale. Cela compilera si color1est défini comme statique final (alors il devient une variable finale statique).

Quand il ne compile pas, vous obtiendrez l'erreur suivante

error: constant string expression required
Subbu Mahadevan
la source
-7

Le mot-clé "const" signifie que votre variable est enregistrée dans la ROM (avec microprocesseur). dans l'ordinateur, votre variable est enregistrée dans la zone RAM pour le code d'assemblage (lecture seule RAM). cela signifie que votre variable n'est pas dans la RAM inscriptible: mémoire statique, mémoire de pile et mémoire de tas.

Le mot-clé "final" signifie que votre variable est enregistrée dans la RAM inscriptible, mais vous remarquez au compilateur que votre variable n'est modifiée qu'une seule fois.

//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

Je pense que "const" est mauvais en termes de performances, donc Java ne l'utilise pas.

HungNM2
la source