2012-03-14 30 views
45

Tôi đang cố gắng "hiện đại hóa" một số mã hiện có.Chuyển unique_ptr đến các hàm

  • Tôi có một lớp hiện có biến thành viên "Device * device_".
  • Nó sử dụng mới để tạo một cá thể trong một số mã khởi tạo và có "xóa thiết bị_" trong phần hủy.
  • Các chức năng thành viên của lớp này gọi nhiều các chức năng khác lấy Thiết bị * làm thông số.

Điều này hoạt động tốt, nhưng để "hiện đại hóa" mã của tôi, tôi nghĩ tôi phải thay đổi biến được định nghĩa là "std::unique_ptr<Device> device_" và xóa cuộc gọi rõ ràng để xóa mã.

Câu hỏi của tôi là thế này -

  • Làm thế nào tôi nên sau đó vượt qua thiết bị _ biến cho tất cả các chức năng mà cần nó như là một paramater?

Tôi có thể gọi .get để nhận con trỏ thô trong mỗi cuộc gọi hàm. Nhưng điều đó có vẻ xấu xí và lãng phí một số lý do để sử dụng một unique_ptr ở nơi đầu tiên.

Hoặc tôi có thể thay đổi mọi chức năng để thay vì tham số loại "Device *", giờ đây nó có tham số loại "std :: unique_ptr &". Mà (đối với tôi) phần nào làm xáo trộn các nguyên mẫu hàm, và làm cho chúng khó đọc.

Thực tiễn tốt nhất cho việc này là gì? Tôi có bỏ lỡ bất kỳ lựa chọn nào khác không?

+3

Tại sao sự thay đổi này sẽ làm cho các mã an toàn hơn và nói chung là tốt hơn? Từ mô tả của bạn, tôi sẽ nói rằng điều ngược lại là đúng. –

+0

@ James Kanze Tôi đồng ý, unique_ptr dường như không mua bất cứ thứ gì trong trường hợp này. – juanchopanza

+2

Bạn cũng có thể đúng. Tôi thích "an toàn" của unique_ptr trong đó bạn không cần phải nhớ để xóa nó, nhưng nó dường như có một số chi phí trong trường hợp này. – jcoder

Trả lời

44

Trong phong cách hiện đại C++, có hai phím khái niệm:

  • Sở hữu
  • Huỷ bỏ

Sở hữu là về chủ sở hữu của một số đối tượng/nguồn (trong trường hợp này , một ví dụ của Device). std::unique_ptr, boost::scoped_ptr hoặc std::shared_ptr khác nhau về quyền sở hữu.

Nullity đơn giản hơn nhiều: tuy nhiên nó chỉ thể hiện một đối tượng cụ thể có rỗng hay không và không quan tâm đến bất kỳ điều gì khác và chắc chắn không phải về quyền sở hữu!


Bạn đã đúng để di chuyển thực hiện lớp học của bạn đối với unique_ptr (nói chung), mặc dù bạn có thể muốn có một con trỏ thông minh với ngữ nghĩa bản sao sâu nếu mục tiêu của bạn là để thực hiện một pImpl.

Điều này thể hiện rõ ràng rằng lớp học của bạn là người duy nhất chịu trách nhiệm về bộ nhớ này và giao dịch gọn gàng với tất cả các cách bộ nhớ khác nhau có thể bị rò rỉ.


Mặt khác, hầu hết người dùng các nguồn tài nguyên không thể chăm sóc ít hơn về quyền sở hữu của nó.

Miễn là hàm không giữ tham chiếu đến đối tượng (lưu trữ nó trong bản đồ hoặc thứ gì đó), thì tất cả những vấn đề đó là tuổi thọ của đối tượng vượt quá thời lượng của cuộc gọi hàm.

Vì vậy, lựa chọn như thế nào để vượt qua các tham số phụ thuộc vào khả năng của nó Huỷ bỏ:

  • Never null? Vượt qua một tham chiếu
  • Có thể không?Vượt qua một con trỏ , một con trỏ trần đơn giản hay một lớp con trỏ tương tự (với một cái bẫy trên null ví dụ)

+0

Hmm, tài liệu tham khảo ... Tôi có thể làm cho các chức năng của tôi có tham số "Thiết bị & thiết bị" và sau đó trong mã gọi sử dụng * device_ vì nó vẫn hoạt động với unique_ptr và tôi biết device_ không thể rỗng (và có thể kiểm tra với xác nhận). Bây giờ tôi cần phải suy nghĩ rằng một chút và quyết định nếu nó xấu xí hay thanh lịch! – jcoder

+2

