2013-08-27 30 views
5

Nó không phải là một câu hỏi "làm thế nào để làm điều đó" nó là "làm thế nào để làm điều đó đúng cách"C++ 11 tham chiếu không sở hữu/con trỏ tới unique_ptr?

Tôi đang phát triển một trình soạn thảo trong Qt nơi các widget khác nhau hiển thị trẻ em và các biến (thành viên) của nó. Mỗi tiện ích con này phải giữ một tham chiếu/con trỏ tới con đã chỉnh sửa để hiển thị và thay đổi các biến thành viên của chúng.

Nỗ lực đầu tiên là cách ANSI C cũ mà tôi đã học (và vẫn bị mắc kẹt) với con trỏ thô đơn giản đến các đối tượng được sử dụng. Nó hoạt động tốt nhưng kể từ khi tiêu chuẩn C++ 11 hỗ trợ con trỏ thông minh và sử dụng chúng được khuyến khích tôi đang cố gắng sử dụng chúng.

Vấn đề là, tôi không hoàn toàn chắc chắn rằng "cách tốt nhất" để sử dụng chúng trong trường hợp này là những gì ... Sau khi đọc Smart Pointers: Or who owns you baby?Which kind of pointer do I use when? và một vài người khác tôi đi đến những kết luận khác nhau:

Các đầu tiên là sử dụng *unique_ptr vì đối tượng được chỉnh sửa rõ ràng là chủ sở hữu tạo và cũng xóa các con của nó. Các vật dụng chỉ đơn giản là đề cập đến đứa trẻ để hiển thị hoặc thay đổi chúng. Vấn đề là các tiện ích nên tham chiếu đến đứa trẻ như thế nào ...

hiện tại tôi vẫn đang sử dụng một con trỏ thô với phương pháp get() của unique_ptr nhưng điều này dường như không đúng với tôi. Tôi vẫn có thể gọi đến xóa trên con trỏ và hủy các lợi ích của con trỏ thông minh.

Cách tiếp cận thứ hai là sử dụng shared_ptr vì nhiều đối tượng tham chiếu đến trẻ và chỉnh sửa nó. Ngoài ra tai nạn xóa nó trong một widget sẽ không gây hại vì nó vẫn thuộc sở hữu của các đối tượng khác. Vấn đề là họ sở hữu nó. Khi tôi muốn xóa nó khỏi đối tượng đã chỉnh sửa, tôi cũng phải báo hiệu tất cả các tiện ích để xóa nó trước khi nó thực sự biến mất. (điều này một lần nữa có vẻ thiếu sót và dễ bị lỗi)

Tôi không thực sự hài lòng với cả hai cách. Có cách nào sạch (er) để trỏ đến con unique_ptr của đối tượng? Hay tôi thiếu một cách tiếp cận hoàn toàn khác và tốt hơn cho vấn đề này?

+0

Bạn có cần các đối tượng tham chiếu được cảnh báo khi đối tượng được tham chiếu bị hủy? –

+2

Trong những tình huống như vậy, chúng tôi thực sự cần [con trỏ thông minh câm] (http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2013/n3514.pdf). –

+0

@BenjaminLindley Hiện tại, chúng cần được cảnh báo để xóa các tiện ích/điều khiển tương ứng và không sử dụng chúng khi đã xóa. ví dụ. một widget cần có được tọa độ của một đứa trẻ để đặt nó vào trong OpenGL renderview. Nhưng tôi thực sự cố gắng tránh điều này bởi vì mỗi widget cần phản ứng với tín hiệu phát ra. Nó sẽ là lý tưởng hơn khi chủ sở hữu có thể xác định chính nó cho dù một đối tượng vẫn còn tồn tại hay không. – nils277

Trả lời

2

Bạn muốn sử dụng shared_ptr thay cho số unique_ptrweak_ptr của bạn thay cho con trỏ thô của bạn. Điều này sẽ cung cấp cho chính xác những gì bạn đang sau. weak_ptr sẽ không ảnh hưởng đến khả năng xóa đối tượng cơ bản của đối tượng đã chỉnh sửa.

+0

Cảm ơn, điều này trông giống như điều chính xác tôi đang tìm kiếm. – nils277

1

Trường hợp sử dụng của bạn không dịch trực tiếp đến yêu cầu (nếu ai đó xóa tiện ích khi bạn đang chỉnh sửa nó?) Nhưng tôi cho rằng bạn không cần bất cứ thứ gì ngoài con trỏ thường.

Thư viện chuẩn không cung cấp bất kỳ lớp trình theo dõi con trỏ nghiêm ngặt nào.Trong số các đối tượng quan sát:

  • Nullable, con trỏ có thể thay đổi kiểu bản địa (T *)
  • Non-nullable, tài liệu tham khảo không thể thay đổi kiểu bản địa (T &)
  • Non-nullable, tài liệu tham khảo có thể thay đổi/proxy của lớp loại (std::reference_wrapper<T>)
  • Nullable, tự phê chuẩn, con trỏ có thể thay đổi các đối tượng quản lý (std::weak_ptr<T>)

Nếu bạn muốn một con trỏ không thể vô hiệu hóa, có thể thay đổi được với đối tượng không được quản lý, đó là một điều khá hợp lý để bạn muốn, bạn có thể cuộn con trỏ của riêng mình.

