Nó sẽ làm điều tương tự (không có gì, về bản chất). Nhưng nó không giống như khi bạn không viết nó. Bởi vì việc viết destructor sẽ yêu cầu một trình phá hủy lớp cơ sở làm việc. Nếu destructor lớp cơ sở là riêng tư hoặc nếu có bất kỳ lý do nào khác mà nó không thể được gọi, thì chương trình của bạn bị lỗi. Xem xét việc này
struct A { private: ~A(); };
struct B : A { };
Đó là OK, miễn là bạn không cần phải hủy một đối tượng kiểu B (và do đó, mặc nhiên loại A) - như thế nào nếu bạn không bao giờ gọi xóa trên một đối tượng tự động tạo ra, hoặc bạn không bao giờ tạo ra một đối tượng của nó ở nơi đầu tiên. Nếu bạn làm thế, trình biên dịch sẽ hiển thị một chẩn đoán thích hợp. Bây giờ nếu bạn cung cấp một cách rõ ràng
struct A { private: ~A(); };
struct B : A { ~B() { /* ... */ } };
Đó là một sẽ cố gắng ngầm gọi destructor của các cơ sở hạng nhất, và sẽ gây ra một chẩn đoán đã vào thời điểm định nghĩa của ~B
.
Có một sự khác biệt khác tập trung vào định nghĩa về hàm hủy và các cuộc gọi ngầm đến các trình phá hủy thành viên.Xem xét thành viên con trỏ thông minh này
struct C;
struct A {
auto_ptr<C> a;
A();
};
Giả sử đối tượng của loại C
được tạo ra trong định nghĩa của constructor của một trong file .cpp
, mà cũng chứa các định nghĩa của struct C
. Bây giờ, nếu bạn sử dụng struct A
, và yêu cầu tiêu hủy đối tượng A
, trình biên dịch sẽ cung cấp một định nghĩa ngầm định về hàm hủy, giống như trong trường hợp trên. Đó là destructor cũng sẽ ngầm gọi là destructor của đối tượng auto_ptr. Và điều đó sẽ xóa con trỏ nó giữ, trỏ đến đối tượng C
- mà không biết định nghĩa của C
! Điều đó xuất hiện trong tệp .cpp
trong đó hàm tạo của struct A được định nghĩa.
Đây thực sự là một vấn đề phổ biến trong việc triển khai thành ngữ pimpl. Giải pháp ở đây là thêm một destructor và cung cấp một định nghĩa rỗng của nó trong tệp .cpp
, trong đó struct C
được định nghĩa. Tại thời điểm nó gọi destructor của thành viên của nó, sau đó nó sẽ biết định nghĩa của struct C
, và có thể gọi chính xác destructor của nó.
struct C;
struct A {
auto_ptr<C> a;
A();
~A(); // defined as ~A() { } in .cpp file, too
};
Lưu ý rằng boost::shared_ptr
không có vấn đề đó: Thay vào đó, yêu cầu loại hoàn chỉnh khi hàm tạo của nó được gọi theo một số cách nhất định.
Một điểm khác mà nó tạo sự khác biệt trong C++ hiện tại là khi bạn muốn sử dụng memset
và bạn bè trên đối tượng như vậy có người dùng đã khai báo destructor. Các loại như vậy không phải là POD nữa (dữ liệu cũ thuần túy), và chúng không được phép sao chép bit. Lưu ý rằng hạn chế này không thực sự cần thiết - và phiên bản C++ tiếp theo đã cải thiện tình huống này, do đó nó cho phép bạn vẫn sao chép bit các loại đó, miễn là các thay đổi quan trọng khác không được thực hiện.
Vì bạn đã yêu cầu nhà thầu: Vâng, vì những điều tương tự cũng đúng. Lưu ý rằng các hàm tạo cũng chứa các cuộc gọi ngầm đến các trình phá hủy. Về những thứ như auto_ptr, các cuộc gọi này (ngay cả khi không thực sự thực hiện khi chạy - khả năng thuần túy đã có vấn đề ở đây) sẽ làm hại tương tự như đối với các destructors, và xảy ra khi một cái gì đó trong constructor ném - trình biên dịch sau đó được yêu cầu gọi hàm hủy của các thành viên. This answer làm cho một số sử dụng định nghĩa ngầm định của các hàm tạo mặc định.
Ngoài ra, điều này cũng đúng với mức hiển thị và POD mà tôi đã nói về trình phá hủy ở trên.
Có một sự khác biệt quan trọng liên quan đến việc khởi tạo. Nếu bạn đặt một người sử dụng khai báo constructor, kiểu của bạn không nhận được giá trị khởi tạo của các thành viên nữa, và nó là vào constructor của bạn để làm bất kỳ khởi tạo đó là cần thiết. Ví dụ:
struct A {
int a;
};
struct B {
int b;
B() { }
};
Trong trường hợp này, sau đây là luôn luôn đúng
assert(A().a == 0);
Trong khi đây là hành vi không xác định, bởi vì b
không bao giờ được khởi tạo (constructor của bạn bỏ qua đó). Giá trị có thể bằng 0, nhưng có thể là bất kỳ giá trị lạ nào khác. Cố gắng đọc từ một đối tượng không được khởi tạo như vậy gây ra hành vi không xác định.
assert(B().b == 0);
Điều này cũng đúng cho việc sử dụng cú pháp này trong new
, như new A()
(chú ý dấu ngoặc ở cuối - nếu họ bị bỏ qua giá trị khởi tạo không được thực hiện, và vì không có người sử dụng tuyên bố constructor có thể khởi tạo nó , a
sẽ không được khởi tạo).
Tôi đã sửa đổi câu hỏi này một chút để biến chỉnh sửa sau này thành một phần thực tế của câu hỏi. Nếu có bất kỳ lỗi cú pháp nào trong các phần tôi đã chỉnh sửa, hãy hét lên với tôi, không phải người hỏi ban đầu. @Andrew, nếu bạn cảm thấy như tôi đã thay đổi câu hỏi của bạn quá nhiều, hãy hoàn nguyên nó; nếu bạn thích sự thay đổi nhưng nghĩ rằng nó không đủ, bạn rõ ràng được chào đón để chỉnh sửa câu hỏi của riêng bạn. –