2009-10-03 42 views
7

Tất cả tài liệu tôi có thể tìm thấy trên các thùng chứa STL (cả hàng đợi và danh sách) đều nói rằng đối với bất kỳ chức năng xóa nào, hàm hủy của đối tượng đã xóa được gọi. Điều này có nghĩa rằng tôi không thể sử dụng std :: queue bất cứ lúc nào tôi muốn một hàng đợi đơn giản chỉ là một danh sách các đối tượng cần một số thao tác được thực hiện trên chúng.Xóa khỏi STL std :: xếp hàng mà không phá hủy đối tượng đã xóa?

Tôi muốn có thể thêm các đối tượng vào hàng đợi khi chúng đang chờ xếp hàng để tôi làm điều gì đó với chúng. Sau đó, tôi muốn loại bỏ chúng khỏi nó khi tôi đã hoàn thành với họ, mà không phá hủy các đối tượng trong câu hỏi. Điều này dường như không thể thực hiện được từ tài liệu tôi đã đọc. Tôi có đang đọc sai tài liệu không? Có một loại hàng đợi nào đó trong STL không phải là "hàng đợi" cơ bản không gọi hàm hủy của đối tượng đã xóa trong lệnh gọi pop_front không?

Chỉnh sửa để làm rõ: Trong trường hợp của tôi, tôi đang sử dụng danh sách các con trỏ. Một cái gì đó như thế này:

dbObject *someObject; 
    queue<dbObject *> inputQueue; 
    inputQueue.push_back(someObject); 

    ... 

    dbObject *objectWithInput = inputQueue.front(); 
    //handle object's input... 
    inputQueue.pop_front(); // Remove from queue... destroyed now? 
+1

nếu bạn đang lưu trữ con trỏ bên trong hàng đợi, việc xóa chúng sẽ không gọi hàm hủy. –

+0

Một câu hỏi tương tự: http://stackoverflow.com/questions/1525535/delete-all-items-from-a-c-stdvector –

Trả lời

17

Nếu bạn đặt con trỏ đến các đối tượng trong hàng đợi (và bất kỳ vùng chứa STL nào khác), các con trỏ sẽ không bị xóa khi bạn xóa chúng.

Để xây dựng: khi bạn sử dụng std :: queue và xóa một đối tượng, hàm hủy của some_obj * được gọi. Tuy nhiên, destructor cho con trỏ đơn giản (hoặc bất kỳ loại POD - int, char, vv) là trống rỗng, no-op. Dòng tiền phạt ở đây là destructor cho some_obj * là rất khác với destructor cho some_obj.

+2

Chính xác. Và chỉ để tham khảo, nếu bạn thực sự muốn bao container để xóa các đối tượng được trỏ đến, thì bạn sẽ cần lưu trữ một số loại con trỏ thông minh hoặc sử dụng một cái gì đó khác như con trỏ chứa của boost. – TheUndeadFish

3

Làm thế nào về việc sử dụng danh sách con trỏ cho các đối tượng?

+0

Điều đó sẽ không gọi cho deconstructor? Đó là những gì tôi đang sử dụng, hầu hết các đối tượng của tôi là con trỏ tới một đối tượng được lưu trữ ở nơi khác trong mã. –

4
class someobj_t {}; 

std::queue<someobj_t> q; 
... 

someobj_t ppd = q.front(); // ppd is not a reference 
q.pop(); 

// ppd now contain removed object 

Nếu bạn không muốn someobj_t được sao chép, bạn có thể sử dụng std::queue< shared_ptr<someobj_t> >.

+0

Tôi đang sử dụng danh sách các con trỏ. –

+1

Sau đó đối tượng thực sẽ không bị hủy khi xóa. –

7

Container STL có ngữ nghĩa giá trị. Khi bạn đẩy một đối tượng vào một container STL, container STL giữ bản sao riêng của đối tượng đó và khi đối tượng (bản sao nội bộ) được lấy ra khỏi vùng chứa, nó sẽ bị hủy.

