2013-07-30 27 views
7

Một số của C++ 11 std::unique_ptr sử dụng và gotchas là gì?Một số tiêu chuẩn :: unique_ptr sử dụng và "gotchas"

Tôi có thể sử dụng std::unique_ptr cũng để lưu trữ mảng được phân bổ động không?

Tôi có thể sử dụng std::unique_ptr cũng với các tài nguyên sử dụng cơ chế xóa tùy chỉnh không?

Trả lời

18

Hãy tổ chức một số công dụng và gotchas sử dụng định dạng Q & A.


Q1: Tôi muốn để lưu trữ một con trỏ để một lớp Component bên trong lớp học của tôi X.
Tôi không muốn lớp "chứa" X có thể sao chép được; X là chủ sở hữu duy nhất của Component trường hợp.
Tôi biết rằng sở hữuliệu con trỏ là một điều xấu và các nguồn tiềm năng của "leaktrocities" (thay vì quan sát con trỏ nguyên cũng tốt). Số con trỏ thông minh tôi có thể sử dụng cho mục đích này?

A1: C++ 11 của std::unique_ptr chắc chắn là một lựa chọn tốt.

Trong trường hợp của quyền sở hữu độc đáo (không chia sẻ) và không có phí trên std::shared_ptr.
Đây là một sự thay thế tuyệt vời cho C++ 98/03 boost::scoped_ptr trước đó.
Thực tế, ngoài ra, std::unique_ptr cung cấp di chuyển ngữ nghĩa.
Vì vậy, nếu lớp học X chứa unique_ptr<Component> thành viên dữ liệu (và các thành viên dữ liệu di động khác), toàn bộ lớp học X sẽ là tự động di chuyển.

Một sử dụng ví dụ được thể hiện dưới đây:

#include <memory> // for std::unique_ptr 

class X 
{ 
    std::unique_ptr<Component> m_pComponent; 
    .... 

public: 
    X() 
     : m_pComponent(new Component()) 
    { 
     .... 
    } 
} 

(. Tất nhiên, là một thông minh con trỏ , không cần để xóa một cách rõ ràng trong lớp destructor chứa)


Q2: Tuyệt vời! Không cần phá hủy rõ ràng, không có chi phí std::shared_ptr (triết lý điển hình C++: "Chúng tôi không thanh toán cho những thứ chúng tôi không sử dụng"), di chuyển máy móc ngữ nghĩa đã được triển khai!
Tuy nhiên, tôi có một vấn đề: lớp học của tôi Component có một quá tải xây dựng mà mất một số tham số mà tôi cần phải tính toán trong mã nhà xây dựng trước khi tạo ra Component trường hợp.Tôi đã cố gắng sử dụng bình thường operator= assigment trong constructor để gán mới được tạo ra Component đến unique_ptr, nhưng tôi có một thông báo lỗi:

X::X() 
{ 
    .... 

    const int param = CalculateCoolParameter(); 

    // This assignment fails: 
    m_pComponent = new Component(param); // <---- Error pointing to '=' here 
       ^--- error 
} 

A2: OK, có lẽ bạn đã mong đợi một tình trạng quá tải operator= phát hành trước đó thuộc sở hữu con trỏ (nếu có) và gán cho con mới tạo.
Thật không may, không quá tải.
Tuy nhiên, phương thức std::unique_ptr::reset() sẽ làm!

m_pComponent.reset(new Component(param)); 

Q3: Hey! Điều này unique_ptr là thực sự mát mẻ! Tôi thích thực tế là nó thông minh, nó tự động di chuyển và không mang lại chi phí.
Vì vậy, tôi muốn sử dụng nó để lưu trữ mảng được phân bổ động của một số kích thước cố định (được tính tại thời gian chạy) thay vì sử dụng std::vector (trong phần mã này muốn trả tiền cho chi phí std:vector, vì tôi không muốn tất cả các tính năng tự động thay đổi kích thước std::vector, sao chép sâu, v.v ...).

tôi đã cố gắng một cái gì đó như thế này:

const size_t count = GetComponentsCount(); 
unique_ptr<Component> components(new Component[count]); 

Nó biên dịch tốt, nhưng tôi lưu ý rằng ~Component destructor được gọi là chỉ lần, thay vào đó tôi mong đợi count cuộc gọi destructor! Có gì sai ở đây?

A3: Vấn đề là, với cú pháp trên, std::unique_ptr sử dụng delete để giải phóng các đối tượng được phân bổ. Nhưng kể từ khi chúng được phân bổ bằng cách sử dụng new[], cuộc gọi dọn sạch thích hợp là delete[] (không phải đơn giản delete không có dấu ngoặc vuông).

Để khắc phục điều đó và để hướng dẫn unique_ptr để sử dụng đúng cách delete[] giải phóng nguồn lực, cú pháp sau đây phải được sử dụng:

unique_ptr<Component[]> components(new Components[count]); 
//     ^^ 
// 
// Note brackets "[]" after the first occurrence of "Component" 
// in unique_ptr template argument. 
// 

Q4: Đó là tuyệt vời! Nhưng tôi có thể sử dụng unique_ptr cũng trong trường hợp trong đó các mã nguồn phát hành không được thực hiện bằng C thường ++ delete (hoặc delete[]), nhưng thay vì sử dụng một số chức năng dọn dẹp tùy chỉnh, như fclose() cho các tập tin C <stdio.h> (mở với fopen()), hoặc CloseHandle() cho tập tin Win32 HANDLE s (được tạo bằng cách sử dụng CreateFile())?

A4: Đó là chắc chắn có thể: bạn có thể chỉ định một deleter tùy chỉnh cho std::unique_ptr.

ví dụ::

// 
// Custom deleter function for FILE*: fclose(). 
// 
std::unique_ptr<FILE,   // <-- the wrapped raw pointer type: FILE* 
       int(*)(FILE*)> // <-- the custom deleter type: fclose() prototype 
myFile(fopen("myfile", "rb"), // <-- resource (FILE*) is returned by fopen() 
     fclose);    // <-- the deleter function: fclose() 



// 
// Custom deleter functor for Win32 HANDLE: calls CloseHandle(). 
// 
struct CloseHandleDeleter 
{ 
    // The following pointer typedef is required, since 
    // the raw resource is HANDLE (not HANDLE*). 
    typedef HANDLE pointer; 

    // Custom deleter: calls CloseHandle(). 
    void operator()(HANDLE handle) const 
    { 
     CloseHandle(handle); 
    } 
}; 

std::unique_ptr<HANDLE, CloseHandleDeleter> myFile(CreateFile(....)); 
+0

FWIW, bạn sẽ trả cho các tính năng vectơ bạn không sử dụng? (gợi ý: không có gì) –

+0

Ít nhất từ ​​dấu chân bộ nhớ, 'std :: vector' có thể sử dụng 3 con trỏ, thay vào đó' unique_ptr' chỉ một. –

+0

A2: một giải pháp đẹp hơn, nếu có thể, là có một phương pháp làm phép đo tốc độ và trả về tiêu chuẩn :: unique_ptr, sau đó sử dụng quyền đó trong danh sách khởi tạo. – stijn

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