Trận chung kết của Java so với C ++


151

Các Java cho lập trình viên C ++ hướng dẫn nói rằng (nổi bật là của riêng tôi):

Từ khóa cuối cùng gần tương đương với const trong C ++

"Đại khái" có nghĩa là gì trong bối cảnh này? Không phải là họ chính xác giống nhau không?

Sự khác biệt là gì, nếu có?

Câu trả lời:


195

Trong C ++, đánh dấu một hàm thành viên constcó nghĩa là nó có thể được gọi trong các consttrường hợp. Java không có tương đương với điều này. Ví dụ:

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

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

Các giá trị có thể được chỉ định, một lần, sau này chỉ trong Java, vd:

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

là hợp pháp trong Java, nhưng không phải C ++ trong khi:

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

Trong cả hai biến thành viên Java và C ++ có thể là final/ consttương ứng. Chúng cần được cung cấp một giá trị tại thời điểm một thể hiện của lớp được hoàn thành.

Trong Java, chúng phải được đặt trước khi hàm tạo kết thúc, điều này có thể đạt được theo một trong hai cách:

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

Trong C ++, bạn sẽ cần sử dụng các danh sách khởi tạo để cung cấp cho constcác thành viên một giá trị:

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

Trong Java cuối cùng có thể được sử dụng để đánh dấu những thứ là không thể ghi đè. C ++ (tiền C ++ 11) không làm điều này. Ví dụ:

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

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

Nhưng trong C ++:

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

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

Điều này là tốt, bởi vì ngữ nghĩa của việc đánh dấu một chức năng thành viên constlà khác nhau. (Bạn cũng có thể quá tải khi chỉ có constmột trong các chức năng thành viên. (Cũng lưu ý rằng C ++ 11 cho phép các chức năng thành viên được đánh dấu cuối cùng, xem phần cập nhật C ++ 11)


Cập nhật C ++ 11:

C ++ 11 trên thực tế cho phép bạn đánh dấu cả hai lớp và các hàm thành viên là final, với ngữ nghĩa giống hệt nhau cho cùng một tính năng trong Java, ví dụ như trong Java:

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

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

Bây giờ có thể được viết chính xác trong C ++ 11 như:

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

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

Tôi đã phải biên dịch ví dụ này với bản phát hành trước G ++ 4.7. Lưu ý rằng điều này không thay thế consttrong trường hợp này, mà là tăng cường nó, cung cấp hành vi giống như Java không thấy với từ khóa C ++ tương đương gần nhất. Vì vậy, nếu bạn muốn một hàm thành viên là cả hai finalconstbạn sẽ làm:

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

(Thứ tự constfinalở đây là bắt buộc).

Trước đây không có sự tương đương trực tiếp của các consthàm thành viên mặc dù việc tạo các hàm không virtualphải là một tùy chọn tiềm năng mặc dù không gây ra lỗi khi biên dịch.

Tương tự như vậy Java:

public final class Bar {
}

public class Error extends Bar {
}

trở thành C ++ 11:

class Bar final {
};

class Error : public Bar {
};

(Các nhà privatexây dựng trước đây có lẽ là người gần nhất bạn có thể nhận được điều này trong C ++)

Thật thú vị, để duy trì khả năng tương thích ngược với mã trước C ++ 11 final không phải là một từ khóa theo cách thông thường. (Lấy ví dụ C ++ 98 tầm thường, hợp pháp struct final;để xem tại sao làm cho nó trở thành một từ khóa sẽ phá vỡ mã)


3
Bạn nên làm cho các phương thức đó trở nên ảo; mặt khác, bạn thực sự không làm điều tương tự
BlueRaja - Danny Pflughoeft

1
Trong ví dụ cuối cùng của bạn, những gì bạn có là hợp pháp, nhưng điều đáng nói final int a; a = 10; a = 11;là không phải (đó là mục đích của finalmột công cụ sửa đổi biến.) Ngoài ra, các thành viên cuối cùng một lớp chỉ có thể được đặt tại thời điểm khai báo hoặc một lần trong hàm tạo .
corsiKa

2
Lưu ý rằng C ++ 0x thêm trình finaltrang trí chức năng thành viên cho mục đích chính xác này. VC ++ 2005, 2008 và 2010 đã triển khai điều này, sử dụng từ khóa theo ngữ cảnh sealedthay vì final.
ildjarn

@ildjarn - thật thú vị khi biết và một thay đổi C ++ 0x tương đối nhỏ khác mà tôi không biết! Có lẽ tôi sẽ thêm một nhận xét nhỏ ở đâu đó trong văn bản chỉ ra rằng điều này đang thay đổi với C ++ 0x.
Flexo

1
Rõ ràng mọi người vẫn làm vẫn s / const / thức / g trong codebases với finalructor như kết quả!
Flexo

30

Trong Java, từ khóa cuối cùng có thể được sử dụng cho bốn điều:

  • trên một lớp hoặc phương thức để niêm phong nó (không cho phép các lớp con / ghi đè)
  • trên một biến thành viên để khai báo rằng nó có thể được đặt chính xác một lần (tôi nghĩ đây là những gì bạn đang nói về)
  • trên một biến được khai báo trong một phương thức, để đảm bảo rằng nó có thể được đặt chính xác một lần
  • trên một tham số phương thức, để khai báo rằng nó không thể được sửa đổi trong phương thức

Một điều quan trọng là: Một biến thành viên cuối cùng của Java phải được đặt chính xác một lần! Ví dụ: trong một hàm tạo, khai báo trường hoặc intializer. (Nhưng bạn không thể đặt biến thành viên cuối cùng trong một phương thức).

Một kết quả khác của việc tạo một biến thành viên cuối cùng liên quan đến mô hình bộ nhớ, điều này rất quan trọng nếu bạn làm việc trong môi trường luồng.


Bạn có ý nghĩa gì khi nói 'mô hình bộ nhớ'? Tôi không hiểu
Tony

1
@Tony: Đặc tả ngôn ngữ Java, Chương 17.4. Mô hình bộ nhớ - docs.oracle.com/javase/specs/jls/se8/html/index.html - googles hit đầu tiên
Ralph

27

Một constđối tượng chỉ có thể gọi constcác phương thức và thường được coi là bất biến.

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)

