2011-01-29 38 views
30

Ví dụ: khi tôi không khai báo constructor, trình biên dịch sẽ cung cấp cho tôi default constructor sẽ không có đối số và không có định nghĩa (body), và sẽ mất không có hành động.C++ default destructor

Nếu bây giờ tôi không khai báo một destructor, trình biên dịch sẽ cung cấp cho tôi với một default destructor không có defintion (cơ thể), và do đó, tôi nghĩ rằng không có hành động .

Vì vậy, nếu tôi đã kết thúc với một đối tượng chẳng hạn, không phải bộ nhớ phân bổ lại (miễn phí) default destructor được sử dụng bởi đối tượng phải không? Nếu không, tại sao chúng ta nhận được nó?

Và, có thể cùng một câu hỏi áp dụng cho số default constructor. Nếu nó không có gì, tại sao nó được tạo ra cho chúng ta theo mặc định?

Cảm ơn.

+19

Các nhà xây dựng không phân bổ bộ nhớ của đối tượng, bộ nhớ đã được phân bổ cho nó bằng cách nào đó trước khi hàm tạo được gọi. – dreamlax

+0

Rất nhiều câu sai trong khi đặt ra câu hỏi này. Và câu trả lời chỉ vào họ không được chấp nhận. – Antonio

Trả lời

0

Trình phá hủy mặc định sẽ không có cách nào biết được bộ nhớ mà lớp của bạn "sở hữu" để có thể giải phóng bộ nhớ đó.

Đối với phần constructor mặc định, tôi sẽ trích dẫn Wikipedia article trên này ...

Trong C++, nhà thầu mặc định là rất lớn, vì họ là tự động gọi trong một số hoàn cảnh :

  • Khi giá trị đối tượng được khai báo không có danh sách đối số, ví dụ: MyClass x ;; hoặc được phân bổ động không có danh sách đối số , ví dụ: MyClass mới; các hàm tạo mặc định được sử dụng để khởi tạo đối tượng
  • Khi một mảng đối tượng được khai báo, ví dụ: MyClass x [10] ;; hoặc được phân bổ động, ví dụ: mới MyClass [10]; constructor mặc định được sử dụng để khởi tạo tất cả các yếu tố
  • Khi một constructor lớp có nguồn gốc không rõ ràng gọi lớp constructor cơ sở trong danh sách initializer của nó, các nhà xây dựng mặc định cho lớp cơ sở được gọi là
  • Khi một lớp constructor không gọi một cách rõ ràng các nhà xây dựng của một trong những lĩnh vực đối tượng có giá trị của nó trong danh sách initializer của nó, các nhà xây dựng mặc định cho các lớp học của trường là gọi
  • trong thư viện chuẩn, nhất định container "điền vào" giá trị sử dụng hàm tạo mặc định khi giá trị là không được cung cấp rõ ràng, ví dụ: vectơ (10); khởi tạo vector với 10 phần tử, là được điền với giá trị mặc định được xây dựng thuộc loại của chúng tôi.

Trong trường hợp trên, nó là một lỗi nếu lớp không có constructor mặc định.Trình biên dịch sẽ xác định ngầm định một hàm tạo mặc định

nếu không có hàm tạo nào là được định nghĩa rõ ràng cho một lớp. Điều này mặc định được khai báo ngầm định mặc định hàm tạo tương đương với một hàm tạo mặc định là được xác định bằng một thân trống. (Lưu ý:. Nếu một số nhà xây dựng là xác định, nhưng tất cả chúng đều không mặc định, trình biên dịch sẽ không ngầm định nghĩa một constructor mặc định Điều này có nghĩa rằng một constructor mặc định có thể không tồn tại cho một lớp học.)

4

Bởi vì nếu bạn không có bất kỳ nhà xây dựng (publiclly-truy cập) hoặc destructors, sau đó một đối tượng của lớp không thể được instantiated. Hãy xem xét:

class A 
{ 
private: 
    A() {} 
    ~A() {} 
}; 

