2013-07-17 52 views
29

Ví dụ:Là một destructor được gọi là khi một đối tượng đi ra khỏi phạm vi?

int main() { 
    Foo *leedle = new Foo(); 

    return 0; 
} 

class Foo { 
private: 
    somePointer* bar; 

public: 
    Foo(); 
    ~Foo(); 
}; 

Foo::~Foo() { 
    delete bar; 
} 

có destructor được gọi là ngầm bởi trình biên dịch hoặc sẽ có một rò rỉ bộ nhớ?

Tôi mới sử dụng bộ nhớ động, vì vậy nếu đây không phải là trường hợp thử nghiệm khả dụng, tôi xin lỗi.

+4

Không, bạn cần phải tự gọi chính mình là 'xóa leedle'. – juanchopanza

+0

Bạn có thể trả lời và giải thích tại sao không? – Tux

+3

Có câu hỏi trong tiêu đề, Không cho câu hỏi trong nội dung. Bạn phải tự xóa mọi thứ bạn mới. Nếu bạn sử dụng 'new' trong hàm khởi tạo (miễn là không có ngoại lệ nào được ném ra), bạn có thể gọi' delete' trong destructor và nó sẽ dọn sạch bộ nhớ cho bạn. – Rapptz

Trả lời

71

Có, các biến tự động sẽ bị hủy ở cuối khối mã kèm theo. Nhưng hãy tiếp tục đọc.

Tiêu đề câu hỏi của bạn hỏi liệu trình hủy sẽ được gọi khi biến không nằm trong phạm vi. Có lẽ những gì bạn muốn hỏi là:

Foo của hàm hủy có được gọi vào cuối main() không?

Với mã mà bạn cung cấp, câu trả lời cho câu hỏi đó là không kể từ khi đối tượng Foo có thời gian lưu trữ năng động, như chúng ta sẽ thấy ngay.

Lưu ý ở đây những gì biến tự động là:

Foo* leedle = new Foo(); 

Ở đây, leedle là biến tự động sẽ bị hủy diệt. leedle chỉ là một con trỏ. Điều mà leedle chỉ định là không phải có thời lượng lưu trữ tự động và sẽ không bị hủy. Vì vậy, nếu bạn làm điều này:

void DoIt() 
{ 
    Foo* leedle = new leedle; 
} 

Bạn bị rò rỉ bộ nhớ được phân bổ bởi new leedle.


Bạn phảidelete bất cứ điều gì mà đã được phân bổ với new:

void DoIt() 
{ 
    Foo* leedle = new leedle; 
    delete leedle; 
} 

này được thực hiện đơn giản hơn nhiều và mạnh mẽ hơn bằng cách sử dụng con trỏ thông minh. Trong C++ 03:

void DoIt() 
{ 
    std::auto_ptr <Foo> leedle (new Foo); 
} 

Hoặc trong C++ 11:

void DoIt() 
{ 
    std::unique_ptr <Foo> leedle = std::make_unique <Foo>(); 
} 

con trỏ thông minh được sử dụng như là các biến tự động, như trên, và khi họ đi ra khỏi phạm vi và bị phá hủy, họ tự động (trong destructor) delete đối tượng đang được trỏ đến. Vì vậy, trong cả hai trường hợp trên, không có rò rỉ bộ nhớ.


Hãy cố gắng xóa một chút ngôn ngữ tại đây. Trong C++, các biến có thời lượng lưu trữ. Trong C++ 03, có 3 khoảng thời gian lưu trữ:

1: tự động: Biến có thời lượng lưu trữ tự động sẽ bị hủy ở cuối khối mã kèm theo.

xem xét:

void Foo() 
{ 
    bool b = true; 
    { 
    int n = 42; 
    } // LINE 1 
    double d = 3.14; 
} // LINE 2 

Trong ví dụ này, tất cả các biến có thời gian lưu trữ tự động. Cả hai bd sẽ bị phá hủy tại dòng 2. n sẽ bị phá hủy tại dòng 1.

2: tĩnh: Một biến với thời gian lưu trữ tĩnh sẽ được phân bổ trước khi chương trình bắt đầu, và bị phá hủy khi chương trình kết thúc.

3: động: Một biến với thời gian lưu trữ năng động sẽ được phân bổ khi bạn phân bổ nó sử dụng các hàm cấp phát bộ nhớ động (ví dụ, new) và sẽ bị phá hủy khi bạn tiêu diệt nó sử dụng các hàm cấp phát bộ nhớ động (ví dụ, delete).

Trong ví dụ ban đầu của tôi trên:

void DoIt() 
{ 
    Foo* leedle = new leedle; 
} 

leedle là một biến với thời gian lưu trữ tự động và sẽ bị tiêu diệt tại nẹp kết thúc. Điều mà leedle trỏ tới có thời lượng lưu trữ động và không bị hủy trong mã ở trên. Bạn phải gọi delete để deallocate nó.

C++ 11 cũng cho biết thêm một khoảng thời gian lưu trữ thứ tư:

4: chủ đề: Biến với thời gian lưu trữ chủ đề được phân bổ khi thread bắt đầu và deallocated khi thread kết thúc.

+0

Cảm ơn bạn, điều này trả lời câu hỏi của tôi! :) – Tux

+2

Câu trả lời hay - tôi thấy chủ đề này được giải quyết rất hiệu quả bằng cách chỉ ra rằng con trỏ [tự động] _will_ bị hủy, trước khi tiếp tục thảo luận ngữ nghĩa của đối tượng gián tiếp [động]. Với sự điều chỉnh của Eric tại chỗ, câu trả lời này là hoàn hảo. –

+0

@LightnessRacesinOrbit: Hãy xem, tôi đã thực hiện một chỉnh sửa khác. –

2

thực sự sẽ có rò rỉ bộ nhớ. Các destructor cho đối tượng mà đi ra khỏi phạm vi (Foo *) được gọi là, nhưng một cho các đối tượng chỉ đến (Foo bạn phân bổ) không. Về mặt kỹ thuật, kể từ khi bạn đang ở trong chính, nó không phải là một rò rỉ bộ nhớ, kể từ khi bạn đến khi ứng dụng không được chấm dứt, bạn có thể truy cập mọi biến được phân bổ. Với sự tôn trọng này, tôi trích dẫn Alexandrescu (từ Modern C++, chương về những người độc thân)

Rò rỉ bộ nhớ xuất hiện khi bạn phân bổ dữ liệu tích lũy và mất tất cả tham chiếu . Đây không phải là trường hợp ở đây: Không có gì tích lũy, và chúng tôi nắm giữ kiến ​​thức về bộ nhớ được cấp cho đến khi kết thúc ứng dụng . Hơn nữa, tất cả hiện đại

Tất nhiên, điều này không có nghĩa là bạn không nên gọi delete, vì đó là hành động cực kỳ nguy hiểm (và nguy hiểm).

+1

Bạn có thể xây dựng thêm một chút. – Tux

+0

xin lỗi, tôi đã gửi nhầm khi viết nó –

-5

Trong trường hợp này, khi trả về chính là kết thúc của chương trình, Hệ điều hành sẽ xử lý giải phóng tất cả các tài nguyên. Nếu, ví dụ, đây là bất kỳ chức năng nào khác, bạn sẽ phải sử dụng xóa.

+1

Đúng, nhưng bạn không thể chỉ dựa vào hệ điều hành để thực hiện tất cả công việc cho bạn. ;) – Tux

+5

-1 để dạy thực hành xấu cho một người mới rõ ràng. Plus C++ Standard không đảm bảo rằng hệ điều hành sẽ thu hồi tài nguyên, hoặc hệ điều hành đó thậm chí còn tồn tại. –

+0

Và thậm chí sau đó hệ điều hành có thể chỉ đơn giản là đòi lại bộ nhớ với gọi destructor. –

5

Có, nếu một đối tượng nằm ngoài phạm vi, hàm hủy được gọi. NHƯNG Không, hàm hủy sẽ không được gọi trong trường hợp này, bởi vì bạn chỉ có một con trỏ , con trỏ đó không có hàm hủy cụ thể, do đó sẽ không có cuộc gọi gián tiếp tới hàm hủy của Foo.

Ví dụ này là miền ứng dụng của con trỏ thông minh như std::unique_ptrstd::shared_ptr. Đó là các lớp thực tế, không giống như các con trỏ thô một hàm hủy, (có điều kiện) gọi delete trên đối tượng được trỏ tới.

Btw, destructor Foo 's xóa bar, bur bar chưa bao giờ được khởi tạo cũng không được gán cho một địa chỉ trỏ đến một đối tượng thực tế, vì vậy cuộc gọi xóa sẽ cung cấp cho hành vi không xác định, có khả năng một vụ tai nạn.

1

Lưu ý đầu tiên rằng mã sẽ không biên dịch; new trả về một con trỏ tới một đối tượng được cấp phát trên heap. Bạn cần:

int main() { 
    Foo *leedle = new Foo(); 
    return 0; 
} 

Bây giờ, kể từ khi new phân bổ đối tượng với dung lượng năng động thay vì tự động, nó sẽ không ra khỏi phạm vi ở phần cuối của hàm. Vì vậy nó sẽ không bị xóa, và bạn đã bị rò rỉ bộ nhớ.

+0

Vui lòng dừng lại với các gubbins "heap" đã lỗi thời. –

+1

Có gì sai với nó, @LightnessRacesinOrbit? – Joni

+3

Đó là một sự rò rỉ trừu tượng. Ngôn ngữ cho phép bạn tạo các đối tượng với thời gian lưu trữ tự động, tĩnh và động. Nơi mà chúng kết thúc trong bộ nhớ vật lý hoặc ảo không quan trọng cũng không được chỉ định bởi ngôn ngữ. Lặp lại "trên ngăn xếp" và "trên heap" chỉ giúp truyền bá cơn ác mộng này ... –

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