Nếu bạn sử dụng vùng chứa loại proxy, con trỏ thô, con trỏ thông minh (shared_ptr, weak_ptr) hoặc bộ điều hợp (như tăng :: reference_wrapper), thì vùng chứa STL sẽ hủy proxy nhưng không phải loại. Chọn một trong những người khác thường là một vấn đề làm thế nào bạn muốn đối phó với các nguồn lực.

Thành ngữ phổ biến nhất là sử dụng con trỏ thô, nhưng chúng không rõ ràng ai chịu trách nhiệm hủy bỏ (mã kéo từ vùng chứa nên xóa con trỏ hoặc tài nguyên được xử lý ở nơi khác?).

Chuyển động sử dụng hiện đại theo hướng shared_ptr, vì nó làm loãng vấn đề quyền sở hữu. Các đối tượng sẽ được đảm bảo để được sống khi bạn đưa nó ra khỏi container, và nếu không ai khác giữ một shared_ptr thì đối tượng sẽ tự động bị xóa khi shared_ptr cục bộ đi ra khỏi phạm vi. Sử dụng weak_ptr sẽ giữ quyền sở hữu trong mã ban đầu, nhưng sẽ cho phép bạn kiểm tra tính hợp lệ của con trỏ (nếu nó đã bị xóa) trước khi sử dụng. Điều này có thể cho phép bạn tránh thực hiện thao tác trên một đối tượng sẽ bị xóa ngay lập tức.

Vấn đề với phương pháp shared_ptr/weak_ptr là nó buộc bạn phải sử dụng shared_ptr để giữ tài nguyên gốc. Điều này có nghĩa là bạn sẽ không thể đặt con trỏ vào một subobject (thuộc tính thành viên) của một lớp khác mà không cần thiết kế lại lớp để giữ thuộc tính thông qua shared_ptr và sẽ có các hàm ý khác (các thuộc tính sẽ không còn tiếp giáp trong bộ nhớ nữa) , các hoạt động phân bổ động hơn sẽ được yêu cầu ...)

Kỹ thuật khó thấy là sử dụng bộ điều hợp như tăng :: reference_wrapper <>. Trình bao bọc tham chiếu là một đối tượng proxy có chứa tham chiếu đến đối tượng ban đầu và chính nó có thể sao chép được. Ưu điểm trên các con trỏ thô đơn giản là đọc mã rõ ràng là tài nguyên được quản lý bên ngoài hàng đợi: mã kéo dữ liệu từ hàng đợi không cần xóa đối tượng. Ưu điểm của phương pháp tiếp cận con trỏ thông minh là bạn không cần phải thiết kế lại các phần khác của hệ thống để sử dụng con trỏ thông minh. Điểm bất lợi là, như trong phương pháp tiếp cận con trỏ thô, bạn phải đảm bảo rằng tuổi thọ của đối tượng được giới thiệu nằm ngoài tham chiếu trong vùng chứa theo cách thủ công.

+0

Tôi thích cách bạn giải thích và diễn đạt câu trả lời này, rất hữu ích và rất đầy đủ. – joshperry

2

Hãy nghĩ rằng mục nằm trong vùng chứa là "trong phạm vi" của vùng chứa đó, khi một mục bị xóa khỏi vùng chứa, nó giống như rời khỏi phạm vi của hàm. Nếu biến là một con trỏ không có gì xảy ra với mục khi rời khỏi phạm vi. Nếu biến là một chồng cục bộ thì hàm hủy sẽ tự động được gọi khi rời khỏi phạm vi.

Lưu trữ con trỏ trong các thùng chứa có các mức thiếu tương tự như được phân bổ vào một con trỏ thô cục bộ, bộ nhớ không được tự động làm sạch. Trong một hàm nếu bạn không xóa con trỏ hoặc chuyển quyền sở hữu bằng cách trả về nó thì bạn sẽ bị rò rỉ bộ nhớ.

Khi lưu trữ các con trỏ thô trong một vùng chứa, quyền sở hữu có thể trở nên mơ hồ và rò rỉ có thể dễ dàng xảy ra. Hãy xem tr1 :: shared_ptr và lưu trữ chúng trong các thùng chứa thay thế.

std :: unique_ptr trong C++ 0x cũng sẽ là giải pháp tốt để lưu trữ con trỏ trong vùng chứa stdlib khi có sẵn.

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