Ostateczna wersja Java a stała C ++

151

Java dla programistów C ++ Tutorial mówi, że (kulminacyjnym jest moje własne):

Słowo kluczowe final jest mniej więcej równoważne z const w C ++

Co w tym kontekście oznacza „z grubsza”? Czy nie są dokładnie takie same?

Jakie są różnice, jeśli w ogóle?

WinWin
źródło

Odpowiedzi:

195

W C ++ oznaczenie funkcji constskładowej oznacza, że ​​może być wywoływana w constinstancjach. Java nie ma odpowiednika tego. Na przykład:

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

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

Wartości można przypisać jednorazowo, później tylko w Javie, np .:

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

jest legalne w Javie, ale nie w C ++, podczas gdy:

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

Zarówno w Javie, jak i C ++ zmiennymi składowymi mogą być odpowiednio final/ const. Muszą one otrzymać wartość do czasu zakończenia tworzenia instancji klasy.

W Javie muszą być ustawione przed zakończeniem pracy konstruktora, można to osiągnąć na dwa sposoby:

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

W C ++ będziesz musiał użyć list inicjalizacyjnych, aby nadać constczłonkom wartość:

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

W Java final może być używany do oznaczania rzeczy jako niepodlegających zastąpieniu. C ++ (przed C ++ 11) tego nie robi. Na przykład:

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

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

Ale w C ++:

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

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

to jest w porządku, ponieważ semantyka oznaczania funkcji składowej constjest inna. (Możesz również przeciążać , mając tylko constna jednej z funkcji składowych. (Zwróć również uwagę, że C ++ 11 umożliwia oznaczanie funkcji składowych jako ostateczne, zobacz sekcję Aktualizacja C ++ 11)


Aktualizacja C ++ 11:

C ++ 11 w rzeczywistości pozwala na oznaczanie zarówno klas, jak i funkcji składowych jako final, z identyczną semantyką dla tej samej funkcji w Javie, na przykład w Javie:

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

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

Można teraz dokładnie zapisać w C ++ 11 jako:

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

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

Musiałem skompilować ten przykład z wersją wstępną G ++ 4.7. Zauważ, że nie zastępuje constto w tym przypadku, ale raczej go rozszerza, zapewniając zachowanie podobne do Java, którego nie widziano z najbliższym odpowiednikiem słowa kluczowego C ++. Więc jeśli chcesz, aby funkcja członkowska była jednocześnie finali constzrobiłbyś:

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

(Kolejność consti finaltutaj jest wymagana).

Wcześniej nie było bezpośredniego odpowiednika constfunkcji składowych, chociaż uczynienie funkcji innymi niż- virtualbyłoby potencjalną opcją, aczkolwiek bez powodowania błędu w czasie kompilacji.

Podobnie Java:

public final class Bar {
}

public class Error extends Bar {
}

staje się w C ++ 11:

class Bar final {
};

class Error : public Bar {
};

(Wcześniej privatekonstruktory były prawdopodobnie najbliższe tego, co można było uzyskać w C ++)

Co ciekawe, w celu zachowania wstecznej kompatybilności z kodem sprzed wersji C ++ 11 w zwykły sposób final nie jest słowem kluczowym. (Weź trywialny, legalny przykład C ++ 98, struct final;aby zobaczyć, dlaczego uczynienie go słowem kluczowym spowodowałoby uszkodzenie kodu)

Flexo
źródło
3
Powinieneś uczynić te metody wirtualnymi; w przeciwnym razie naprawdę nie robisz tego samego
BlueRaja - Danny Pflughoeft
1
W Twoim ostatnim przykładzie to, co masz, jest legalne, ale warto wspomnieć, że final int a; a = 10; a = 11;tak nie jest (jest to celem finaljako modyfikator zmiennej). Ponadto końcowe elementy składowe klasy można ustawić tylko w czasie deklaracji lub raz w konstruktorze .
corsiKa
2
Zwróć uwagę, że C ++ 0x dodaje finaldekorator funkcji składowej właśnie w tym celu. VC ++ 2005, 2008 i 2010 już to zaimplementowano, używając kontekstowego słowa kluczowego sealedzamiast final.
ildjarn
@ildjarn - to interesujące wiedzieć i jeszcze jedna stosunkowo niewielka zmiana w C ++ 0x, o której nie byłem świadomy! Prawdopodobnie dodam gdzieś w tekście mały komentarz wskazujący, że to się zmienia w C ++ 0x.
Flexo
1
Najwyraźniej ludzie nadal robią s / const / final / g w bazach kodu z finalructor w rezultacie!
Flexo
30

W Javie ostatnie słowo kluczowe może być użyte do czterech rzeczy:

  • na klasie lub metodzie, aby ją zapieczętować (bez podklas / nadpisywania dozwolone)
  • na zmiennej składowej, aby zadeklarować, czy można ją ustawić dokładnie raz (myślę, że o tym mówisz)
  • na zmiennej zadeklarowanej w metodzie, aby upewnić się, że można ją ustawić dokładnie raz
  • na parametrze metody, aby zadeklarować, że nie można go modyfikować w ramach metody

Jedna ważna rzecz: ostateczna zmienna składowa Java musi być ustawiona dokładnie raz! Na przykład w konstruktorze, deklaracji pola lub inicjalizatorze. (Ale nie można ustawić końcowej zmiennej składowej w metodzie).

Innym skutkiem ostatecznej zmiany składowej zmiennej jest model pamięci, który jest ważny, jeśli pracujesz w środowisku wątkowym.

Ralph
źródło
Co masz na myśli mówiąc „model pamięci”? Nie rozumiem.
Tony
1
@Tony: Specyfikacja języka Java, rozdział 17.4. Model pamięci - docs.oracle.com/javase/specs/jls/se8/html/index.html - pierwsze trafienie w Google
Ralph
27

constObiekt może tylko wywołać constmetody i jest powszechnie uważany za niezmienne.

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)

