2009-08-11 25 views
7

Tôi đang viết một danh sách liên kết và tôi muốn hủy cấu trúc của cấu trúc (cấu trúc nút) để xóa chính nó và không có bất kỳ tác dụng phụ nào. Tôi muốn destructor danh sách của tôi để lặp đi lặp lại gọi destructor Node trên chính nó (lưu trữ các nút tiếp theo tạm thời), như thế này:Có OK để sử dụng "xóa mục này" để xóa đối tượng hiện tại không?

//my list class has first and last pointers 
//and my nodes each have a pointer to the previous and next 
//node 
DoublyLinkedList::~DoublyLinkedList 
{ 
    Node *temp = first(); 

    while (temp->next() != NULL) 
    { 
     delete temp; 
     temp = temp->next(); 
    } 
} 

Vì vậy, đây sẽ là destructor Node tôi:

Node::~Node 
{ 
    delete this; 
} 

Đây có phải là chấp nhận được, đặc biệt trong bối cảnh này?

+0

Tôi sẽ là người cuối cùng sẽ tiếp tục bị kết thúc nhưng tôi tin rằng câu hỏi này đã được hỏi một vài lần trong quá khứ rồi: http://www.google.com/search?q=delete + site + này: stackoverflow.com –

Trả lời

17

Nếu trình phá hủy nút đang được gọi, thì nó đang trong quá trình xóa. Vì vậy, một xóa không có ý nghĩa bên trong Node destructor của bạn.

Ngoài ra đây là sai:

while (temp->next() != NULL) 
{ 
    delete temp; 
    temp = temp->next(); 
} 

Thay vào đó bạn sẽ nhận được temp-> tiếp theo() vào một biến temp. Nếu không, bạn đang truy cập bộ nhớ đã xóa.

Vì vậy, nhiều như thế này:

DoublyLinkedList::~DoublyLinkedList 
{ 
    Node *temp = first(); 
    while (temp != NULL) 
    { 
     Node *temp2 = temp->next(); 
     delete temp; 
     temp = temp2; 
    } 
} 
+0

Lưu ý: Vì bạn đang xóa tất cả các nút khỏi trình phá hoại DoublyLinkedList của mình, bạn không nên xóa các đối tượng Nút ở bất kỳ nơi nào khác. –

+0

Vì vậy, tôi có cần một trình phá hủy nút không? Tôi chỉ không muốn dtor biên dịch cung cấp rối tung với cuộc sống của tôi. – jkeys

+1

Bạn có thể đặt một và để trống hoặc không thể đặt một cái nào cả. –

0

Mã trên sẽ gọi Node :: ~ Node() hai lần. (Trong "xóa tạm thời" và trong Node :: ~ Node())

Node :: ~ Node() không nên gọi là "xóa này" (hoặc chương trình của bạn sẽ sụp đổ)

ps. trong khi vòng lặp trong mã của bạn sẽ không hoạt động. Nó sẽ dereference con trỏ không hợp lệ. Trước tiên, bạn nên sao chép giá trị của temp-> next, và sau đó phá hủy con trỏ temp.

+1

Trên thực tế, 'Node :: ~ Node()' sẽ không được gọi hai lần, nhưng kết thúc trong một ngăn xếp tràn (Ha!) Do đệ quy vô tận. – sbi

+0

@sbi Bạn nói đúng. Nó sẽ gây ra tràn ngăn xếp. – rein

4

Không, bạn không được delete this từ trình phá hủy. Các destructor được gọi là do một tuyên bố xóa (hoặc đi ra khỏi phạm vi) và điều này rất có thể sẽ dẫn đến một số loại vụ tai nạn.

Bạn cũng có một vài vấn đề trong bộ đếm thời gian DoublyLinkedList. Một, bạn xóa temp sau đó truy cập temp sau khi nó bị xóa. Thứ hai, mã sẽ không thực sự xóa phần tử cuối cùng trong danh sách được liên kết.

1

xóa nội dung này; sẽ gọi hàm hủy của đối tượng hiện tại. Trong trường hợp đó, nếu bạn đang gọi xóa này; trong destructor, sau đó destructor sẽ được gọi là vô hạn cho đến khi vụ tai nạn.

4

Hiện nay, mã của bạn sẽ gây ra một sự vi phạm truy cập, kể từ thứ hai của dòng sau truy cập rõ ràng bộ nhớ được giải phóng:

delete temp; 
temp = temp->next(); 

Nếu bạn muốn một cách đệ quy xóa cấu trúc, bạn muốn một cái gì đó như thế này:

DoublyLinkedList::~DoublyLinkedList 
{ 
    Node *temp = first(); 
    delete temp; 
} 

Node::~Node 
{ 
    if(this->next() != NULL) delete this->next(); 
} 
+2

Một vấn đề với cách tiếp cận này là nếu bạn muốn xóa một nút tùy ý, bạn sẽ xóa sạch mọi nút theo sau nó. – harto

+0

Điểm tốt, cảm ơn. Tôi muốn tránh việc thực hiện đệ quy vì điều đó có thể không hiệu quả đối với các danh sách lớn, tôi tin. – jkeys

