2010-10-14 41 views
5

Tôi đã nhận được một số mã sử dụng nhiều con trỏ trỏ đến cùng một địa chỉ. Ví dụ đơn giản tương đương:Cách xóa an toàn nhiều con trỏ

int *p = new int(1); 
int *q = p; 
int *r = q; 

delete r; r = NULL; // ok 
// delete q; q = NULL; // NOT ok 
// delete p; p = NULL; // NOT ok 

Làm thế nào để xóa an toàn mà không bị xóa nhiều? Điều này đặc biệt khó khăn nếu tôi có nhiều đối tượng có con trỏ trỏ đến cùng một địa chỉ.

+1

Điều này có nên hoạt động không? xóa null được quy định trong tiêu chuẩn, vì vậy nó cho phép và nên làm việc. OK, đó không phải là kiểu mã hóa tốt nhất ... –

+0

@Mario: Xóa NULL được chỉ định là một NO-OP nhưng gọi nó là phải chịu một số chi phí. –

+6

Vấn đề là q và p sẽ không là NULL, do đó sẽ có hai lần xóa. –

Trả lời

18

Câu trả lời, mà không cần đến các con trỏ được quản lý, là bạn nên biết có hay không xóa con trỏ dựa vào vị trí được phân bổ.

Ví dụ của bạn là loại giả tạo, nhưng trong một ứng dụng thế giới thực, đối tượng chịu trách nhiệm cấp phát bộ nhớ sẽ chịu trách nhiệm phá hủy nó. Các phương thức và hàm nhận các con trỏ đã khởi tạo và lưu trữ chúng trong một thời gian không xóa các con trỏ đó; trách nhiệm đó nằm với bất kỳ đối tượng nào ban đầu được cấp phát bộ nhớ.

Chỉ cần nhớ rằng các cuộc gọi của bạn tới new phải được cân bằng bằng các cuộc gọi của bạn tới delete. Mỗi khi bạn cấp phát bộ nhớ, bạn biết bạn phải viết mã cân bằng (thường là một destructor) để deallocate bộ nhớ đó.

+0

IMO đây là câu trả lời hay nhất. –

+2

+1: ý kiến ​​của tôi là shared_ptr chỉ nên được sử dụng trong trường hợp góc mà không có chủ sở hữu rõ ràng của bộ nhớ và rằng trong nhiều trường hợp bạn không cần nó. – n1ckp

+0

Vui lòng đọc: http://crazyeddiecpp.blogspot.com/2010/12/pet-peeve.html - bạn không xóa con trỏ! –

28

Công cụ của bạn là shared_ptr trong thư viện boost. Hãy xem tài liệu: http://www.boost.org/doc/libs/1_44_0/libs/smart_ptr/shared_ptr.htm

Ví dụ:

void func() { 
    boost::shared_ptr<int> p(new int(10)); 
    boost::shared_ptr<int> q(p); 
    boost::shared_ptr<int> r(q); 

    // will be destructed correctly when they go out of scope. 
} 
+3

+1 - Kịch bản OP của 'rất nhiều con trỏ trỏ đến cùng một địa chỉ' là lý tưởng cho 'shared_ptr' –

+1

Các trình biên dịch được phát hành gần đây cung cấp shared_ptr như một thành viên của không gian tên tr1. Vì vậy, thúc đẩy là không cần thiết - bạn có thể sử dụng tr1 :: shared_ptr thay thế. – nobar

+1

Con trỏ "Nguyên" trong C++ được kế thừa từ C, nhưng vấn đề là chúng _không thể hiện quyền sở hữu nào cả (nó là một trong những vấn đề không thể được đề cập đủ thường xuyên, IMHO). Một trong những điều quan trọng nhất về mã là nó phải thể hiện ý định của tác giả (bao gồm quyền sở hữu), vì vậy tốt hơn nên sử dụng các con trỏ thông minh tăng cường, hoặc ít nhất ** std :: auto_ptr **. (Nhưng con trỏ trường học cũ là tốt cho các trường hợp đơn giản, như nội bộ cho các đối tượng 'tầm thường hành xử'). – riviera

10

Câu trả lời "hiện đại" là sử dụng một con trỏ thông minh và không làm bất cứ xóa bằng tay.

boost::shared_ptr<int> p(new int(1)); 
boost::shared_ptr<int> q = p; 
boost::shared_ptr<int> r = q; 

Kết thúc câu chuyện!

+0

+1 để kết thúc câu chuyện ';)' –

+0

-1 để đăng mã chưa được kiểm tra. Một API con trỏ thông minh tốt (chẳng hạn như std :: auto_ptr của boost :: shared_ptr) không cho phép gán con trỏ cho con trỏ thông minh hoặc tạo con trỏ thông minh tiềm ẩn từ con trỏ bằng cách sử dụng hàm tạo bản sao. theo cách này quyền sở hữu sẽ không rõ ràng. Thay đổi dòng đầu tiên của bạn để tăng :: shared_ptr p = boost :: shared_ptr (int mới (1)); –

+0

cảm ơn, mã cố định –

0

Có một số trường hợp rất hiếm khi bạn không thể sử dụng con trỏ thông minh (có thể đang xử lý mã cũ), nhưng không thể sử dụng lược đồ "quyền sở hữu" đơn giản.

Hãy tưởng tượng bạn có một số std::vector<whatever*> và một số con trỏ whatever* trỏ đến cùng một đối tượng. Tính năng dọn dẹp an toàn liên quan đến việc đảm bảo bạn không xóa cùng một thứ gì hai lần - vì vậy hãy xây dựng một std::set<whatever*> khi bạn thực hiện và chỉ xóa các con trỏ chưa có trong tập hợp. Khi tất cả các đối tượng được trỏ tới đã bị xóa, cả hai vùng chứa đều có thể được xóa một cách an toàn.

