Const final de Java vs. const de C ++

151

O tutorial para programadores Java para C ++ diz que (o destaque é o meu):

A palavra-chave final é aproximadamente equivalente a const em C ++

O que significa "grosso modo" neste contexto? Eles não são exatamente iguais?

Quais são as diferenças, se houver?

WinWin
fonte

Respostas:

195

Na marcação C ++, uma função de membro constsignifica que ela pode ser chamada em constinstâncias. Java não tem um equivalente a isso. Por exemplo:

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

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

Os valores podem ser atribuídos, uma vez, posteriormente em Java, por exemplo:

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

é legal em Java, mas não em C ++, enquanto 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 em Java quanto em C ++, as variáveis ​​de membro podem ser final/ constrespectivamente. Eles precisam receber um valor no momento em que uma instância da classe termina de ser construída.

Em Java, eles devem ser definidos antes da conclusão do construtor, isso pode ser alcançado de uma de duas maneiras:

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

No C ++, você precisará usar as listas de inicialização para fornecer aos constmembros um valor:

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

Em Java, final pode ser usado para marcar as coisas como não substituíveis. C ++ (pré-C ++ 11) não faz isso. Por exemplo:

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

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

Mas em C ++:

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

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

isso é bom, porque a semântica de marcar uma função de membro consté diferente. (Você também pode sobrecarregar tendo apenas constuma das funções de membro. (Observe também que o C ++ 11 permite que as funções de membro sejam marcadas como finais, consulte a seção de atualização do C ++ 11)


Atualização do C ++ 11:

De fato, o C ++ 11 permite marcar classes e funções-membro como final, com semântica idêntica ao mesmo recurso em Java, por exemplo, em Java:

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

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

Agora pode ser escrito exatamente em C ++ 11 como:

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

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

Eu tive que compilar este exemplo com um pré-lançamento do G ++ 4.7. Observe que isso não substitui constnesse caso, mas aumenta, fornecendo o comportamento semelhante ao Java que não foi visto com a palavra-chave C ++ equivalente mais próxima. Portanto, se você quisesse que uma função membro fosse ambas finale constvocê faria:

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

(A ordem de conste finalaqui é obrigatória).

Anteriormente, não havia um equivalente direto de constfunções-membro, embora tornar as funções não- virtualseria uma opção em potencial, sem causar um erro no tempo de compilação.

Da mesma forma que o Java:

public final class Bar {
}

public class Error extends Bar {
}

torna-se em C ++ 11:

class Bar final {
};

class Error : public Bar {
};

(Anteriormente, os privateconstrutores eram provavelmente o mais próximo possível disso em C ++)

Curiosamente, para manter a compatibilidade com versões anteriores ao código pré-C ++ 11 final não é uma palavra-chave da maneira usual. (Pegue o exemplo trivial e legal do C ++ 98 struct final;para ver por que torná-lo uma palavra-chave quebraria o código)

Flexo
fonte
3
Você deve tornar esses métodos virtuais; caso contrário, você realmente não está fazendo a mesma coisa
BlueRaja - Danny Pflughoeft
1
No seu último exemplo, você é legal, mas vale a pena mencionar que final int a; a = 10; a = 11;não é (sendo esse o objetivo de finalum modificador de variável). Além disso, os membros finais de uma classe só podem ser configurados no momento da declaração ou uma vez em um construtor. .
CorsiKa
2
Observe que o C ++ 0x adiciona o finaldecorador da função de membro para esse propósito exato. O VC ++ 2005, 2008 e 2010 já tem isso implementado, usando a palavra-chave contextual em sealedvez de final.
Ildjarn
@ildjarn - é interessante saber e ainda outra mudança relativamente pequena em C ++ 0x que eu não sabia! Provavelmente adicionarei um pequeno comentário em algum lugar do texto, indicando que isso está mudando com o C ++ 0x.
Flexo
1
Aparentemente, as pessoas ainda fazem s / const / final / g em bases de código com o finalructor como resultado!
Flexo
30

Em Java, a palavra-chave final pode ser usada para quatro coisas:

  • em uma classe ou método para selá-lo (nenhuma subclasse / substituição permitida)
  • em uma variável membro para declarar que pode ser definido exatamente uma vez (acho que é disso que você está falando)
  • em uma variável declarada em um método, para garantir que ela possa ser definida exatamente uma vez
  • em um parâmetro de método, para declarar que não pode ser modificado dentro do método

Uma coisa importante é: Uma variável de membro final Java deve ser definida exatamente uma vez! Por exemplo, em um construtor, declaração de campo ou intializador. (Mas você não pode definir uma variável de membro final em um método).

Outra consequência de tornar uma variável de membro final está relacionada ao modelo de memória, o que é importante se você trabalhar em um ambiente encadeado.

Ralph
fonte
O que você quer dizer com "modelo de memória"? Eu não entendo.
Tony
1
@Tony: Especificação da Linguagem Java, Capítulo 17.4. Modelo de memória - docs.oracle.com/javase/specs/jls/se8/html/index.html - o primeiro hit do googles
Ralph
27

Um constobjeto pode chamar apenas constmétodos e geralmente é considerado imutável.

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)

