2011-08-22 65 views
46

Trong C++, bạn có thể sử dụng một danh sách khởi tạo để khởi tạo các trường của lớp trước khi hàm khởi tạo bắt đầu chạy. Ví dụ:Tại sao Java không có danh sách khởi tạo như trong C++?

Foo::Foo(string s, double d, int n) : name(s), weight(d), age(n) { 
    // Empty; already handled! 
} 

Tôi tò mò tại sao Java không có tính năng tương tự. Theo Lõi Java: Tập 1:

C++ sử dụng cú pháp đặc biệt này để gọi hàm tạo trường. Trong Java, không cần thiết vì các đối tượng không có các đối tượng con, chỉ các con trỏ đến các đối tượng khác.

Dưới đây là câu hỏi của tôi: "vì đối tượng không có subobjects"

  1. gì họ có ý nghĩa bởi Tôi không hiểu subobject là gì (tôi đã thử tìm kiếm nó); họ có nghĩa là một instantiation của một subclass mà mở rộng một superclass? Vì lý do tại sao Java không có danh sách khởi tạo như C++, tôi cho rằng lý do là vì tất cả các trường đã được khởi tạo mặc định trong Java và cũng vì Java sử dụng từ khóa super để gọi siêu (hoặc cơ sở trong C++ lingo) -class constructor. Điều này có đúng không?

Trả lời

86

Trong C++, danh sách khởi tạo là cần thiết vì một vài tính năng ngôn ngữ mà là một trong hai không có mặt trong Java hoặc công việc khác nhau trong Java:

  1. const: trong C++, bạn có thể định nghĩa một lĩnh vực được đánh dấu const mà không thể được gán cho và phải được khởi tạo trong danh sách khởi tạo. Java có trường final nhưng bạn có thể gán cho các trường final trong phần thân của hàm tạo.Trong C++, gán cho một trường const trong hàm tạo là bất hợp pháp.

  2. Tài liệu tham khảo: Trong C++, tài liệu tham khảo (như trái ngược với con trỏ) phải được khởi tạo liên kết với một số đối tượng. Việc tạo tham chiếu không có bộ khởi tạo là bất hợp pháp. Trong C++, cách mà bạn chỉ định điều này là với danh sách initializer, vì nếu bạn đã tham chiếu đến tham chiếu trong phần thân của hàm tạo mà không khởi tạo nó lần đầu tiên, bạn sẽ sử dụng tham chiếu uninitialized. Trong Java, các tham chiếu đối tượng hoạt động giống như con trỏ C++ và có thể được gán cho sau khi được tạo. Chúng chỉ mặc định là null nếu không.

  3. trực tiếp subobjects. Trong C++, một đối tượng có thể chứa đối tượng trực tiếp dưới dạng các trường, trong khi trong các đối tượng Java chỉ có thể giữ các tham chiếu cho các đối tượng đó. Tức là, trong C++, nếu bạn khai báo một đối tượng có một thành viên string, không gian lưu trữ cho chuỗi đó được xây dựng trực tiếp vào không gian cho đối tượng đó, trong khi trong Java bạn chỉ nhận được khoảng trống để tham chiếu đến một số khác String đối tượng được lưu trữ ở nơi khác. Do đó, C++ cần cung cấp một cách để bạn có thể cung cấp cho các giá trị ban đầu của các giá trị con đó, vì nếu không chúng sẽ không được khởi tạo. Theo mặc định, nó sử dụng hàm tạo mặc định cho các kiểu đó, nhưng nếu bạn muốn sử dụng một hàm tạo khác hoặc không có hàm tạo mặc định nào thì danh sách initializer cung cấp cho bạn một cách để bỏ qua điều này. Trong Java, bạn không cần phải lo lắng về điều này vì các tham chiếu sẽ mặc định là null và sau đó bạn có thể gán chúng để tham chiếu đến các đối tượng mà bạn thực sự muốn chúng tham chiếu đến. Nếu bạn muốn sử dụng một hàm tạo không mặc định, thì bạn không cần bất kỳ cú pháp đặc biệt nào cho nó; chỉ cần đặt tham chiếu đến đối tượng mới được khởi tạo thông qua hàm tạo thích hợp.

Trong vài trường hợp Java có thể muốn danh sách khởi tạo (ví dụ, để gọi constructor lớp cha hoặc đưa ra các giá trị mặc định cho các lĩnh vực của nó), điều này được xử lý thông qua hai tính năng ngôn ngữ khác: super từ khóa để gọi constructor lớp cha và thực tế là các đối tượng Java có thể cung cấp cho các giá trị mặc định của trường tại thời điểm chúng được khai báo. Vì C++ có nhiều thừa kế, chỉ cần có một từ khóa super đơn lẻ sẽ không chỉ rõ ràng một lớp cơ sở duy nhất, và trước khi C++ 11 C++ không hỗ trợ khởi tạo mặc định trong một lớp và phải dựa vào các danh sách khởi tạo.