A a; // Oh dear! Compilation error 

Nếu bạn không khai báo rõ ràng bất kỳ nhà thầu hoặc trình phá hủy nào, trình biên dịch phải cung cấp cho phép tạo đối tượng.

+1

Tại sao lỗi biên dịch? Không có một hàm tạo mặc định nào chưa? Cảm ơn – Simplicity

+2

@user: Không, bởi vì chúng tôi đã tuyên bố hàm tạo mặc định là riêng tư. Vì vậy, nó không thể được gọi (ngoài việc thông qua một hàm thành viên lớp tĩnh). –

1

Câu trả lời ngắn gọn là trong C++ mọi đối tượng cần một hàm tạo và một hàm hủy, ngay cả khi chúng không làm gì cả. Vì vậy, trình biên dịch tạo chúng cho bạn trong nền đáp ứng yêu cầu này.

Câu trả lời dài hơn là các nhà xây dựng chịu trách nhiệm khởi tạo thành viên của lớp học. Hàm khởi tạo mặc định khởi tạo mặc định tất cả các thành viên. (Điều đó không có nghĩa là bất cứ điều gì cho các loại POD, nhưng các lớp khác nhận được các hàm tạo mặc định của chúng được gọi là.)

4

Mặc định destructor sẽ không làm bất cứ điều gì (giống như một hàm tạo mặc định).

Bạn sẽ cần phải xác định một chính mình, nếu destructor của bạn thực sự cần phải làm điều gì đó (ví dụ: giải phóng một số tài nguyên).

Lưu ý rằng thông thường bạn nên theo dõi rule of three: nếu chương trình của bạn cần làm điều gì đó trong destructor của nó (ví dụ: giải phóng tài nguyên), bạn cũng nên cung cấp một hàm tạo bản sao và toán tử gán; C++ cũng cung cấp các phiên bản mặc định của chúng (trong đó, một lần nữa, sẽ không làm bất cứ điều gì).

Mặc định constructor/destructor/assign operator/copy constructor rất hữu ích khi bạn đang xử lý các lớp đơn giản, nơi bạn không cần phải làm bất cứ điều gì. Một trường hợp đặc biệt là POD: chúng (trước C++ 0x) thậm chí không thể có các hàm tạo hoặc hàm hủy rõ ràng.

3

constructor mặc định và destructors chỉ là một mặt hàng trong trường hợp bạn không cần bất cứ điều gì đặc biệt thực hiện với lớp của bạn, bạn không cần phải viết một phiên bản trống bằng tay. Điều này là phổ biến đối với các ngôn ngữ OO khác, ví dụ trong Java bạn không cần phải cung cấp một hàm tạo nếu không khởi tạo các thành viên đủ. Đồng thời nó là một yêu cầu cho tương thích ngược với C. Nếu bạn có một struct trong C, nó sẽ không có hàm tạo hoặc hàm hủy (C không có các khái niệm đó), để có thể xử lý mã đó trong C++ mà phải là mã hợp lệ.

Một tùy chọn khác sẽ được khai báo bằng ngôn ngữ mà lớp không có hàm tạo hoặc hàm hủy, nhưng toàn bộ đặc tả ngôn ngữ sẽ phải xử lý thực tế là một số loại có thể có hàm tạo và hàm hủy và điều đó sẽ làm cho ngôn ngữ phức tạp hơn và khó xác định hơn. Trong khi có các phiên bản được xác định ngầm không thay đổi hành vi và giảm bớt đặc điểm kỹ thuật.

Ở cấp độ này, nó giống như cụm từ ghi đè đang được áp dụng cho phương pháp trong lớp cơ sở. Trong lớp cơ sở, không ghi đè bất kỳ thứ gì, không có gì để ghi đè! Tuy nhiên, ngôn ngữ này nói rõ rằng một phương pháp không thuần khiết ảo được khai báo trong một cơ sở là một người ghi đè. Điều này cho phép spec chỉ đơn giản nói rằng số ghi đè cuối cùng sẽ được gọi khi phương thức được gọi thông qua con trỏ hoặc tham chiếu mà không phải thêm extre * hoặc triển khai phương thức cơ sở nếu không có người ghi đè tồn tại cho phương thức cụ thể đó .