Giá trị trả về từ insert có thể được sử dụng để xác định xem mục được chèn có mới hay không. Tôi đã không thử nghiệm sau đây (hoặc sử dụng std :: thiết lập cho một thời gian) nhưng tôi nghĩ rằng sau đây là đúng ...

if (myset.insert (pointervalue).second) 
{ 
    // Value was successfully inserted as a new item 
    delete pointervalue; 
} 

Bạn không nên thiết kế dự án để điều này là cần thiết, tất nhiên, nhưng nó không quá khó để đối phó với tình hình nếu bạn không thể tránh nó.

3

Sự cố bạn đang gặp phải là ngữ nghĩa quyền sở hữu trong chương trình của bạn không rõ ràng. Từ quan điểm thiết kế, hãy thử xác định ai là chủ sở hữu của các đối tượng ở mỗi bước. Trong nhiều trường hợp sẽ ngụ ý rằng bất cứ ai tạo ra đối tượng sẽ phải xóa nó sau này, nhưng trong các trường hợp khác quyền sở hữu có thể được chuyển giao hoặc thậm chí chia sẻ.

Khi bạn biết ai sở hữu bộ nhớ, sau đó quay lại mã và triển khai.Nếu đối tượng là người duy nhất chịu trách nhiệm cho một đối tượng khác, đối tượng phải giữ nó thông qua một con trỏ thông minh độc quyền (std::auto_ptr/unique_ptr) hoặc thậm chí một con trỏ thô (cố gắng tránh điều này vì nó là một nguồn lỗi phổ biến) và quản lý bộ nhớ theo cách thủ công. Sau đó chuyển tham chiếu hoặc con trỏ đến các đối tượng khác. Khi quyền sở hữu được chuyển giao, hãy sử dụng các phương tiện con trỏ thông minh để đưa đối tượng đến chủ sở hữu mới. Nếu quyền sở hữu thực sự được chia sẻ (không có chủ sở hữu rõ ràng nào của đối tượng được phân bổ) thì bạn có thể sử dụng shared_ptr và để con trỏ thông minh xử lý việc quản lý bộ nhớ).

1

Tại sao bạn lại cố gắng xóa con trỏ tùy ý? Mỗi đối tượng được phân bổ động được phân bổ trong một địa điểm, bởi một chủ sở hữu. Và nó phải là một trong những chủ sở hữu trách nhiệm để đảm bảo các đối tượng bị xóa một lần nữa.

Trong một số trường hợp, bạn có thể muốn chuyển quyền sở hữu cho một đối tượng hoặc thành phần khác, trong trường hợp đó trách nhiệm xóa cũng thay đổi.

Và đôi khi, bạn chỉ muốn quên quyền sở hữu và sử dụng chia sẻ quyền sở hữu: mọi người sử dụng đối tượng chia sẻ chủ sở hữu và miễn là ít nhất một người dùng tồn tại, đối tượng sẽ không bị xóa.

Sau đó, bạn sử dụng shared_ptr.

Tóm lại, hãy sử dụng RAII. Đừng cố gắng xóa các đối tượng theo cách thủ công.

0

Không thể biết liệu bộ nhớ được tham chiếu bởi một con trỏ đã bị xóa hay chưa, như trong trường hợp của bạn.
Nếu bạn không thể sử dụng thư viện với con trỏ thông minh, tính tham chiếu và bạn không thể thực hiện lược đồ đếm tham chiếu của riêng mình (nếu bạn thực sự cần làm những gì bạn mô tả trong bài đăng của bạn), hãy thử gọi realloc trên con trỏ.
Tôi đã đọc trong các bài đăng khác tùy thuộc vào việc triển khai cuộc gọi đến realloc có thể không bị lỗi nhưng trả lại con trỏ rỗng. Trong trường hợp này bạn biết rằng bộ nhớ được tham chiếu bởi con trỏ đó đã được giải phóng.
Nếu điều này làm việc như một giải pháp bẩn, nó sẽ không được di động, nhưng nếu bạn không có lựa chọn khác, hãy thử nó. Điều tồi tệ hơn tất nhiên sẽ là làm hỏng ứng dụng của bạn :)

0

Tôi sẽ nói rằng một số lần, con trỏ thông minh thực sự có thể làm chậm ứng dụng của bạn, không phải bởi rất nhiều. Những gì tôi sẽ làm là tạo ra một phương pháp, như vậy:

void safeDelete(void **ptr) 
{ 
    if(*ptr != NULL) 
    { 
    delete ptr; 
    *ptr = NULL; 
    } 
} 

Tôi không chắc chắn nếu tôi đã làm nó một cách chính xác 100%, nhưng những gì bạn làm là bạn vượt qua con trỏ vào phương pháp này mà sẽ đưa con trỏ của một con trỏ, kiểm tra để đảm bảo con trỏ nó trỏ đến không được đặt thành NULL, sau đó xóa đối tượng và sau đó đặt địa chỉ là 0 hoặc NULL. Đúng với tôi nếu đây không phải là cách tốt để làm điều này, tôi cũng mới làm điều này và ai đó nói với tôi đây là một cách tuyệt vời để kiểm tra mà không bị phức tạp.

+0

Điều này không giải quyết được vấn đề; với ví dụ trong câu hỏi, 'safeDelete (& r)' sẽ hoạt động, nhưng nó sẽ không sửa đổi 'q' hoặc' p', vì vậy 'safeDelete (& q)' sẽ có khả năng gặp sự cố. – Synxis

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