2013-08-28 69 views
10

Tôi đã đọc this answer và tác giả đề cập đến boost best practices mà nói:C++ phân bổ shared_ptr với C++ 11 (std :: shared_ptr): Vẫn còn xấu để khởi tạo shared_ptr thành một biến tạm thời?

Tránh sử dụng shared_ptr giấu tên là tạm thời để tiết kiệm gõ; để xem tại sao này là nguy hiểm, hãy xem xét ví dụ sau:

void f(shared_ptr<int>, int); 
int g(); 

void ok() { 
    shared_ptr<int> p(new int(2)); 
    f(p, g()); 
} 

void bad() { 
    f(shared_ptr<int>(new int(2)), g()); 
} 

Chức năng ok sau phương châm để lá thư, trong khi cấu trúc xấu shared_ptr tạm thời tại chỗ, thừa nhận khả năng của một rò rỉ bộ nhớ . Vì các đối số hàm được đánh giá theo thứ tự không xác định, có thể để int (2) mới được đánh giá đầu tiên, g() thứ hai và chúng tôi không bao giờ có thể nhận được đối với hàm tạo shared_ptr nếu g ném ngoại lệ. <...>

Vấn đề an toàn ngoại lệ nêu trên cũng có thể được loại bỏ bằng sử dụng các chức năng nhà máy make_shared hoặc allocate_shared quy định tại tăng/make_shared.hpp. Các chức năng của nhà máy này cũng cung cấp lợi ích hiệu quả bằng cách hợp nhất phân bổ.

Tôi đoán tôi sẽ bắt đầu sử dụng make_shared, nhưng tôi đã tự hỏi liệu lời khuyên này vẫn áp dụng cho C++ 11 shared_ptr. Tôi hỏi bởi vì tôi không thực sự hiểu rõ lý do tại sao nó là một ném g() sẽ ngăn chặn các ctor nhận được gọi.

Trả lời

9

Có, của C++ 11 shared_ptr hoạt động theo cùng một cách.

Tôi hỏi vì tôi không thực sự hiểu đầy đủ lý do tại sao nó là một ném g() sẽ ngăn chặn các ctor nhận được gọi.

Bạn không hiểu gì? Đó là một thứ tự của vấn đề hoạt động, và tiêu chuẩn không yêu cầu một thứ tự cụ thể. Hãy giải nén câu lệnh thành một chuỗi các biểu thức:

auto __temp = new int(2); 
auto &&__temp2 = g(); 
auto __temp3 = shared_ptr<int>(__temp); 

Bây giờ bạn có thấy vấn đề không? Nếu g ném, thì __temp3 sẽ không bao giờ được khởi chạy. Do đó, __temp sẽ bị bị rò rỉ.

Tiêu chuẩn C++ không yêu cầu tuyên bố phải được giải nén theo cách này. Nhưng nó không cấm nó một trong hai. Trình biên dịch được phép tự do đặt hàng các biểu thức độc lập này tuy nhiên nó thấy phù hợp.

+0

Tôi không thể yêu cầu một lời giải thích tốt hơn, nó chỉ thực sự bất ngờ (với tôi) rằng "thứ tự giải nén" của một cái gì đó như thế này sẽ không phải lúc nào cũng giống nhau. Ngoài ra, tôi vẫn chưa có cơ hội để có được một sự hiểu biết đúng đắn về các tham chiếu rvalue. –

+0

@StevenLu: tham chiếu rvalue không liên quan gì đến điều đó. Tôi vừa sử dụng 'auto &&' để nắm bắt kết quả của 'g()' để nó có thể được chuyển tiếp đúng khi nó đến thời gian để gọi hàm. Nó không có gì để làm với thứ tự của các hoạt động. Ngoài ra, thứ tự có thể sẽ giống nhau ... chỉ không * qua * trình biên dịch. –

+0

Ah. Thấy chưa, tôi đã thấy '&&', biết đó là "điều tôi chưa hiểu", nên tôi không điều tra thêm. Nhưng bây giờ tôi thấy đoạn mã (giả) này minh họa cách mà trình biên dịch có thể tạo mã để tạo ra 'g()' ** sau khi ** phân bổ int, và cả hai dòng đầu tiên có thể ngược lại trật tự (trong trường hợp này mọi thứ sẽ ổn). –

4

Tôi không nghĩ rằng bất kỳ điều gì đã thay đổi liên quan đến thứ tự đánh giá trong C++ 11. Tức là, sử dụng std::make_shared<T>(...) là lựa chọn tốt hơn đối với an toàn ngoài việc chỉ yêu cầu cấp phát bộ nhớ thay vì hai.

liên quan đến những vấn đề Với: việc đánh giá sau đây của f() đối số là hoàn toàn hợp lệ:

  1. $tmp = new int(1) (sử dụng $tmp để chỉ ra một trình biên dịch cung cấp, biến tạm thời)
  2. g()
  3. std::shared_ptr<int>($tmp)

Bây giờ, nếu g() ném, đánh giá trên các phần khác không bao giờ được thực thi, tức là, std::shared_ptr<int> không bao giờ được xây dựng và $tmp bị rò rỉ.