@SebastianRedl đã đưa ra các câu trả lời đơn giản, trực tiếp, nhưng một số giải thích thêm có thể hữu ích.
TL; DR = có một quy tắc kiểu để giữ cho các nhà xây dựng đơn giản, có lý do cho nó, nhưng những lý do đó chủ yếu liên quan đến một kiểu mã hóa lịch sử (hoặc đơn giản là xấu). Việc xử lý các trường hợp ngoại lệ trong các hàm tạo được xác định rõ và các hàm hủy sẽ vẫn được gọi cho các biến và thành viên cục bộ được xây dựng đầy đủ, điều đó có nghĩa là không nên có bất kỳ vấn đề nào trong mã C ++ thành ngữ. Quy tắc kiểu vẫn tồn tại, nhưng thông thường đó không phải là vấn đề - không phải tất cả các khởi tạo phải nằm trong hàm tạo và đặc biệt không nhất thiết phải là hàm tạo đó.
Đó là một quy tắc kiểu chung mà các nhà xây dựng nên thực hiện ở mức tối thiểu tuyệt đối có thể để thiết lập trạng thái hợp lệ được xác định. Nếu việc khởi tạo của bạn phức tạp hơn, nó sẽ được xử lý bên ngoài hàm tạo. Nếu không có giá trị khởi tạo giá rẻ mà nhà xây dựng của bạn có thể thiết lập, bạn nên làm suy yếu những kẻ xâm phạm được lớp của bạn thi hành để thêm một giá trị. Ví dụ: nếu phân bổ dung lượng lưu trữ cho lớp của bạn để quản lý quá đắt, hãy thêm trạng thái chưa được phân bổ nhưng chưa có, vì tất nhiên có các trạng thái trường hợp đặc biệt như null không bao giờ gây ra bất kỳ vấn đề nào. À.
Mặc dù phổ biến, chắc chắn ở dạng cực đoan này, nó rất xa. Cụ thể, như lời mỉa mai của tôi chỉ ra, tôi ở trong trại nói rằng sự bất biến yếu gần như luôn luôn là một cái giá quá cao. Tuy nhiên, có những lý do đằng sau quy tắc phong cách, và có nhiều cách để có cả các hàm tạo tối thiểu và các bất biến mạnh.
Những lý do liên quan đến dọn dẹp hủy diệt tự động, đặc biệt là trong trường hợp ngoại lệ. Về cơ bản, phải có một điểm được xác định rõ khi trình biên dịch trở thành trách nhiệm gọi các hàm hủy. Trong khi bạn vẫn đang trong một cuộc gọi của nhà xây dựng, đối tượng không nhất thiết phải được xây dựng đầy đủ, vì vậy việc gọi hàm hủy cho đối tượng đó là không hợp lệ. Do đó, trách nhiệm phá hủy đối tượng chỉ chuyển đến trình biên dịch khi hàm tạo hoàn thành thành công. Điều này được gọi là RAII (Phân bổ tài nguyên là khởi tạo) không thực sự là tên tốt nhất.
Nếu một ngoại lệ xảy ra bên trong hàm tạo, bất kỳ thứ gì được xây dựng một phần cần phải được làm sạch rõ ràng, thường là trong a try .. catch
.
Tuy nhiên, các thành phần của đối tượng đã được xây dựng thành công là trách nhiệm của trình biên dịch. Điều này có nghĩa là trong thực tế, nó không thực sự là một vấn đề lớn. ví dụ
classname (args) : base1 (args), member2 (args), member3 (args)
{
}
Cơ thể của nhà xây dựng này là trống rỗng. Miễn là các nhà xây dựng cho base1
, member2
và member3
ngoại lệ an toàn, không có gì phải lo lắng. Ví dụ: nếu hàm tạo của member2
ném, hàm tạo đó chịu trách nhiệm tự dọn dẹp. Cơ sở base1
đã được xây dựng hoàn chỉnh, do đó, hàm hủy của nó sẽ được gọi tự động. member3
thậm chí không bao giờ được xây dựng một phần, vì vậy không cần dọn dẹp.
Ngay cả khi có một cơ thể, các biến cục bộ đã được xây dựng đầy đủ trước khi ngoại lệ được ném sẽ tự động bị hủy, giống như bất kỳ hàm nào khác. Các cơ quan xây dựng xử lý các con trỏ thô hoặc "sở hữu" một loại trạng thái ẩn (được lưu trữ ở nơi khác) - thường có nghĩa là một lệnh gọi hàm bắt đầu / thu nhận phải được khớp với một lệnh gọi kết thúc / giải phóng - có thể gây ra các vấn đề an toàn ngoại lệ, nhưng vấn đề thực sự ở đó không thể quản lý tài nguyên đúng cách thông qua một lớp. Ví dụ: nếu bạn thay thế các con trỏ thô bằng unique_ptr
trong hàm tạo, hàm hủy unique_ptr
sẽ được gọi tự động nếu cần.
Vẫn còn những lý do khác mà mọi người đưa ra cho việc ưu tiên các nhà xây dựng tối thiểu. Một là đơn giản vì quy tắc kiểu tồn tại, nhiều người cho rằng các cuộc gọi của nhà xây dựng là rẻ. Một cách để có điều đó, nhưng vẫn có những bất biến mạnh, là có một lớp nhà máy / nhà xây dựng riêng biệt có các bất biến suy yếu thay vào đó, và thiết lập giá trị ban đầu cần thiết bằng cách sử dụng (có thể nhiều) các lệnh gọi hàm thành viên bình thường. Khi bạn có trạng thái ban đầu bạn cần, hãy chuyển đối tượng đó làm đối số cho hàm tạo cho lớp với các bất biến mạnh. Điều đó có thể "đánh cắp can đảm" của đối tượng yếu bất biến - di chuyển ngữ nghĩa - đó là một noexcept
hoạt động rẻ tiền (và thường ).
Và tất nhiên bạn có thể gói nó trong một make_whatever ()
hàm, vì vậy những người gọi hàm đó không bao giờ cần phải xem thể hiện của lớp suy yếu-bất biến.