@JohnB: tốt, quan điểm của tôi được xem là xóa bỏ khả năng vô hiệu tại thời gian biên dịch an toàn hơn nhiều :) –

+6

@ JohnB: Hoàn toàn đồng ý với Matthieu ở đây và tôi thậm chí còn đi xa hơn, tại sao bạn muốn tự động phân bổ một đối tượng có tuổi thọ bị ràng buộc bởi tuổi thọ của đối tượng giữ con trỏ? Tại sao không khai báo nó như một thành viên đơn giản với bộ nhớ tự động? –

7

Tôi sẽ sử dụng std::unique_ptr const&. Sử dụng tham chiếu không const sẽ cung cấp cho hàm được gọi là khả năng đặt lại con trỏ của bạn.
Tôi nghĩ đây là một cách hay để thể hiện rằng hàm được gọi của bạn có thể sử dụng con trỏ nhưng không có gì khác.
Vì vậy, đối với tôi điều này sẽ làm cho giao diện dễ đọc hơn. Tôi biết rằng tôi không phải loay hoay xung quanh với con trỏ truyền cho tôi.

+0

Vì vậy, để được rõ ràng, thêm const có nghĩa là tôi không thể thay đổi unique_ptr trong bất kỳ cách nào, nhưng tôi * có thể * gọi các chức năng không const của đối tượng chứa thông qua unique_ptr? – jcoder

+0

@JohnB: Có const ngăn chặn hàm được gọi để gọi, ví dụ: đặt lại trên unique_ptr của bạn. Nhưng vẫn miễn là con trỏ bên trong không phải là const bạn có thể gọi các hàm không const. – mkaes

+0

Có, bạn có thể. get() const phương thức của unique_ptr trả về con trỏ không const đến đối tượng được bảo vệ bởi unique_ptr – zabulus

1

Điều đó có thể không khả thi đối với bạn nhưng thay thế mỗi lần xuất hiện là Device* bởi const unique_ptr<Device>& là một khởi đầu tốt.

Bạn rõ ràng không thể sao chép unique_ptr giây và bạn không muốn di chuyển. Thay thế bằng tham chiếu đến unique_ptr cho phép cơ thể của các cơ quan chức năng hiện tại tiếp tục hoạt động.

Bây giờ có một cái bẫy, bạn phải vượt qua const & để ngăn chặn callees làm unique_ptr.reset() hoặc unique_ptr().release(). Lưu ý rằng điều này vẫn chuyển một con trỏ có thể sửa đổi sang thiết bị. Với giải pháp này, bạn không có cách nào dễ dàng để chuyển con trỏ hoặc tham chiếu đến const Device.

+0

Vì const unique_ptr & khó sử dụng, tôi sẽ gõ vào Device_t hoặc tương tự. –

14

Nó thực sự phụ thuộc. Nếu hàm phải có quyền sở hữu của unique_ptr, thì chữ ký của nó phải có giá trị unique_ptr<Device> bv giá trị và người gọi phải là std::move con trỏ. Nếu quyền sở hữu không phải là một vấn đề, sau đó tôi sẽ giữ chữ ký con trỏ thô và chuyển con trỏ unique_ptr bằng cách sử dụng get(). Đây không phải là xấu xí nếu chức năng được đề cập không chiếm quyền sở hữu.

+0

Có, tôi không muốn lấy quyền sở hữu, chỉ cần sử dụng vật nhọn để đối tượng trong hàm. – jcoder

+2

Bạn cũng có thể chuyển tham chiếu const tới unique_ptr nếu quyền sở hữu không phải là vấn đề. Một số người sử dụng nhị phân tham chiếu con trỏ để mô phỏng ngữ nghĩa tham số chính thức trong của Pascals (hướng dẫn kiểu Google C++). –

2

Thực hành tốt nhất có thể không sử dụng std::unique_ptr trong trường hợp này, mặc dù điều đó phụ thuộc. (Bạn thường không nên có nhiều hơn một con trỏ thô cho đối tượng được phân bổ động trong một lớp. Mặc dù điều này cũng phụ thuộc.) Điều bạn không muốn làm trong trường hợp này là đi qua khoảng std::unique_ptr (và dưới dạng bạn đã nhận thấy, std::unique_ptr<> const& hơi khó sử dụng và gây khó chịu). Nếu là con trỏ được chỉ định động duy nhất trong đối tượng, tôi chỉ cần dán với con trỏ thô và delete trong trình phá hủy. Nếu có một số con trỏ như vậy, tôi sẽ cân nhắc chuyển xuống từng số một thành một lớp cơ sở riêng biệt (nơi chúng vẫn có thể là con trỏ thô).

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