2012-05-08 32 views
5

Tôi đã thấy rất nhiều mã nơi các lập trình viên xác định hàm init() cho các lớp và gọi nó là điều đầu tiên sau khi tạo cá thể.Khởi tạo trong Constructor

Có bất kỳ tác hại hoặc giới hạn nào trong việc thực hiện tất cả các khởi tạo trong Constructor không?

+2

Tại sao thẻ này được gắn thẻ với cả Java và C++? Cố ý? –

+1

không, nhưng đôi khi init được gọi để khởi tạo sau hai hàm tạo khác nhau - để tránh việc dán mã. – Anycorn

+0

init ảo() có thể được sử dụng để khởi tạo đa hình – vid

Trả lời

0

Đây là mẫu thiết kế có liên quan đến ngoại lệ ném từ bên trong một constructor đối tượng.

Trong C++ nếu một ngoại lệ được ném từ bên trong một đối tượng costructor thì đối tượng đó được coi là không được xây dựng ở tất cả, bởi thời gian chạy ngôn ngữ. Kết quả là đối tượng hủy sẽ không được gọi khi đối tượng nằm ngoài phạm vi.

Điều này có nghĩa rằng nếu bạn có mã như thế này bên trong constructor của bạn:

int *p1 = new int; 
int *p2 = new int; 

và mã như thế này trong destructor của bạn:

delete p1; 
delete p2; 

và khởi tạo của p2 bên trong constructor không do không còn bộ nhớ, nên ngoại lệ bad_alloc được ném bởi nhà điều hành mới mới. Vào thời điểm đó đối tượng của bạn không được xây dựng hoàn toàn, ngay cả khi bộ nhớ cho p1 đã được cấp phát chính xác. Nếu điều này xảy ra, destructor sẽ không được gọi và bạn đang bị rò rỉ p1.

Vì vậy, càng có nhiều mã bạn đặt bên trong hàm tạo, càng có nhiều khả năng lỗi sẽ xảy ra dẫn đến rò rỉ bộ nhớ tiềm ẩn.

Đó là lý do chính cho lựa chọn thiết kế đó, không quá điên sau khi tất cả.

Thông tin thêm về này trên Herb Sutter blog: Constructors exceptions in C++

0

Đó là một lựa chọn thiết kế. Bạn muốn giữ hàm tạo của bạn càng đơn giản càng tốt, vì vậy thật dễ dàng để đọc những gì nó đang làm. Đó là lý do tại sao bạn thường sẽ thấy các nhà thầu gọi các phương thức hoặc chức năng khác, tùy thuộc vào ngôn ngữ. Nó cho phép lập trình viên đọc và theo dõi logic mà không bị mất mã.

Khi các nhà thầu đi, bạn có thể nhanh chóng chạy vào một tình huống mà bạn có một chuỗi các sự kiện lớn mà bạn muốn kích hoạt. Thiết kế tốt ra lệnh rằng bạn phân tích các trình tự này thành các phương thức đơn giản, một lần nữa, để làm cho nó dễ đọc hơn và dễ bảo trì hơn trong tương lai.

Vì vậy, không, không có hại hoặc hạn chế, đó là tùy chọn thiết kế. Nếu bạn cần tất cả các khởi tạo được thực hiện trong constructor sau đó làm điều đó ở đó. Nếu bạn chỉ cần nó được thực hiện sau đó, sau đó đặt nó trong một phương pháp bạn gọi sau này. Dù bằng cách nào, nó hoàn toàn tùy thuộc vào bạn và không có quy tắc cứng hoặc nhanh xung quanh nó.

2

thường cho bảo trì và giảm kích thước mã khi nhiều nhà xây dựng gọi mã khởi tạo cùng:

class stuff 
{ 
public: 
    stuff(int val1) { init(); setVal = val1; } 
    stuff()   { init(); setVal = 0; } 

    void init()  { startZero = 0; } 

protected: 
    int setVal; 
    int startZero; 
}; 
1

Trong Java, có những lý do tốt để giữ cho nhà xây dựng ngắn và di chuyển lôgic khởi vào một phương pháp init():

  • nhà thầu không được kế thừa, vì vậy mọi lớp con phải triển khai lại hoặc cung cấp chuỗi đó với super
  • bạn không nên gọi các phương thức Overridable trong một constructor vì bạn có thể tìm thấy đối tượng của bạn trong tình trạng không nhất quán, nơi nó được khởi tạo một phần
+0

Cả hai đối số của bạn dường như tranh luận chống lại bằng cách sử dụng phương thức 'init()'. Sử dụng 'super' làm cho nó rõ ràng rằng bạn đang khởi tạo lớp cơ sở,' init' có thể bị ghi đè. Và thực tế là 'init' có thể được định nghĩa trong một lớp dẫn xuất là một đối số rất, rất mạnh khi sử dụng nó trong một lớp cơ sở. –

2

Chỉ ngược lại: nó thường là tốt hơn để đưa tất cả các khởi tạo trong constructor. Trong C++, chính sách "tốt nhất" thường đặt các khởi tạo trong danh sách khởi tạo, để các thành viên được xây dựng trực tiếp với các giá trị chính xác, thay vì mặc định được tạo, sau đó được gán. Trong Java, bạn muốn tránh một hàm (trừ khi nó là private hoặc final), vì độ phân giải động có thể đặt bạn vào một đối tượng chưa được khởi tạo.

Lý do duy nhất bạn sử dụng chức năng init() là vì bạn có rất nhiều nhà thầu có tính phổ biến đáng kể. (Trong trường hợp của C++, bạn vẫn phải cân nhắc sự khác biệt giữa việc xây dựng mặc định , sau đó chuyển nhượng và xây dựng ngay lập tức với giá trị chính xác .)

0

Nếu bạn có nhiều đối tượng cùng lớp hoặc các lớp học khác nhau mà cần phải được khởi tạo với con trỏ với nhau, do đó có ít nhất một chu kỳ của con trỏ phụ thuộc, bạn không thể làm tất cả các khởi tạo trong các nhà xây dựng một mình. (Làm thế nào bạn sẽ xây dựng đối tượng đầu tiên với một con trỏ/tham chiếu đến một đối tượng khác khi đối tượng khác chưa được tạo ra?)

Một tình huống có thể dễ dàng xảy ra trong hệ thống mô phỏng sự kiện nơi các thành phần khác nhau tương tác, để mỗi thành phần cần con trỏ đến các thành phần khác.

Vì không thể thực hiện tất cả việc khởi tạo trong các hàm tạo, nên ít nhất một số khởi tạo phải xảy ra trong các hàm init. Điều đó dẫn đến câu hỏi: Phần nào nên được thực hiện trong các hàm init? Lựa chọn linh hoạt có vẻ là làm tất cả khởi tạo con trỏ trong các hàm init. Sau đó, bạn có thể xây dựng các đối tượng theo thứ tự bất kỳ kể từ khi xây dựng một đối tượng cụ thể, bạn không phải lo lắng về việc liệu bạn đã có các con trỏ cần thiết cho các đối tượng khác mà nó cần phải biết hay không.

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