49

Thật sai khi nói rằng trình tạo mặc định do trình biên dịch tạo ra sẽ không thực hiện hành động nào. Nó tương đương với một hàm tạo do người dùng định nghĩa với một thân trống và một danh sách khởi tạo rỗng, nhưng điều đó không có nghĩa là nó không thực hiện hành động nào. Dưới đây là những gì nó làm:

  1. Nó gọi hàm tạo mặc định của lớp cơ sở.
  2. Nó khởi tạo con trỏ vtable, nếu lớp là đa hình.
  3. Nó gọi các hàm tạo mặc định của tất cả các thành viên có chúng. Nếu có một thành viên với một số nhà xây dựng, nhưng không có một nhà cung cấp mặc định, thì đó là lỗi biên dịch.

Và chỉ khi một lớp không đa hình, không có lớp cơ sở và không có thành viên yêu cầu xây dựng, thì trình tạo mặc định do trình biên dịch tạo không làm gì cả. Nhưng ngay cả sau đó một nhà xây dựng mặc định đôi khi là cần thiết vì những lý do được giải thích trong các câu trả lời khác.

Điều tương tự cũng xảy ra với destructor - nó gọi là destructor lớp cơ sở và destructors của tất cả các thành viên có chúng, vì vậy nó không đúng trong trường hợp chung rằng một trình biên dịch tạo ra destructor không có gì.

Nhưng phân bổ bộ nhớ thực sự không liên quan gì đến điều này. Bộ nhớ được cấp phát trước khi constructor được gọi, và nó được giải phóng chỉ sau khi destructor cuối cùng đã kết thúc.

+1

Để công bằng, tất cả điều này xảy ra "đằng sau hậu trường". Nó cũng xảy ra với một hàm tạo do người dùng khai báo, ngay cả khi bạn không viết bất kỳ mã nào trong hàm tạo của bạn. –

+2

@Oli, tất nhiên, đó là chính xác những gì tôi có nghĩa là "tương đương với một nhà xây dựng người dùng xác định với một cơ thể trống rỗng và một danh sách khởi tạo rỗng". Quan điểm của tôi đơn giản là nói sai rằng đó là một nhà xây dựng không hành động, trong trường hợp chung. –

+0

My [answer] (http://stackoverflow.com/a/36207338/1628638) đưa ra một ví dụ trong đó destructor mặc định là chìa khóa để tránh rò rỉ bộ nhớ. –

0

Khi sử dụng con trỏ thông minh, trình phá hủy mặc định (xem câu trả lời của Sergey) có thể rất quan trọng để tránh rò rỉ bộ nhớ. Ở đây một ví dụ:

#include <iostream> 
#include <memory> 

using namespace std; 

class Foo { 
public: 
    Foo(int n = 0): n(n) { cout << "Foo(" << n << ")" << endl; } 
    ~Foo() { cout << "~Foo(" << n << ")" << endl; } 
private: 
    int n; 
}; 

// notes: 
// * default destructor of Bar calls destructors of unique_ptr<Foo> foo 
// and of unique_ptr<Foo[]> foo3, which, in turn, delete the Foo objects 
// * foo2's Foo object leaks 
class Bar { 
public: 
    Bar(): foo(new Foo(1)), foo2(new Foo(2)), foo3(new Foo[2]) { } 
private: 
    unique_ptr<Foo> foo; 
    Foo* foo2; 
    unique_ptr<Foo[]> foo3; 
}; 

int main() { 
    Bar bar; 
    cout << "in main()" << endl; 
} 

đây sản lượng, cho thấy một sự rò rỉ xảy ra chỉ dành cho foo2:

Foo(1) 
Foo(2) 
Foo(0) 
Foo(0) 
in main() 
~Foo(0) 
~Foo(0) 
~Foo(1)