Một finalđối tượng không thể được đặt thành một đối tượng mới, nhưng nó không phải là bất biến - không có gì ngăn cản ai đó gọi bất kỳ setphương thức nào .

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

Java không có cách khai báo các đối tượng cố định; bạn cần phải tự thiết kế lớp học như bất biến.

Khi biến là kiểu nguyên thủy, final/ consthoạt động như nhau.

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

3
Đây cũng là một câu trả lời tuyệt vời (giống như nhiều người khác ở đây). Thật không may, tôi chỉ có thể chấp nhận một câu trả lời. :)
WinWin

1
Câu trả lời hoàn hảo!
ADJ

13

Java cuối cùng tương đương với C ++ const trên các loại giá trị nguyên thủy.

Với các kiểu tham chiếu Java, từ khóa cuối cùng tương đương với một con trỏ const ... tức là

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

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

"từ khóa cuối cùng tương đương với một con trỏ const" cũng được nói
ADJ

8

Bạn đã có một số câu trả lời tuyệt vời ở đây rồi, nhưng một điểm có vẻ đáng để thêm vào: consttrong C ++ thường được sử dụng để ngăn các phần khác của chương trình thay đổi trạng thái của các đối tượng. Như đã được chỉ ra, finaltrong java không thể làm điều này (ngoại trừ nguyên thủy) - nó chỉ ngăn tham chiếu không bị thay đổi thành một đối tượng khác. Nhưng nếu bạn đang sử dụng a Collection, bạn có thể ngăn các thay đổi đối tượng của mình bằng phương thức tĩnh

 Collection.unmodifiableCollection( myCollection ) 

