2012-01-11 27 views
10

Hãy xem xét đoạn mã sau:Điều gì đang xảy ra trong khi xóa câu lệnh `delete this; '?

class foo 
{ 
public: 
    foo(){} 
    ~foo(){} 
    void done() { delete this;} 
private: 
    int x; 
}; 

gì đang xảy ra (và nó là hợp lệ?) Trong hai tùy chọn sau:

tùy chọn 1:

void main() 
{ 
    foo* a = new foo(); 
    a->done(); 
    delete a; 
} 

phương án 2:

void main() 
{ 
    foo a; 
    a.done(); 
} 

Câu lệnh thứ hai là delete a; khi chọn ion 1 sẽ gây ra một ngoại lệ hoặc tham nhũng đống?

Tùy chọn2 có gây ra ngoại lệ hoặc tham nhũng heap không?

+0

Bạn có quên dấu ngoặc mở cho lớp do nhầm lẫn hay chính xác là mã được sao chép không? – Neophile

+0

@Nerds: một lỗi đánh máy - cố định ... – NirMH

+0

Thú vị. Tôi đoán đầu tiên sẽ gây ra một segfault hoặc heap tham nhũng, và thứ hai sẽ làm bất cứ điều gì xóa một con trỏ vào ngăn xếp không. – cha0site

Trả lời

19

delete this; được phép, nó sẽ xóa đối tượng.

Cả hai đoạn mã của bạn đều có hành vi không xác định - trong trường hợp đầu tiên xóa đối tượng đã bị xóa và trong trường hợp thứ hai xóa đối tượng có thời lượng lưu trữ tự động.

Vì hành vi không được xác định, tiêu chuẩn không cho biết liệu chúng có gây ra ngoại lệ hoặc tham nhũng heap hay không. Đối với các triển khai khác nhau, nó có thể là, không, hoặc cả hai, và nó có thể hoặc có thể không giống nhau mỗi lần bạn chạy mã.

+0

Và hy vọng, nó sẽ là cái cũ. – Xeo

+1

Hành vi không xác định, câu trả lời đúng nhất và chính xác. +1 –

4

Cả hai đều gây ra lỗi.

Đầu tiên con trỏ bị xóa hai lần, với lỗi thứ hai delete gây ra lỗi.

+0

Bạn đánh tôi với nó! – Neophile

+0

Tôi đã chỉnh sửa câu hỏi. Tôi nghĩ rằng nó nên được cấp phát trên heap và sau đó xóa với 'done'. Điều gì đang xảy ra trong trường hợp này? Xóa trên ngăn xếp không có ý nghĩa và có thể không được hỏi bởi OP. – duedl0r

+0

Câu trả lời của bạn là chính xác, nhưng câu hỏi là những gì * sắp xếp * lỗi sẽ xảy ra. – cha0site

1

Cả hai sẽ gây ra lỗi, những gì bạn muốn là:

void main() 
{ 
    foo* a = new foo(); 
    a->done(); 
} 

nào trình biên dịch sẽ mở rộng tới một cái gì đó như dưới đây, mà tôi hy vọng làm cho xóa "này" một chút ít khó hiểu.

void __foo_done(foo* this) 
{ 
    delete this; 
} 

void main() 
{ 
    foo* a = new foo(); 
    __foo_done(a); 
} 

Xem thêm, Is it legal (and moral) for a member function to say delete this?

+0

Điều gì sẽ xảy ra nếu nó là thành viên đầu tiên của một đối tượng trong lớp bố cục tiêu chuẩn (hoặc POD), được phân bổ bằng đồng bằng mới và không có destructors nào khác? (Không phải điều đó sẽ không là _insane_, nhưng, cũng ...) – Random832

+0

@ Random832: Tôi không thể hiểu được câu hỏi của bạn, xin lỗi. – ronag

+0

Câu hỏi thường gặp mà bạn liên kết cung cấp cung cấp danh sách các điều kiện mà theo đó 'xóa này 'an toàn. Vì thành viên đầu tiên của một lớp bố cục tiêu chuẩn có cùng địa chỉ với chính đối tượng cha mẹ, có vẻ như về mặt lý thuyết sẽ là một trường hợp khác. – Random832

1

Calling delete this là một ý tưởng tồi. Bất cứ ai gọi new nên gọi số delete. Do đó các vấn đề như được nêu bật trong các phản ứng khác.

Ngoài ra, bạn có thể bị rò rỉ bộ nhớ/hành vi không xác định khi tạo một mảng đối tượng.

+0

Tại sao gọi 'xóa 'này là một ý tưởng tồi? Tôi muốn nói rằng trong nhiều ứng dụng (nhất?), Nó sẽ là dạng phổ biến nhất của 'xóa'. –

+0

@JamesKanze Tại sao? 'xóa này' không phải là cách giải phóng các đối tượng bình thường. –

+0

@VJovic Điều đó phụ thuộc vào ứng dụng.Trong rất nhiều ứng dụng, thường xuyên nhất (hoặc thậm chí là duy nhất) 'xóa' sẽ được 'xóa này'. Trong các trường hợp khác, 'xóa' sẽ có hệ thống trong trình quản lý giao dịch, hoặc một cái gì đó tương tự. Và ở những người khác ... Nó phụ thuộc vào lý do tại sao bạn đang sử dụng bộ nhớ động. Nếu nó cho các đối tượng mô hình, sau đó 'xóa này' hoặc một người quản lý giao dịch là thường xuyên nhất. Nếu nó dành cho các cấu trúc dữ liệu phức tạp có kích thước không xác định, thì chủ sở hữu của cấu trúc sẽ thực hiện 'delete' và' delete this' sẽ hầu như không bao giờ xảy ra. –

0

Trong cả hai trường hợp, heap của bạn sẽ bị hỏng. Tất nhiên, bạn không thể sử dụng trong tùy chọn 2 "->" nếu giá trị bên trái (trong trường hợp này, "a") không phải là một con trỏ, biểu mẫu chính xác trong tùy chọn 2 sẽ là a.done(); Nhưng yeah, bạn sẽ nhận được một đống tham nhũng nếu bạn cố gắng xóa 1) những gì đã bị xóa, hoặc 2) biến địa phương.

Gọi "xóa mục này" về mặt kỹ thuật hợp lệ và giải phóng bộ nhớ.

EDIT Tôi đã tò mò và thực sự cố gắng này:

class foo 
{ 
public: 
    foo(){} 
    ~foo(){} 
    void done() { delete this; } 
    void test() { x = 2; } 
    int getx() { return x; } 
private: 
    int x; 
} ; 


int main(int argc, char* argv[]) 
{ 
    foo *a = new foo(); 
    a->done(); 
    a->test(); 
    int x = a->getx(); 
    printf("%i\n", x); 
    return x; 
} 

Một cuộc gọi đến printf sẽ thành công, và x sẽ giữ giá trị 2.

+0

Cảm ơn - thực sự việc sử dụng "->" trong tùy chọn2 là lỗi đánh máy được thực hiện bởi chỉnh sửa bên ngoài - tôi đã cập nhật lại câu hỏi. – NirMH

+0

Vì vậy, 'xóa này' sẽ giải phóng bộ nhớ đối tượng từ heap? – NirMH

+0

@NirMH: cách sử dụng "->" do bạn viết. – duedl0r

0

Tôi sẽ rất thận trọng về ngữ cảnh trong đó bạn giải phóng bộ nhớ. Trong khi gọi "xóa này"; sẽ cố gắng giải phóng bộ nhớ liên kết với cấu trúc lớp, hoạt động đó không có phương tiện để suy luận ngữ cảnh của việc phân bổ bộ nhớ ban đầu, tức là, nếu nó được cấp phát từ heap hoặc chồng. Lời khuyên của tôi là làm lại mã của bạn để thao tác deallocation được gọi từ một ngữ cảnh bên ngoài. Lưu ý rằng điều này khác với các cấu trúc deallocating bên trong lớp, trong trường hợp đó, sử dụng destructor.

3

Câu hỏi này đã được trả lời nhưng tôi sẽ bổ sung thêm một điểm mới nếu lớp của bạn thực hiện cuộc gọi này thì bạn cũng nên hủy riêng tư.

Điều này đảm bảo rằng chỉ có lớp có thể tự xóa.

Nếu bạn đặt trình phá hủy của mình ở trên, cả hai mẫu mã của bạn sẽ không thể biên dịch được.

+0

tất nhiên nó không ngăn chặn tất cả các lỗi, ví dụ: bằng cách sử dụng đối tượng sau khi gọi xong() trên nó (thậm chí gọi xong() một lần nữa sẽ gọi một xóa kép). – CashCow

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