Const final de Java vs. C ++


151

El tutorial para programadores de Java para C ++ dice que (lo más destacado es mío):

La palabra clave final es aproximadamente equivalente a const en C ++

¿Qué significa "aproximadamente" en este contexto? ¿No son exactamente lo mismo?

¿Cuáles son las diferencias, si hay alguna?

Respuestas:


195

En C ++, marcar una función miembro constsignifica que puede llamarse en constinstancias. Java no tiene un equivalente a esto. P.ej:

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

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

Los valores se pueden asignar, una vez, más tarde en Java solo, por ejemplo:

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

es legal en Java, pero no en C ++ mientras 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.
   }
}

Tanto en Java como en C ++, las variables miembro pueden ser final/ constrespectivamente. Es necesario darles un valor para cuando se termine de construir una instancia de la clase.

En Java se deben configurar antes de que el constructor haya terminado, esto se puede lograr de una de dos maneras:

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

En C ++ necesitará usar listas de inicialización para dar a los constmiembros un valor:

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

En Java final se puede usar para marcar cosas como no reemplazables. C ++ (pre-C ++ 11) no hace esto. P.ej:

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

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

Pero en C ++:

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

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

Esto está bien, porque la semántica de marcar una función miembro constes diferente. (También puede sobrecargar al tener solo constuna de las funciones miembro. (Tenga en cuenta también que C ++ 11 permite que las funciones miembro se marquen como finales, consulte la sección de actualización de C ++ 11)


Actualización de C ++ 11:

De hecho, C ++ 11 le permite marcar tanto las clases como las funciones miembro como final, con semántica idéntica a la misma característica en Java, por ejemplo en Java:

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

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

Ahora se puede escribir exactamente en C ++ 11 como:

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

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

Tuve que compilar este ejemplo con un prelanzamiento de G ++ 4.7. Tenga en cuenta que esto no reemplaza consten este caso, sino que lo aumenta, proporcionando el comportamiento similar a Java que no se vio con la palabra clave C ++ equivalente más cercana. Entonces, si quisieras que una función miembro fuera ambas finaly constharías:

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

(Se requiere el orden consty finalaquí).

Anteriormente no había un equivalente directo de las constfunciones miembro, aunque hacer que las funciones no virtualfueran una opción potencial, aunque sin causar un error en el momento de la compilación.

Asimismo el Java:

public final class Bar {
}

public class Error extends Bar {
}

se convierte en C ++ 11:

class Bar final {
};

class Error : public Bar {
};

(Anteriormente, los privateconstructores eran probablemente lo más cercano a esto en C ++)

Curiosamente, para mantener la compatibilidad con el código anterior a C ++ 11 final no es una palabra clave de la manera habitual. (Tome el ejemplo trivial y legal de C ++ 98 struct final;para ver por qué convertirlo en una palabra clave rompería el código)


3
Debes hacer que esos métodos sean virtuales; de lo contrario, realmente no estás haciendo lo mismo
BlueRaja - Danny Pflughoeft

1
En su último ejemplo, lo que tiene es legal, pero vale la pena mencionar que final int a; a = 10; a = 11;no lo es (ese es el propósito de finalun modificador variable). Además, los miembros finales de una clase solo se pueden establecer en el momento de la declaración, o una vez en un constructor .
corsiKa

2
Tenga en cuenta que C ++ 0x agrega el finaldecorador de funciones miembro para este propósito exacto. VC ++ 2005, 2008 y 2010 ya lo tienen implementado, utilizando la palabra clave contextual en sealedlugar de final.
ildjarn

@ildjarn: ¡es interesante saberlo y otro cambio C ++ 0x relativamente pequeño que no conocía! Probablemente agregaré un pequeño comentario en algún lugar del texto que indique que esto está cambiando con C ++ 0x.
Flexo

1
Aparentemente, la gente todavía hace s / const / final / g en bases de código con el resultado finalructor
Flexo

30

En Java, la palabra clave final se puede usar para cuatro cosas:

  • en una clase o método para sellarlo (no se permiten subclases / anulaciones)
  • en una variable miembro para declarar que se puede configurar exactamente una vez (creo que esto es de lo que estás hablando)
  • en una variable declarada en un método, para asegurarse de que se puede configurar exactamente una vez
  • en un parámetro de método, para declarar que no se puede modificar dentro del método

Una cosa importante es: ¡una variable miembro final de Java debe establecerse exactamente una vez! Por ejemplo, en un constructor, declaración de campo o intializador. (Pero no puede establecer una variable miembro final en un método).

Otra consecuencia de hacer que una variable miembro sea final se relaciona con el modelo de memoria, que es importante si trabaja en un entorno roscado.


¿Qué quieres decir con "modelo de memoria"? No lo entiendo
Tony

1
@ Tony: Java Language Specification, Capítulo 17.4. Modelo de memoria - docs.oracle.com/javase/specs/jls/se8/html/index.html - googles primer hit
Ralph

27

Un constobjeto solo puede llamar constmétodos, y generalmente se considera inmutable.

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 finalobjeto no se puede establecer en un nuevo objeto, pero no es inmutable: no hay nada que impida que alguien llame a ningún setmétodo.

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

Java no tiene una forma inherente de declarar objetos inmutables; necesita diseñar la clase como inmutable usted mismo.

Cuando la variable es un tipo primitivo, final/ consttrabajar de la misma.

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

3
Esta es una gran respuesta también (como muchas otras aquí). Lamentablemente, solo puedo aceptar una respuesta. :)
WinWin