Nhưng con trỏ khỏa thân không phải là xấu. Điểm khác biệt duy nhất là nó có thể là nullptr và rằng nó không có tên đẹp, dài, rõ ràng bên trong không gian tên std.

1

Nếu bạn đang sử dụng Qt, bạn có thể xem xét sử dụng các con trỏ thông minh Qt thay vì std :: gợi ý thông minh:

hoặc cho QObjects:

  • QPointer (hoặc QWeakPointer, đặc biệt là trong Qt4)

hoặc để đạt được copy-on-write chia sẻ dữ liệu, Qt container và QString phong cách:

Có lớp con trỏ khác quá, nhưng một số những điều trên có nhiều khả năng để làm những gì bạn muốn. Cũng quan trọng, nếu bạn có dữ liệu của bạn trong các lớp chứa Qt, QStrings hoặc chúng, chúng xử lý bộ nhớ của riêng chúng bằng ngữ nghĩa sao chép trên ghi và thường được chuyển xung quanh dưới dạng giá trị đơn giản (đôi khi làm tham chiếu) thay cho con trỏ.

Nhưng quan trọng nhất, không sử dụng std::unique_ptr hoặc std::shared_ptr với QObjects có cha mẹ, bởi vì nếu cha mẹ xóa con đầu tiên, sau đó con trỏ :: sẽ xóa nó một lần nữa, làm rơi chương trình (cách khác sẽ hoạt động ok, trẻ sẽ thông báo cho cha mẹ của nó rằng nó đã bị xóa). Nói cách khác, có một cơ hội tốt của các lỗi tinh tế nếu bạn trộn QObjects và std :: con trỏ, do đó, chỉ cần không làm điều đó. Nó không rõ ràng từ câu hỏi của bạn nếu bạn đang làm điều đó, nói rằng chỉ trong trường hợp.

+0

Bạn có một điểm tốt ở đó. Tôi đã không mong đợi 'QObject' để có một vấn đề với std :: con trỏ. Tôi nghĩ rằng 'QSharedPointer' và' QWeakPointer' là con đường để đi cho tôi, kể từ khi đối tượng của tôi lấy được từ QObject để sử dụng tín hiệu và khe. – nils277

+0

@ nill277 Lưu ý rằng vấn đề tương tự tồn tại với 'QSharedPointer', nó không có hỗ trợ đặc biệt cho' QObject' bị cha mẹ xóa. Nếu không có đối tượng cha mẹ, thì nó là tốt, nhưng nếu không, điều tương tự cũng được áp dụng. Tôi đoán đó là một cách ok để làm điều đó, nếu QObject của bạn không có bất kỳ cha mẹ/chủ sở hữu hợp lý, chỉ cần cẩn thận bạn nhớ để giữ nó theo cách đó (chẳng hạn như, không có một nhà xây dựng cho phép cho cha mẹ). Nếu họ có cha mẹ, sau đó chỉ cần sử dụng 'QPointer' để giữ tài liệu tham khảo yếu ở nơi khác. – hyde

0

Có đề xuất cho số world's dumbest smart pointer, là con trỏ không sở hữu. Về cơ bản, đó là số T* có nội dung "oh, và nhân tiện, tôi không yêu cầu bất kỳ quyền sở hữu nào đối với dữ liệu này".

Trong trường hợp này, bạn phải quản lý con trỏ quan sát/thụ động/câm được đặt lại nếu số unique_ptr biến mất - một nửa quản lý toàn thời gian thủ công. Sử dụng số T* thô có tên chỉ ra rằng đó là tác phẩm không sở hữu cũng như trước khi có ở trên.

Có lợi ích khi thực hiện ở trên, đặc biệt là khi bạn có đối tượng có tuổi thọ phụ thuộc.

Nếu không, thì shared_ptrweak_ptr sẽ hoạt động. Tuy nhiên, lưu ý rằng bất kỳ ai có số weak_ptr đều có thể tạo shared_ptr mới, có thể kéo dài tuổi thọ của đối tượng được chia sẻ vượt quá shared_ptr ban đầu. Điều này phải xảy ra, vì nếu không có điều kiện chủng tộc theo đó người dùng của weak_ptr đảm bảo dữ liệu của nó hợp lệ, sau đó sử dụng nó, nhưng trước khi dữ liệu của shared_ptr bị hủy.

Vì vậy, weak_ptr phải bằng cách ngăn chặn việc bị hư hỏng shared_ptr (chặn trong ngữ cảnh đa luồng, khiến nó "thất bại" bằng cách nào đó trong ngữ cảnh đơn luồng). "Thất bại" trong trường hợp này bao gồm kéo dài tuổi thọ, giải quyết cả vấn đề đa luồng và luồng đơn.

(Vấn đề duy nhất luồng là bạn xác minh weak_ptr là tốt, làm cái gì mà làm cho shared_ptr để thiết lập lại, sau đó tiếp tục muốn sử dụng dữ liệu của weak_ptr mà không kiểm tra lại. Ngây thơ, nó có thể tránh được với trường hợp ngoại lệ, nhưng khi kiểm tra kỹ hơn, bạn gặp phải các vấn đề mà bạn phải mã hóa tất cả các phương pháp để kiểm tra xóa trước khi truy cập this và ném nếu this bị xóa, yêu cầu khá khắc nghiệt!)

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