Điều này trả về một Collectiontham chiếu cho phép truy cập đọc vào các phần tử, nhưng sẽ đưa ra một ngoại lệ nếu cố gắng sửa đổi, làm cho nó giống như consttrong C ++


8

Java finalchỉ hoạt động trên các kiểu và tham chiếu nguyên thủy, không bao giờ trên các cá thể đối tượng nơi từ khóa const hoạt động trên bất cứ thứ gì.

So sánh const list<int> melist;với final List<Integer> melist;cái đầu tiên làm cho không thể sửa đổi danh sách, trong khi cái sau chỉ ngăn bạn gán một danh sách mới melist.


3

Ngoài việc có các thuộc tính đa luồng nhất định và tinh tế , các biến được khai báo finalkhông cần phải được khởi tạo khi khai báo!

tức là Điều này hợp lệ trong Java:

// declare the variable
final int foo;

{
    // do something...

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

Điều này sẽ không hợp lệ nếu được viết bằng C ++ const.


2

Theo wikipedia :

  • Trong C ++, trường const không chỉ được bảo vệ khỏi bị gán lại, mà còn có giới hạn bổ sung mà chỉ các phương thức const có thể được gọi trên nó và nó chỉ có thể được chuyển qua làm đối số const của các phương thức khác.
  • Các lớp bên trong không tĩnh có thể tự do truy cập bất kỳ trường nào của lớp kèm theo, cuối cùng hoặc không.

1
Từ 'được gán lại' không xuất hiện trong phiên bản hiện tại của trang đó và cũng không có gì giống với điểm thứ hai của bạn, không chính xác hoặc không liên quan, tùy thuộc vào ý nghĩa của bạn khi 'truy cập'. "Nội tâm không tĩnh" là nói chuyện đôi. Wikipedia không phải là tài liệu tham khảo quy phạm cho C ++ hoặc Java.
Hầu tước Lorne

2

Tôi đoán nó nói "đại khái" bởi vì ý nghĩa của constC ++ trở nên phức tạp khi bạn nói về con trỏ, tức là con trỏ không đổi so với con trỏ với các đối tượng không đổi. Vì không có con trỏ "rõ ràng" trong Java, finalnên không có những vấn đề này.


1

Hãy để tôi giải thích những gì tôi hiểu với một ví dụ về tuyên bố chuyển đổi / trường hợp.

Các giá trị trong mỗi câu lệnh tình huống phải là các giá trị hằng số thời gian biên dịch của cùng loại dữ liệu với giá trị chuyển đổi.

khai báo một cái gì đó như bên dưới (trong phương thức của bạn là các thể hiện cục bộ hoặc trong lớp của bạn dưới dạng biến tĩnh (sau đó thêm tĩnh vào nó) hoặc một biến thể hiện.

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

Mã này sẽ không biên dịch, nếu color1là biến lớp / thể hiện và không phải là biến cục bộ. Điều này sẽ biên dịch nếucolor1 được định nghĩa là tĩnh cuối cùng (sau đó nó trở thành biến cuối cùng tĩnh).

Khi nó không biên dịch, bạn sẽ gặp lỗi sau

error: constant string expression required

-7

từ khóa "const" có nghĩa là biến của bạn được lưu trong ROM (với Bộ vi xử lý). trong máy tính, biến của bạn được lưu trong vùng RAM cho mã hội (chỉ đọc RAM). điều đó có nghĩa là biến của bạn không nằm trong RAM có thể ghi bao gồm: bộ nhớ tĩnh, bộ nhớ ngăn xếp và bộ nhớ heap.

từ khóa "cuối cùng" có nghĩa là biến của bạn được lưu trong RAM có thể ghi, nhưng bạn lưu ý với trình biên dịch rằng biến của bạn chỉ thay đổi một lần duy nhất.

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

Tôi nghĩ, "const" là hiệu năng kém, vì vậy Java không sử dụng nó.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.