Java финал против C ++ const - PullRequest
       56

Java финал против C ++ const

142 голосов
/ 11 февраля 2011

В учебнике Java для программистов на C ++ говорится, что (выделение - мое):

Ключевое слово final примерно эквивалентно const в C ++

Что значит «примерно» в этом контексте?Разве они точно не одинаковы?

В чем различия, если таковые имеются?

Ответы [ 11 ]

182 голосов
/ 11 февраля 2011

В маркировке C ++ функция-член const означает, что она может вызываться в const экземплярах. У Java нет эквивалента этому. E.g.:

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

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

Значения могут быть назначены, один раз, позже только в Java, например ::

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

допустимо в Java, но не в C ++, тогда как:

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

В Java и C ++ переменные-члены могут быть final / const соответственно. Им должно быть присвоено значение к моменту завершения создания экземпляра класса.

В Java они должны быть установлены до завершения конструктора, это может быть достигнуто одним из двух способов:

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

В C ++ вам нужно будет использовать списки инициализации, чтобы дать const членам значение:

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

В Java final может использоваться для пометки вещей как не подлежащих переопределению. C ++ (до C ++ 11) этого не делает. E.g.:

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

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

Но в C ++:

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

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

это нормально, потому что семантика маркировки функции-члена const различна. (Вы также можете перегрузить , имея только const на одной из функций-членов. (Обратите внимание, что C ++ 11 позволяет функциям-членам быть помеченными как final, см. Раздел обновления C ++ 11)


C ++ 11 обновление:

C ++ 11 фактически позволяет вам помечать и классы, и функции-члены как final, с семантикой, идентичной той же функции в Java, например в Java:

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

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

Теперь может быть точно написано на C ++ 11 как:

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

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

Мне пришлось скомпилировать этот пример с предварительной версией G ++ 4.7. Обратите внимание, что в этом случае это не заменяет const, а скорее дополняет его, предоставляя Java-подобное поведение, которое не было видно с наиболее близким эквивалентным ключевым словом C ++. Поэтому, если вы хотите, чтобы функция-член была и final, и const, вы бы сделали:

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

(здесь требуется заказ const и final).

Раньше не было прямого эквивалента const функций-членов, хотя создание функций, не являющихся virtual, было бы потенциальной опцией, хотя и не вызывало ошибки во время компиляции.

Аналогично Java:

public final class Bar {
}

public class Error extends Bar {
}

становится в C ++ 11:

class Bar final {
};

class Error : public Bar {
};

(Ранее private конструкторы были, вероятно, самыми близкими к C ++)

Интересно, что для обеспечения обратной совместимости с кодом до C ++ 11 final не является ключевым словом обычным способом. (Возьмем тривиальный, допустимый пример C ++ 98 struct final;, чтобы понять, почему его использование в качестве ключевого слова может нарушить код)

29 голосов
/ 11 февраля 2011

В Java последнее ключевое слово может использоваться для четырех вещей:

  • для класса или метода для его опечатывания (подклассы / переопределение не допускаются)
  • для переменной-члена, чтобы объявить, что она может быть установлена ​​ровно один раз (я думаю, это то, о чем вы говорите)
  • для переменной, объявленной в методе, чтобы убедиться, что она может быть установлена ​​ровно один раз
  • для параметра метода, чтобы объявить, что он не может быть изменен в методе

Одна важная вещь: Конечная переменная-член Java должна быть установлена ​​ровно один раз! Например, в конструкторе, объявлении поля или инициализаторе. (Но вы не можете установить конечную переменную-член в методе).

Еще одно последствие создания окончательной переменной-члена относится к модели памяти, что важно, если вы работаете в многопоточной среде.

25 голосов
/ 11 февраля 2011

A const объект может вызывать только const методы, и обычно считается неизменным.

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)

A final объект не может быть установлен на новый объект, но он не является неизменным - ничто не мешает кому-либо вызывать любые set методы.

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

Java не имеет встроенного способа объявления объектов неизменными; вам нужно самим сделать класс неизменным.

Когда переменная является примитивным типом, final / const работают так же.

const int a = 10; //C++
final int a = 10; //Java
a = 11; //Invalid in both languages
12 голосов
/ 11 февраля 2011

Java final эквивалентен C ++ const в примитивных типах значений.

В ссылочных типах Java последнее ключевое слово эквивалентно константному указателю ... т.е.

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

//C++
const int constInt = 5;
MyObject * const constPointer = new MyObject();
8 голосов
/ 13 февраля 2011

У вас уже есть несколько хороших ответов, но один момент, который, кажется, стоит добавить: const в C ++ обычно используется для предотвращения изменения состояния объектов другими частями программы.Как уже указывалось, final в java не может этого сделать (за исключением примитивов) - он просто предотвращает изменение ссылки на другой объект.Но если вы используете Collection, вы можете предотвратить изменения ваших объектов, используя статический метод

 Collection.unmodifiableCollection( myCollection ) 

. Это возвращает ссылку Collection, которая дает доступ на чтение к элементам, но выдаетисключение при попытке изменения, что делает его немного похожим на const в C ++

7 голосов
/ 11 февраля 2011

Java final работает только на примитивных типах и ссылках, но не на самих экземплярах объектов, где ключевое слово const работает для всего.

Сравните const list<int> melist; с final List<Integer> melist;. Первое делает невозможным изменение списка, в то время как последнее только мешает вам назначить новый список для melist.

.
3 голосов
/ 30 июля 2014

Помимо определенных и тонких многопоточных свойств , объявленные переменные final не нужно инициализировать при объявлении!

, т.е.допустимый в Java:

// declare the variable
final int foo;

{
    // do something...

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

Это не будет действительным, если написано с C ++ const.

2 голосов
/ 11 февраля 2011

Я предполагаю, что это говорит "грубо", потому что значение const в C ++ усложняется, когда вы говорите об указателях, то есть постоянных указателях против указателей на постоянные объекты.Поскольку в Java нет «явных» указателей, final не имеет этих проблем.

2 голосов
/ 11 февраля 2011

Согласно wikipedia :

  • В C ++ поле const не только защищено от переназначения, но есть дополнительное ограничение, что могут вызываться только методы constоно и может быть передано только в качестве аргумента const других методов.
  • Нестатические внутренние классы могут свободно обращаться к любому полю включающего класса, конечному или нет.
0 голосов
/ 20 октября 2016

Позвольте мне объяснить, что я понял, на примере оператора switch / case.

Значения в каждом операторе case должны быть значениями констант времени компиляции того же типа данных, что и значение переключателя.

объявите что-то вроде ниже (либо в вашем методе как локальные экземпляры, либо в вашем классе как статическую переменную (затем добавьте статическую переменную), либо переменную экземпляра.

final String color1 = "Red";

и

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

Этот код не будет компилироваться, если color1 является переменной класса / экземпляра, а не локальной переменной. Это скомпилируется, если color1 определено как static final (тогда оно становится статической конечной переменной).

Когда он не скомпилируется, вы получите следующую ошибку

error: constant string expression required
...