2017-02-09 24 views
11

Xét đoạn mã sau:Về vận hành an toàn liên quan đến con trỏ độc đáo

#include <memory> 

struct Foo { std::unique_ptr<Foo> next; }; 
void f(Foo &foo) { foo = std::move(*foo.next); } 

int main() { 
    Foo foo{}; 
    foo.next = std::make_unique<Foo>(); 
    foo.next->next = std::make_unique<Foo>(); 
    f(foo); 
} 

Bằng cách foo = std::move(*foo.next);, foo.next.next được chuyển đến foo.next.
Nếu foo.next không hợp lệ làm bước đầu tiên, đối tượng mà điểm đó có thể bị xóa ngay lập tức. Điều này sẽ dẫn đến việc xóa foo.next.next, đó là đối tượng mà tôi đang cố di chuyển đến foo.next.
Tôi khá chắc chắn rằng tôi đang thiếu một cái gì đó trong lý luận của tôi, nhưng tôi không thể tìm ra những gì sai.
Đây có phải là hoạt động an toàn không? Tiêu chuẩn này làm tôi yên tâm về điều đó ở đâu?

Trả lời

7

Tôi nghĩ nó hoàn toàn an toàn. Khi bạn gọi hàm f() trên foo, toán tử gán nhiệm vụ di chuyển của class Foo sẽ gọi std::unique_ptr<Foo>::operator=(std::unique_ptr<Foo>&&). Bây giờ, các tiêu chuẩn C++ 14, §20.8.1.2.3, dấu phẩy 2, nói:

Effects: Chuyển quyền sở hữu u-*this như thể bằng cách gọi reset(u.release()) Tiếp theo get_deleter() = std::forward<D>(u.get_deleter()).

Tại §20.8.1.2.5, dấu phẩy 4, chúng ta thấy hành vi của reset():

Effects: gán p để con trỏ lưu trữ, và sau đó nếu giá trị cũ của lưu trữ con trỏ, old_p, không bằng nullptr, gọi get_deleter()(old_p). [Lưu ý: Thứ tự của các hoạt động này là đáng kể vì cuộc gọi đến get_deleter() có thể hủy *this. -end lưu ý]

Vì vậy, chúng ta có thể tranh luận rằng lưu trữ con trỏ sẽ được thay thế và sau đó các lưu trữ con trỏ cũ sẽ bị xóa, theo thứ tự này. Vì vậy, mọi thứ đều ổn và được xác định rõ.

Hơn nữa, khi bạn sẽ nhập vào reset() chức năng, các đối tượng *foo.next chắc chắn đã được release() d, do đó đối tượng nhọn sẽ không bị hủy diệt với nó.

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