finalObiekt nie może być ustawiony do nowego obiektu, ale to nie jest niezmienna - nie ma nic zatrzymując kogoś z wywoływania żadnych setmetod.

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

Java nie ma własnego sposobu deklarowania niezmienności obiektów; musisz sam zaprojektować klasę jako niezmienną.

Gdy zmienna jest typem pierwotnym, final/ constdziała tak samo.

const int a = 10; //C++
final int a = 10; //Java
a = 11; //Invalid in both languages
BlueRaja - Danny Pflughoeft
źródło
3
To także świetna odpowiedź (jak wiele innych tutaj). Niestety, mogę przyjąć tylko jedną odpowiedź. :)
WinWin
1
Doskonała odpowiedź!
ADJ
13

Java final jest odpowiednikiem C ++ const na pierwotnych typach wartości.

W przypadku typów referencyjnych Java słowo kluczowe final jest równoważne wskaźnikowi do stałej ... tj

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

//C++
const int constInt = 5;
MyObject * const constPointer = new MyObject();
James Schek
źródło
„końcowe słowo kluczowe jest równoważne ze wskaźnikiem do stałej”, dobrze powiedziane
ADJ
8

Masz już tutaj kilka świetnych odpowiedzi, ale jedną kwestię, która wydawała się warta dodania: constw C ++ jest powszechnie używana, aby zapobiec zmianie stanu obiektów przez inne części programu. Jak już wspomniano, finalw Javie nie można tego zrobić (z wyjątkiem prymitywów) - po prostu zapobiega to zmianie odniesienia na inny obiekt. Ale jeśli używasz a Collection, możesz zapobiec zmianom w swoich obiektach, używając metody statycznej

 Collection.unmodifiableCollection( myCollection ) 

Zwraca to Collectionodniesienie, które daje dostęp do odczytu elementów, ale zgłasza wyjątek, jeśli podejmowane są próby modyfikacji, dzięki czemu jest trochę jak constw C ++

pocketdora
źródło
8

Java finaldziała tylko na prymitywnych typach i referencjach, nigdy na samych instancjach obiektów, gdzie słowo kluczowe const działa na czymkolwiek.

Porównanie const list<int> melist;z final List<Integer> melist;pierwszą uniemożliwia modyfikację listy, natomiast ta druga tylko uniemożliwia przypisanie nowej listy do melist.

josefx
źródło
3

Oprócz posiadania pewnych i subtelnych właściwości wielowątkowości , zadeklarowane zmienne finalnie muszą być inicjowane przy deklaracji!

ie Dotyczy to języka Java:

// declare the variable
final int foo;

{
    // do something...

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

Nie byłoby to poprawne, gdyby zostało napisane w C ++ const.

antak
źródło
2

Według Wikipedii :

  • W C ++ pole const jest nie tylko chronione przed ponownym przypisaniem, ale istnieje dodatkowe ograniczenie polegające na tym, że można na nim wywołać tylko metody const i można je przekazać tylko jako argument const innych metod.
  • Niestatyczne klasy wewnętrzne mogą swobodnie uzyskiwać dostęp do dowolnego pola otaczającej klasy, końcowego lub nie.
KevenK
źródło
1
Słowo „przypisane ponownie” nie pojawia się w aktualnej wersji tej strony, podobnie jak druga uwaga, która jest albo niepoprawna, albo nieistotna, w zależności od tego, co rozumiesz przez „dostęp”. „Niestatyczna wewnętrzna” to podwójna mowa. Wikipedia nie jest normatywnym odniesieniem ani dla C ++, ani dla Javy.
Markiz Lorne
2

Domyślam się, że mówi się „z grubsza”, ponieważ znaczenie constw C ++ komplikuje się, gdy mówisz o wskaźnikach, tj. Wskaźnikach stałych vs. wskaźnikach do obiektów stałych. Ponieważ w Javie finalnie ma „wyraźnych” wskaźników, nie ma tych problemów.

Dima
źródło
1

Pozwólcie, że wyjaśnię, co zrozumiałem, na przykładzie instrukcji switch / case.

Wartości w każdej instrukcji case muszą być stałymi wartościami czasu kompilacji o tym samym typie danych, co wartość przełącznika.

zadeklaruj coś takiego jak poniżej (w swojej metodzie jako instancje lokalne lub w swojej klasie jako zmienną statyczną (dodaj do niej statyczną) lub zmienną instancji.

final String color1 = "Red";

i

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

Ten kod nie będzie się kompilował, jeśli color1jest zmienną klasy / instancji, a nie zmienną lokalną. Skompiluje się, jeśli color1zostanie zdefiniowany jako statyczny wynik końcowy (wtedy stanie się statyczną zmienną końcową).

Jeśli się nie skompiluje, pojawi się następujący błąd

error: constant string expression required
Subbu Mahadevan
źródło
-7

słowo kluczowe „const” oznacza, że ​​zmienna jest zapisana w pamięci ROM (z mikroprocesorem). w komputerze, twoja zmienna jest zapisywana w obszarze RAM dla kodu asemblera (RAM tylko do odczytu). oznacza to, że zmienna nie znajduje się w zapisywalnej pamięci RAM, w tym: pamięć statyczna, pamięć stosu i pamięć sterty.

słowo kluczowe „final” oznacza, że ​​zmienna jest zapisywana w zapisywalnej pamięci RAM, ale kompilator zwraca uwagę, że zmienna zmienia się tylko raz.

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

Myślę, że „const” ma słabą wydajność, więc Java go nie używa.

HungNM2
źródło