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


151

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

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

Что значит «грубо» в этом контексте? Разве они точно так же?

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

Ответы:


195

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

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 финал может использоваться, чтобы пометить вещи как не подлежащие переопределению. C ++ (до C ++ 11) этого не делает. Например:

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функций-членов, хотя создание функций non- 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;чтобы понять, почему создание ключевого слова может привести к поломке кода)


3
Вы должны сделать эти методы виртуальными; в противном случае вы действительно не делаете то же самое
BlueRaja - Дэнни Пфлугхофт

1
В последнем примере вы имеете то, что имеете, допустимо, но стоит упомянуть, что final int a; a = 10; a = 11;это не так (это является целью finalмодификатора переменной). Кроме того, конечные члены класса могут быть установлены только во время объявления или один раз в конструкторе. ,
CorsiKa

2
Обратите внимание, что C ++ 0x добавляет finalдекоратор-член для этой конкретной цели. VC ++ 2005, 2008 и 2010 уже реализовали это, используя sealedвместо этого контекстное ключевое слово final.
ildjarn

@ildjarn - это интересно знать, и еще одно относительно небольшое изменение в C ++ 0x, о котором я не знал! Я, вероятно, добавлю небольшой комментарий где-то в тексте, указывающий, что это меняется с C ++ 0x.
Флексо

1
Очевидно, что люди все еще делают s / const / final / g в базах кодов с помощью finalructor !
Flexo

30

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

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

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

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


Что вы подразумеваете под «моделью памяти»? Я не понимаю
Тони

1
@Tony: спецификация языка Java, глава 17.4. Память Модель - docs.oracle.com/javase/specs/jls/se8/html/index.html - Googles первый хит
Ральф

27

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)

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

3
Это тоже отличный ответ (как и многие другие здесь). К сожалению, я могу принять только один ответ. :)
WinWin

1
Идеальный ответ!
ADJ

13

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();

«последнее ключевое слово эквивалентно
ADJ

8

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

 Collection.unmodifiableCollection( myCollection ) 

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


8

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

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


3

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

т.е. это действительно в Java:

// declare the variable
final int foo;

{
    // do something...

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

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


2

Согласно википедии :

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

1
Слово «переназначено» не появляется в текущей версии этой страницы, и при этом ничего не напоминает ваш второй пункт, который либо неверен, либо не имеет значения, в зависимости от того, что вы подразумеваете под «доступом». «Нестатичное внутреннее» - это двойной разговор. Википедия не является нормативным справочником ни для C ++, ни для Java.
Маркиз Лорн

2

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


1

Позвольте мне объяснить, что я понял, на примере оператора 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 (тогда оно становится статической final final).

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

error: constant string expression required

-7

Ключевое слово «const» означает, что ваша переменная сохраняется в ПЗУ (с микропроцессором). на компьютере ваша переменная сохраняется в области ОЗУ для кода сборки (ОЗУ только для чтения). это означает, что ваша переменная отсутствует в ОЗУ с возможностью записи: статическая память, стековая память и кучная память.

Ключевое слово "final" означает, что ваша переменная сохраняется в ОЗУ с возможностью записи, но вы замечаете, что ваша переменная изменяется только один раз.

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

Я думаю, что «const» плохо работает, поэтому Java его не использует.

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.