Um finalobjeto não pode ser definido como um novo objeto, mas não é imutável - não há nada que impeça alguém de chamar qualquer setmétodo.

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

Java não possui uma maneira inerente de declarar objetos imutáveis; você precisa criar a classe como imutável.

Quando a variável é um tipo primitivo, final/ consttrabalha da mesma maneira.

const int a = 10; //C++
final int a = 10; //Java
a = 11; //Invalid in both languages
BlueRaja - Danny Pflughoeft
fonte
3
Esta é uma ótima resposta também (como muitas outras aqui). Infelizmente, só posso aceitar uma resposta. :)
WinWin
1
Resposta perfeita!
ADJ
13

Java final é equivalente a C ++ const em tipos de valores primitivos.

Nos tipos de referência Java, a palavra-chave final é equivalente a um ponteiro const ...

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

//C++
const int constInt = 5;
MyObject * const constPointer = new MyObject();
James Schek
fonte
"a palavra-chave final é equivalente a um ponteiro const" bem dito
ADJ
8

Você já tem ótimas respostas aqui, mas um ponto que parecia valer a pena acrescentar: constno C ++ é comumente usado para impedir que outras partes do programa alterem o estado dos objetos. Como foi apontado, finalno java não é possível fazer isso (exceto os primitivos) - apenas impede que a referência seja alterada para um objeto diferente. Mas se você estiver usando a Collection, poderá impedir alterações nos seus objetos usando o método estático

 Collection.unmodifiableCollection( myCollection ) 

Isso retorna uma Collectionreferência que fornece acesso de leitura aos elementos, mas lança uma exceção se forem tentadas modificações, tornando-o um pouco como constem C ++

pocketdora
fonte
8

O Java finalfunciona apenas em tipos e referências primitivas, nunca nas próprias instâncias de objetos em que a palavra-chave const funciona em qualquer coisa.

Comparar const list<int> melist;com final List<Integer> melist;o primeiro torna impossível modificar a lista, enquanto o último apenas impede que você atribua uma nova lista melist.

josefx
fonte
3

Além de ter certas e sutis propriedades multithreading , as variáveis ​​declaradas finalnão precisam ser inicializadas na declaração!

isto é válido em Java:

// declare the variable
final int foo;

{
    // do something...

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

Isso não seria válido se escrito com C ++ 's const.

Antak
fonte
2

De acordo com a wikipedia :

  • No C ++, um campo const não é apenas protegido contra redesignação, mas há a limitação adicional de que apenas os métodos const podem ser chamados nele e somente podem ser passados ​​como o argumento const de outros métodos.
  • Classes internas não estáticas podem acessar livremente qualquer campo da classe anexa, final ou não.
KevenK
fonte
1
A palavra 'reatribuído' não aparece na versão atual dessa página e nada se assemelha ao seu segundo ponto, que é incorreto ou irrelevante, dependendo do que você quer dizer com 'acesso'. 'Interior não estático' é uma conversa dupla. A Wikipedia não é uma referência normativa para C ++ ou Java.
Marquês de Lorne
2

Eu acho que ele diz "aproximadamente" porque o significado de constem C ++ fica complicado quando você fala sobre ponteiros, ou seja, ponteiros constantes versus ponteiros para objetos constantes. Como não há ponteiros "explícitos" em Java, finalnão há esses problemas.

Dima
fonte
1

Deixe-me explicar o que entendi com um exemplo de instrução switch / case.

Os valores em cada instrução de caso devem ser valores constantes em tempo de compilação do mesmo tipo de dados que o valor do comutador.

declare algo como abaixo (no seu método como instâncias locais ou na sua classe como variável estática (adicione estática a ele então) ou uma variável de instância.

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

Este código não será compilado, se color1for uma variável de classe / instância e não uma variável local. Isso será compilado se color1definido como estático final (então se tornará variável final estática).

Quando não é compilado, você receberá o seguinte erro

error: constant string expression required
Subbu Mahadevan
fonte
-7

A palavra-chave "const" significa que sua variável é salva na ROM (com microprocessador). no computador, sua variável é salva na área RAM para o código Assembly (RAM somente leitura). isso significa que sua variável não está na RAM gravável: memória estática, memória de pilha e memória de pilha.

A palavra-chave "final" significa que sua variável é salva na RAM gravável, mas você percebe no compilador que sua variável é alterada apenas uma 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

Eu acho que "const" é ruim no desempenho, então o Java não o usa.

HungNM2
fonte