1
¡Respuesta perfecta!
ADJ

13

Java final es equivalente a C ++ const en tipos de valores primitivos.

Con los tipos de referencia de Java, la palabra clave final es equivalente a un puntero constante ... es decir

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

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

"la palabra clave final es equivalente a un puntero constante" bien dicho
ADJ

8

Ya tiene algunas respuestas excelentes aquí, pero un punto que parecía valioso agregar: consten C ++ se usa comúnmente para evitar que otras partes del programa cambien el estado de los objetos. Como se ha señalado, finalen Java no se puede hacer esto (excepto para las primitivas), solo evita que la referencia se cambie a un objeto diferente. Pero si está utilizando a Collection, puede evitar cambios en sus objetos utilizando el método estático

 Collection.unmodifiableCollection( myCollection ) 

Esto devuelve una Collectionreferencia que da acceso de lectura a los elementos, pero arroja una excepción si se intentan modificaciones, lo que lo hace un poco como consten C ++


8

De Java final funciona solo en tipos y referencias primitivas, nunca en instancias de objetos en las que la palabra clave const funcione en nada.

Comparar const list<int> melist;con final List<Integer> melist;el primero hace que sea imposible modificar la lista, mientras que el último solo le impide asignar una nueva lista a melist.


3

¡Además de tener ciertas y sutiles propiedades de subprocesos múltiples , las variables declaradas finalno necesitan inicializarse en la declaración!

es decir, esto es válido en Java:

// declare the variable
final int foo;

{
    // do something...

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

Esto no sería válido si se escribiera con C ++ const.


2

De acuerdo con wikipedia :

  • En C ++, un campo const no solo está protegido contra la reasignación, sino que existe la limitación adicional de que solo se pueden invocar métodos const y solo se puede pasar como argumento const de otros métodos.
  • Las clases internas no estáticas pueden acceder libremente a cualquier campo de la clase adjunta, final o no.

1
La palabra 'reasignado' no aparece en la versión actual de esa página, y tampoco hay nada parecido a su segundo punto, que es incorrecto o irrelevante, dependiendo de lo que quiera decir con 'acceso'. 'Interior no estático' es doble conversación. Wikipedia no es una referencia normativa para C ++ o Java.
Marqués de Lorne

2

Supongo que dice "aproximadamente" porque el significado de consten C ++ se complica cuando se habla de punteros, es decir, punteros constantes frente a punteros a objetos constantes. Como no hay punteros "explícitos" en Java, finalno tiene estos problemas.


1

Permítanme explicar lo que entendí con un ejemplo de cambio / declaración de caso.

Los valores en cada declaración de caso deben ser valores constantes en tiempo de compilación del mismo tipo de datos que el valor del interruptor.

declare algo como a continuación (ya sea en su método como instancias locales, o en su clase como variable estática (agregue estática entonces) o una variable de instancia.

final String color1 = "Red";

y

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

Este código no se compilará si color1es una variable de clase / instancia y no una variable local. Esto se compilará si color1se define como estático final (entonces se convierte en variable estática final).

Cuando no se compila, recibirá el siguiente error

error: constant string expression required

-7

La palabra clave "const" significa que su variable se guarda en ROM (con microprocesador). en la computadora, su variable se guarda en el área RAM para el código de ensamblaje (solo lectura RAM). significa que su variable no está en la memoria RAM grabable incluye: memoria estática, memoria de pila y memoria de montón.

La palabra clave "final" significa que su variable se guarda en la memoria RAM que se puede escribir, pero observa al compilador que su variable solo cambia una vez.

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

Creo que "const" es malo en rendimiento, por lo que Java no lo usa.

Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.