Costante di Java vs. const di C ++


151

Il tutorial dei programmatori Java per C ++ dice che (il momento saliente è mio):

La parola chiave final è approssimativamente equivalente a const in C ++

Cosa significa "approssimativamente" in questo contesto? Non sono esattamente gli stessi?

Quali sono le differenze, se ce ne sono?

Risposte:


195

Nella marcatura C ++ una funzione membro constsignifica che può essere chiamata su constistanze. Java non ha un equivalente a questo. Per esempio:

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

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

I valori possono essere assegnati, una volta, successivamente solo in Java, ad esempio:

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

è legale in Java, ma non in C ++ mentre:

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

In Java e C ++ le variabili membro possono essere final/ constrispettivamente. A questi deve essere assegnato un valore al termine della costruzione di un'istanza della classe.

In Java devono essere impostati prima che il costruttore abbia terminato, ciò può essere ottenuto in due modi:

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

In C ++ dovrai usare gli elenchi di inizializzazione per dare constun valore ai membri:

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

In Java final può essere utilizzato per contrassegnare le cose come non sostituibili. C ++ (pre-C ++ 11) non lo fa. Per esempio:

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

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

Ma in C ++:

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

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

questo va bene, perché la semantica della marcatura di una funzione membro constè diversa. (Potresti anche sovraccaricare avendo solo constuna delle funzioni membro. (Notare anche che C ++ 11 consente alle funzioni membro di essere contrassegnate come finali, vedere la sezione di aggiornamento di C ++ 11)


Aggiornamento C ++ 11:

C ++ 11 infatti consente di contrassegnare sia le classi che le funzioni membro come final, con semantica identica alla stessa funzionalità in Java, ad esempio in Java:

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

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

Ora può essere scritto esattamente in C ++ 11 come:

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

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

Ho dovuto compilare questo esempio con una pre-release di G ++ 4.7. Si noti che questo non sostituisce constin questo caso, ma piuttosto lo aumenta, fornendo il comportamento simile a Java che non è stato visto con la parola chiave C ++ equivalente più vicina. Quindi, se si desidera che una funzione membro sia entrambe finale constsi farebbe:

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

(L'ordine di constefinal qui è obbligatorio).

In precedenza non esisteva un equivalente diretto delle constfunzioni membro, sebbene le funzioni non fosserovirtual sarebbe una potenziale opzione sebbene senza causare un errore in fase di compilazione.

Allo stesso modo Java:

public final class Bar {
}

public class Error extends Bar {
}

diventa in C ++ 11:

class Bar final {
};

class Error : public Bar {
};

(In precedenza private costruttori erano probabilmente i più vicini a questo in C ++)

È interessante notare che per mantenere la retrocompatibilità con il codice pre-C ++ 11 final non è una parola chiave nel solito modo. (Prendi l'esempio banale e legale di C ++ 98 struct final;per capire perché trasformarlo in una parola chiave romperebbe il codice)


3
Dovresti rendere virtuali quei metodi; altrimenti, non stai davvero facendo la stessa cosa
BlueRaja - Danny Pflughoeft

1
Nel tuo ultimo esempio, quello che hai è legale, ma vale la pena ricordare che final int a; a = 10; a = 11;non lo è (che è lo scopo di finalcome modificatore di variabile). Inoltre, i membri finali di una classe possono essere impostati solo al momento della dichiarazione o una volta in un costruttore .
corsiKa

2
Si noti che C ++ 0x aggiunge il finaldecoratore della funzione membro per questo preciso scopo. VC ++ 2005, 2008 e 2010 lo hanno già implementato, usando la parola chiave contestuale sealedanziché final.
ildjarn,

@ildjarn -è interessante sapere e ancora un'altra modifica C ++ 0x relativamente piccola di cui non ero a conoscenza! Probabilmente aggiungerò un piccolo commento da qualche parte nel testo indicando che questo sta cambiando con C ++ 0x.
Flexo

1
Apparentemente le persone fanno ancora s / const / final / g in codebase con finalructor come risultato!
Flexo

30

In Java la parola chiave finale può essere utilizzata per quattro cose:

  • su una classe o un metodo per sigillarlo (nessuna sottoclasse / sostituzione consentita)
  • su una variabile membro per dichiarare che può essere impostata esattamente una volta (penso che questo sia ciò di cui stai parlando)
  • su una variabile dichiarata in un metodo, per assicurarsi che possa essere impostata esattamente una volta
  • su un parametro di metodo, per dichiarare che non può essere modificato all'interno del metodo

Una cosa importante è: una variabile membro finale Java deve essere impostata esattamente una volta! Ad esempio, in un costruttore, in una dichiarazione di campo o in un inizializzatore. (Ma non è possibile impostare una variabile membro finale in un metodo).

Un'altra conseguenza della creazione di una variabile membro finale riguarda il modello di memoria, che è importante se si lavora in un ambiente con thread.


Cosa intendi con "modello di memoria"? Non capisco
Tony,

1
@Tony: specifica del linguaggio Java, capitolo 17.4. Modello di memoria - docs.oracle.com/javase/specs/jls/se8/html/index.html - primo colpo di googles
Ralph

27

Un constoggetto può solo chiamare constmetodi ed è generalmente considerato immutabile.

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 finaloggetto non può essere impostato su un nuovo oggetto, ma non è immutabile: non c'è nulla che impedisce a qualcuno di chiamare alcun setmetodo.

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

Java non ha un modo intrinseco di dichiarare gli oggetti immutabili; devi progettare la classe come te stesso immutabile.

Quando la variabile è di tipo primitivo final/ constfunziona allo stesso modo.

const int a = 10; //C++
final int a = 10; //Java
a = 11; //Invalid in both languages

3
Anche questa è un'ottima risposta (come molti altri qui). Sfortunatamente, posso accettare solo una risposta. :)
WinWin

1
Risposta perfetta!
ADJ,

13

Java final equivale a C ++ const su tipi di valore primitivi.

Con i tipi di riferimento Java, la parola chiave finale equivale a un puntatore const ... ad es

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

//C++
const int constInt = 5;
MyObject * const constPointer = new MyObject();

"la parola finale è equivalente ad un puntatore const" ben detto
ADJ

8

Hai già delle ottime risposte qui, ma un punto che sembrava degno di essere aggiunto: constin C ++ è comunemente usato per impedire ad altre parti del programma di cambiare lo stato degli oggetti. Come è stato sottolineato, finalin java non può farlo (tranne che per le primitive) - impedisce semplicemente che il riferimento venga cambiato in un oggetto diverso. Ma se si utilizza a Collection, è possibile impedire le modifiche agli oggetti utilizzando il metodo statico

 Collection.unmodifiableCollection( myCollection ) 

Ciò restituisce un Collectionriferimento che fornisce l'accesso in lettura agli elementi, ma genera un'eccezione se si tentano modifiche, rendendolo un po 'come constin C ++


8

Java finalfunziona solo su tipi e riferimenti primitivi, mai su istanze di oggetti stesse in cui la parola chiave const funziona su qualsiasi cosa.

Il confronto const list<int> melist;con final List<Integer> melist;il primo rende impossibile modificare l'elenco, mentre quest'ultimo impedisce solo di assegnare un nuovo elenco a melist.


3

Oltre ad avere proprietà multi-threading certe e sottili , le variabili dichiarate finalnon devono essere inizializzate sulla dichiarazione!

cioè Questo è valido in Java:

// declare the variable
final int foo;

{
    // do something...

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

Questo non sarebbe valido se scritto con C ++ const.


2

Secondo Wikipedia :

  • In C ++, un campo const non è solo protetto dalla riassegnazione, ma c'è la limitazione aggiuntiva che solo i metodi const possono essere richiamati su di esso e può essere passato solo come argomento const di altri metodi.
  • Le classi interne non statiche possono accedere liberamente a qualsiasi campo della classe che lo racchiude, finale o no.

1
La parola "riassegnata" non appare nella versione corrente di quella pagina, e nemmeno qualcosa che assomigli al tuo secondo punto, che è errato o irrilevante, a seconda di cosa intendi per "accesso". "Interno non statico" è un doppio discorso. Wikipedia non è un riferimento normativo per C ++ o Java.
Marchese di Lorne,

2

Immagino che dica "approssimativamente" perché il significato di constin C ++ diventa complicato quando parli di puntatori, cioè puntatori costanti contro puntatori a oggetti costanti. Poiché non ci sono puntatori "espliciti" in Java, finalquesti problemi non sono presenti.


1

Lasciami spiegare cosa ho capito con un esempio di istruzione switch / case.

I valori in ciascuna istruzione case devono essere valori costanti tempo di compilazione dello stesso tipo di dati del valore di commutazione.

dichiarare qualcosa di simile di seguito (o nel proprio metodo come istanze locali o nella propria classe come variabile statica (aggiungere quindi statica ad essa) o una variabile di istanza.

final String color1 = "Red";

e

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

Questo codice non verrà compilato, se color1è una variabile di classe / istanza e non una variabile locale. Questo compilerà se color1è definito come finale statico (quindi diventa variabile finale statico).

Quando non viene compilato, verrà visualizzato il seguente errore

error: constant string expression required

-7

la parola chiave "const" indica che la variabile è salvata nella ROM (con microprocessore). nel computer, la variabile viene salvata nell'area RAM per il codice assembly (sola lettura RAM). significa che la tua variabile non è nella RAM scrivibile include: memoria statica, memoria dello stack e memoria dell'heap.

la parola chiave "final" indica che la variabile viene salvata nella RAM scrivibile, ma si nota al compilatore che la variabile viene modificata solo una volta.

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

Penso che "const" sia pessimo nelle prestazioni, quindi Java non lo usa.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.