2010-04-27 38 views
6

Vài giờ trở lại tôi đã không quan tâm đến vấn đề Memory Leak và hóa ra là tôi thực sự có một số thứ cơ bản về các destructor ảo sai! Hãy để tôi đặt giải thích thiết kế lớp học của tôi.Trình phá hủy ảo hoạt động như thế nào?

class Base 
{ 
    virtual push_elements() 
    {} 
}; 

class Derived:public Base 
{ 
vector<int> x; 
public: 
    void push_elements(){ 
     for(int i=0;i <5;i++) 
     x.push_back(i); 
    } 
}; 

void main() 
{ 
    Base* b = new Derived(); 
    b->push_elements(); 
    delete b; 
} 

Công cụ kiểm tra giới hạn báo cáo rò rỉ bộ nhớ trong vector lớp dẫn xuất. Và tôi đã tìm ra rằng destructor không phải là ảo và destructor lớp dẫn xuất không được gọi. Và nó đáng ngạc nhiên đã được cố định khi tôi thực hiện các destructor ảo. Không phải là vector deallocated tự động ngay cả khi destructor lớp dẫn xuất không được gọi là? Đó có phải là một quirk trong BoundsChecker công cụ hoặc là sự hiểu biết của tôi về destructor ảo sai?

+1

Vui lòng không định dạng mã của bạn bằng HTML. Chọn và nhấn nút '0101' sẽ thụt lề bằng 4 dấu cách. – Yacoby

+0

mã như đã đăng không có mối quan hệ nào giữa Base và Derived – JRL

+0

@JRL: Cảm ơn. Có nguồn gốc từ Base. Tôi đã thực hiện thay đổi .. – Prabhu

Trả lời

15

Xóa đối tượng lớp dẫn xuất thông qua con trỏ cấp cơ sở khi lớp cơ sở không có destructor ảo dẫn đến hành vi không xác định.

Những gì bạn đã quan sát (phần phần thừa của đối tượng không bao giờ bị hủy và do đó các thành viên của nó không bao giờ bị phân phối) có lẽ là phổ biến nhất trong nhiều hành vi có thể xảy ra. chắc chắn các destructor của bạn là ảo khi bạn sử dụng đa hình theo cách này.

1

Nếu destructor không phải là ảo thì cơ sở destructor sẽ được gọi. Hàm hủy cơ sở làm sạch đối tượng Base và kết thúc. Không có cách nào cho destructor đối tượng cơ sở biết về đối tượng dẫn xuất, nó phải là destructor dẫn xuất được gọi, và cách để làm điều đó, như với bất kỳ hàm nào, là làm cho destructor ảo.

+0

Đây không phải là như vậy, như tôi và những người khác đã chỉ ra, những gì bạn nhận được là hành vi không xác định, mà không nhất thiết có nghĩa là cơ sở destructor được gọi. –

+1

Gọi destructor cơ sở chỉ là biểu hiện có khả năng nhất của hành vi không xác định được gọi ở đây mặc dù, đó là hữu ích để biết cho mục đích gỡ lỗi. –

+0

@Neil: Vâng, nhưng tôi hy vọng trình phá hủy cơ sở sẽ được gọi là hầu hết thời gian, giống như tôi mong đợi 'int i = 3; i = i ++ + i ++; 'để lại' i' chứa một số nguyên nhỏ không có thay đổi trạng thái nào khác. Điều này có ý nghĩa quan trọng trong trường hợp này bởi vì một hành vi chính đáng dẫn đến kết quả quan sát được, và tăng cường xác suất rằng việc thiếu một destructor ảo là nguyên nhân gây ra vấn đề. Hãy suy nghĩ về nó như một vấn đề chẩn đoán hơn là một vấn đề phù hợp. –

8

Nếu lớp cơ sở không có trình phá hủy ảo, thì kết quả của mã của bạn là hành vi không xác định, không nhất thiết là trình phá hủy sai được gọi. Đây có lẽ là điều mà BoundsChecker đang chẩn đoán.

+4

Những đứa trẻ hư hỏng làm hỏng các câu trả lời đúng về mặt kỹ thuật không phải là một cảnh tượng sắc sảo. –

2

Mặc dù điều này là không xác định về mặt kỹ thuật, bạn vẫn cần phải biết phương pháp phổ biến nhất của thất bại để chẩn đoán nó. Đó là phương pháp phổ biến của thất bại là để gọi destructor sai. Tôi không biết thực hiện bất kỳ sẽ thất bại trong bất kỳ cách nào khác, mặc dù thừa nhận rằng tôi chỉ sử dụng hai triển khai.

Lý do điều này xảy ra là cùng một lý do chức năng 'sai' sẽ được gọi khi bạn cố gắng ghi đè lên một hàm thành viên không phải ảo và gọi nó qua một con trỏ cơ sở.

1

Từ câu hỏi thường gặp C++ Lite: "Khi nào thì trình hủy của tôi là ảo?" Đọc nó here. (C++ FAQ Lite là một nguồn tuyệt vời cho tất cả các câu hỏi của bạn liên quan đến C++, nhân tiện).

+1

Sách C++ FAQ thậm chí còn tốt hơn. –

1

Trong C++, destructor tầm thường là một khái niệm được xác định đệ quy - đó là một destructor mà trình biên dịch đã viết cho bạn khi mọi thành viên của lớp (và mọi lớp cơ sở) có một destruct tầm thường. (Có một khái niệm tương tự được gọi là hàm tạo tầm thường.)

Khi một đối tượng có một destructor không độc hại được bao gồm trong một đối tượng (như vector trong ví dụ của bạn), thì hàm hủy của đối tượng bên ngoài (như Derived) là không còn tầm thường nữa. Mặc dù bạn không viết destructor, trình biên dịch C++ tự động viết một destructor gọi các destructors của bất kỳ thành viên nào có destructors.

Vì vậy, ngay cả khi bạn không viết bất cứ điều gì, hãy cẩn thận khi viết một trình phá hủy không ảo.

0

Hàm hủy là hàm thành viên của lớp có cùng tên của tên lớp và được đặt trước bởi dấu ngã (~).Destructor được sử dụng để phá hủy đối tượng của lớp khi đối tượng đi ra khỏi phạm vi hoặc bạn có thể nói rằng tất cả làm sạch lớp hủy diệt sẽ được thực hiện trong destructor. Tất cả bộ nhớ được cấp phát trong khi xây dựng đối tượng trong lớp sẽ bị hủy (hoặc phát hành bộ nhớ) khi đối tượng nằm ngoài phạm vi.

Tìm thêm chi tiết với ví dụ trên BoundsCheck

1

nếu bạn đang đến từ C#, sau đó bạn đã đúng trong tự hỏi tại sao vector không tự động de-phân bổ. Nhưng trong c + +, quản lý bộ nhớ tự động không khả dụng trừ khi bạn sử dụng Microsoft Extectedions của Microsoft cho C++ (C++/CLI).

vì không có destructor trong lớp cơ sở ảo, đối tượng lớp dẫn xuất sẽ không bao giờ được giải phóng và do bạn rò rỉ bộ nhớ được cấp cho thành viên dữ liệu vectơ của lớp dẫn xuất.

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