+1

Mã OP sẽ không nhất thiết gây ra AV - bộ nhớ giải phóng có thể được truy cập bởi quá trình này hoặc nó có thể được trả lại cho hệ điều hành và trở nên không thể tiếp cận - nó phụ thuộc vào trình quản lý heap. – sharptooth

0

Nói chung, trình phá hủy chỉ nên lo lắng về việc xóa (hoặc giải phóng nếu bạn đang sử dụng C hoặc malloc) bất kỳ bộ nhớ nào được cấp riêng cho đối tượng của bạn. Xóa một con trỏ đến đối tượng của bạn sẽ luôn được quản lý bởi hệ điều hành và bạn không phải lo lắng về một điều về phần đó.

Một điều đáng lưu ý là, khi xây dựng, bạn tạo đối tượng đầu tiên (khi luồng điều khiển đi vào thân của hàm tạo), THEN các đối tượng bên trong; để hủy diệt, bạn phải làm điều đó ngược lại, bởi vì nếu bạn xóa đối tượng bên ngoài trước tiên, bạn sẽ không có cách nào để truy cập các con trỏ bên trong để xóa chúng. Thay vào đó, bạn xóa các đối tượng bên trong bằng cách sử dụng destructor, sau đó hệ điều hành quản lý việc giải phóng thực tế bộ nhớ khi luồng điều khiển rơi ra khỏi destructor.

Ngẫu nhiên, cùng một loại điều xảy ra với phân lớp-- nếu bạn có lớp A và lớp B: công khai A, thì khi bạn tạo B() mới, hàm tạo A thực hiện trước, sau đó là hàm tạo B; về sự hủy diệt, destructor của B thực hiện đầu tiên, tiếp theo là A's. Tôi khá chắc chắn rằng bạn không phải lo lắng về điều này, mặc dù - C + + sẽ chăm sóc nó cho bạn. Vì vậy, không cố gắng tìm ra cách để gọi xóa trên siêu lớp.

1

Trước bất cứ điều gì khác: Tôi thực sự, thực sự hy vọng đây là bài tập về nhà được giao cho bạn để hiểu danh sách được liên kết đôi. Nếu không, không có lý do gì để sử dụng thay vì std::list. Với cách này:

Không, delete this trong dtor luôn sai, vì dtor được gọi khi this đang ở trạng thái bị xóa.

Ngoài ra, trong khi

delete temp; 
temp = temp->next(); 

tình cờ có thể làm việc, nó chắc chắn sai, vì, nơi bạn cố gắng truy cập temp->next(), temp đã được xóa, vì vậy bạn nên gọi một hàm thành viên trên đó. Làm như vậy gọi cái gọi là "hành vi không xác định". (Nói ngắn gọn: Nó có thể làm những gì bạn muốn, nhưng nó cũng có thể thất bại hoặc không thường xuyên hoặc chỉ khi thứ Sáu, 13, va chạm với mặt trăng mới. Nó cũng kêu gọi rất quỷ mũi khó chịu trên bạn.)

Lưu ý rằng bạn có thể giải quyết cả hai vấn đề bằng cách xóa các nút tiếp theo trong dtor của nút của bạn:

Node::~Node() 
{ 
    delete next(); 
} 

bằng cách đó, danh sách dtor của bạn trở nên rất dễ dàng, quá:

DoublyLinkedList::~DoublyLinkedList() 
{ 
    delete first(); 
} 

Đối với tôi, điều này dường như những gì dtors đã được phát minh, vì vậy, ngoại trừ một thực tế là không ai nên viết các loại danh sách liên kết của riêng mình nữa ngày nay, với tôi điều này dường như là giải pháp C++ cho vấn đề của bạn.

+0

Không phải bài tập về nhà, nhưng đó là một bài tập học tập. Tôi chưa bao giờ thực sự sử dụng một danh sách. Tôi là một fan hâm mộ của STL, vì vậy đừng lo lắng về điều đó. – jkeys

+0

@Hooked: Một tiếng thở dài nhẹ nhõm về phía này. ': ^>' – sbi

0

Cả hai sẽ không bao giờ được thực hiện.

này

DoublyLinkedList::~DoublyLinkedList 
{ 
    Node *temp = first(); 
    while (temp->next() != NULL) 
    { 
     delete temp; 
     temp = temp->next(); 
    } 
} 

sẽ gây ra hành vi undefined - bạn không được phép truy cập vào bộ nhớ mà bạn đã quay trở lại đống. Thay vào đó nó sẽ là:

DoublyLinkedList::~DoublyLinkedList 
{ 
    Node *temp = first(); 
    while(temp != NULL) 
    { 
     Node* next = temp->next(); 
     delete temp; 
     temp = next; 
    } 
} 

Gọi số delete this sẽ dẫn đến cái gọi là miễn phí đôi cũng sẽ gây ra hành vi không xác định. Các destructor chỉ nên gọi xóa cho các biến thành viên con trỏ, không bao giờ cho this. Gọi delete this là hợp lý từ các phương pháp khác để deallocate đối tượng hiện tại, nhưng không phải từ destructor.

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