Hy vọng điều này sẽ hữu ích!

+0

Câu trả lời hay. Tôi đã rất ngạc nhiên khi trả lời câu hỏi của mình. Tôi sẽ upvote bạn sau khi tôi đăng ký. Ngoài ra, một liên kết về sự thay đổi trong C++ 0x sẽ được đánh giá cao. –

+0

@ Jesse- Rất vui được giúp đỡ! Đừng quên chấp nhận câu trả lời là tốt nếu bạn nghĩ nó trả lời câu hỏi. :-) Và tôi đã bao gồm một liên kết cho sự thay đổi C++ 0x trong câu hỏi. – templatetypedef

+1

19 phiếu trong một chút trong một giờ trong đêm ... tốt đẹp :-) –

1

Vì Java không cần chúng cho phép khởi tạo các trường có loại không có giá trị bằng 0.

Trong C++

class C { 
    D d; 
} 

mà không có một khởi tạo thành viên cho d, D::D() sẽ được gọi mà làm cho nó không thể khởi tạo lĩnh vực này nếu không có zero-kiểu cho D. Điều này có thể xảy ra khi D::D() được khai báo rõ ràng private.

Trong Java, có một zero-value được biết cho tất cả các loại tham chiếu, null, do đó, trường luôn có thể được khởi tạo.

Java cũng làm một loạt các công việc để đảm bảo * rằng tất cả final trường này được khởi tạo trước khi sử dụng đầu tiên và trước các nhà xây dựng kết thúc, vì vậy trong khi Java có một yêu cầu như yêu cầu khởi const lĩnh vực C++ 's, nó chỉ làm quá tải this.fieldName = <expression> trong constructor cơ thể có nghĩa là khởi tạo trường.

  • : trường hợp ngoại lệ modulo ném vào ctor, phương pháp ghi đè các cuộc gọi từ các lớp cơ sở vv
+0

Cảm ơn câu trả lời. Tuy nhiên, tôi là một chút mệt mỏi của thuật ngữ "giá trị bằng không", vì nó có vẻ như nó đang nói về số nguyên 0, trong khi trong C++ Ox (nullptr) và null Java tôi không coi là tương đương. –

+0

@Jesse, tôi không phát minh ra thuật ngữ "không có giá trị". Nó là một thuật ngữ phổ biến trong các tài liệu đặc tả ngôn ngữ lập trình và tương tự. Ví dụ. ["Effective Go"] (http://golang.org/doc/effective_go.html) nói về các tham số trả về, "Khi được đặt tên, chúng được khởi tạo thành giá trị bằng không cho các kiểu của chúng khi hàm bắt đầu" và [spec spec ] (http://golang.org/doc/go_spec.html#The_zero_value) định nghĩa 'nil' là giá trị bằng không cho kiểu con trỏ. –

+0

Cảm ơn bạn đã liên kết về "không có giá trị". Như bạn có thể biết, các ngôn ngữ khác nhau sử dụng thuật ngữ khác nhau liên quan đến các khái niệm tương tự, như [ở đây] (http://stackoverflow.com/questions/1350819/c-free-store-vs-heap). Vì vậy, tôi sẽ khuyên bạn nên phân biệt hai. –

8

C++

Có sự khác biệt giữa

ClassType t(initialization arguments); 

ClassType * pt; 

Sau này không cần phải được khởi tạo (thiết lập để NULL). Các cựu không. Hãy nghĩ về nó như một số nguyên. Bạn không thể có int mà không có giá trị, NHƯNG bạn có thể có một con trỏ int không có giá trị.

Vì vậy, khi bạn có:

class ClassType 
{ 
    OtherClass value; 
    OtherClass * reference; 
}; 

Sau đó tuyên bố:

ClassType object; 

tự động tạo ra một thể hiện của OtherClass trong value.Do đó, nếu OtherClass đã khởi tạo, nó phải được thực hiện trong hàm tạo ClassType. Tuy nhiên, reference chỉ là một con trỏ (địa chỉ trong bộ nhớ) và có thể vẫn chưa được khởi tạo. Nếu bạn muốn một thể hiện của OtherClass bạn phải sử dụng

object.reference = new OtherClass(initialization arguments); 

Java

Có chỉ

class ClassType 
{ 
    OtherClass reference; 
} 

này tương đương với một con trỏ trong C++ là. Trong trường hợp này khi bạn thực hiện:

ClassType object = new ClassType(); 

Bạn không tự động tạo trường hợp OtherClass. Vì vậy, bạn không phải khởi tạo bất kỳ thứ gì trong hàm tạo trừ khi bạn muốn. Khi bạn muốn một đối tượng của OtherClass bạn có thể sử dụng

object.reference = new OtherClass(); 
+0

Cảm ơn câu trả lời rất dễ hiểu. –

Các vấn đề liên quan