2013-10-20 15 views
11

Chỉ cần làm rõ, sử dụng make_unique chỉ thêm sự an toàn ngoại lệ khi bạn có nhiều phân bổ trong một biểu thức, không chỉ một, đúng không? Ví dụAn toàn ngoại lệ và make_unique

void f(T*); 

f(new T); 

là hoàn toàn an toàn ngoại lệ (như xa như phân bổ và các công cụ), trong khi

void f(T*, T*); 

f(new T, new T); 

không phải là, có đúng không?

+0

Câu hỏi của bạn dường như mâu thuẫn với chính nó. Trước tiên, bạn khẳng định rằng nhiều phân bổ là ngoại lệ an toàn, sau đó bạn hiển thị một ví dụ mà điều này có vẻ bề ngoài ngược lại với những gì xảy ra. –

+0

@LightnessRacesinOrbit no, tôi khẳng định rằng nhiều phân bổ không phải là ngoại lệ an toàn. Tôi nói "make_unique chỉ thêm an toàn ngoại lệ khi bạn có nhiều phân bổ trong một biểu thức" có nghĩa là nó không thêm gì cho chỉ một phân bổ. – Kal

+0

Ah! Vâng, được rồi :) –

Trả lời

21

Không chỉ khi bạn có nhiều phân bổ, nhưng bất cứ khi nào bạn có thể ném vào các địa điểm khác nhau. Xem xét việc này:

f(make_unique<T>(), function_that_can_throw()); 

Versus:

f(unique_ptr<T>(new T), function_that_can_throw()); 

Trong trường hợp thứ hai, trình biên dịch được phép gọi số (theo thứ tự):

  • new T
  • function_that_can_throw()
  • unique_ptr<T>(...)

Rõ ràng nếu function_that_can_throw thực sự ném thì bạn bị rò rỉ. make_unique ngăn chặn trường hợp này.

Và tất nhiên, phân bổ thứ hai (như trong câu hỏi của bạn) chỉ là trường hợp đặc biệt là function_that_can_throw().

Theo nguyên tắc chung, chỉ cần sử dụng make_unique để mã của bạn nhất quán. Nó luôn luôn đúng (đọc: ngoại lệ an toàn) khi bạn cần unique_ptr và không ảnh hưởng đến hiệu suất, vì vậy không có lý do gì để không sử dụng nó (trong khi thực tế không sử dụng nó giới thiệu rất nhiều gotchas).

+2

Cảm ơn, đây là những gì tôi đang tìm kiếm. Xin lỗi để hủy hoại 2^13 đại diện của bạn. – Kal

+1

@Kal OK, tôi đang nhắm đến 2^14 ngay bây giờ ...;) – syam

6

tôi nghĩ rằng bạn muốn được tốt hơn so với những thứ thực sự sử dụng std::unique_ptr<T>:

void f(std::unique_ptr<T>); 

f(std::unique_ptr<T>(new T)); 
f(std::make_unique<T>()); 

Không phải của các cuộc gọi có thể bị rò rỉ nếu có một ngoại lệ được ném. Tuy nhiên

void f(std::unique_ptr<T>, std::unique_ptr<T>); 

g(std::unique_ptr<T>(new T), std::unique_ptr<T>(new T)); 
g(std::make_unique<T>(), std::make_unique<T>()); 

Trong trường hợp này, phiên bản sử dụng std::unique_ptr<T> rõ ràng có thể bị rò rỉ nếu một ngoại lệ được ném (vì trình biên dịch có thể bắt đầu đánh giá new -expressions trước khi xây dựng một trong hai là tạm thời).

2

Tính đến C++ 17, vấn đề an toàn ngoại lệ được cố định bởi một rewording của [expr.call]

Việc khởi của một tham số, bao gồm tất cả các tính toán giá trị liên quan và tác dụng phụ, được indeterminately trình tự liên quan đến điều đó với của bất kỳ tham số nào khác.

Đây indeterminately trình tự có nghĩa là ai được giải mã trước khác, nhưng nó không được chỉ định đó.

f(unique_ptr<T>(new T), function_that_can_throw()); 

thể chỉ có hai thứ có thể thực hiện

  1. new Tunique_ptr<T>::unique_ptrfunction_that_can_throw
  2. function_that_can_thrownew Tunique_ptr<T>::unique_ptr

Có nghĩa là nó bây giờ là an toàn ngoại lệ.

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