2015-05-28 19 views
8

[Trả lời cho câu hỏi this]khởi của shared_ptr <T> từ unique_ptr <T[]>

Tôi đã xử lý một chút với con trỏ thông minh để mảng c-phong cách thời gian gần đây. Cuối cùng, tôi sẽ làm điều được khuyên dùng và sử dụng con trỏ thông minh cho vectơ thay vào đó, nhưng trong thời gian đó, tôi có một chút lời khuyên: không sử dụng đối tượng shared_ptr<T> để quản lý mảng ban đầu được thực hiện với make_unique<T[]> vì nó sẽ không gọi delete[] nhưng thay vì delete.

này dường như không hợp lý với tôi, và tôi đã kiểm tra cả Coliru và Tiêu chuẩn:


Mã này:

#include <iostream> 
#include <memory> 

int main() 
{ 
    std::cout << "start!\n"; 
    auto customArrayAllocator = [](unsigned int num){ 
     std::cout << "custom array allocator\n"; 
     return new int[num]; 
    }; 

    std::cout << "allocator constructed\n"; 

    auto customArrayDeleter = [](int *ptr){ 
     std::cout << "custom array deleter\n"; 
     delete[] ptr; 
    }; 

    std::cout << "deleter constructed\n"; 

    std::unique_ptr<int[], decltype(customArrayDeleter)> 
     myUnique(customArrayAllocator(4), customArrayDeleter); 

    std::cout << "unique_ptr constructed\n"; 

    std::shared_ptr<int> 
     myShared = std::move(myUnique); 

    std::cout << "shared_ptr constructed\n"; 
} 

sản xuất sản lượng này:

start! 
allocator constructed 
deleter constructed 
custom array allocator 
unique_ptr constructed 
shared_ptr constructed 
custom array deleter 

Điều này dường như chỉ ra rằng dấu phân cách của unique_ptr<T[]> được chuyển đến shared_ptr<T>, như tôi mong đợi.


Từ C++ 14 Chuẩn § 20.8.2.2.1 pg. 571 of doc, 585 of pdf

mẫu shared_ptr (unique_ptr & & r);
Nhận xét: Nhà xây dựng này không được tham gia vào độ phân giải quá tải trừ khi unique_ptr :: pointer chuyển đổi thành T *.
Hiệu ứng: Tương đương với shared_ptr (r.release(), r.get_deleter()) khi D không phải là loại tham chiếu, nếu không shared_ptr (r.release(), ref (r.get_deleter())).
An toàn ngoại lệ: Nếu ngoại lệ được ném, hàm tạo không có hiệu lực.

Nếu tôi đọc đúng, điều đó có nghĩa là đối tượng shared_ptr tự xây dựng từ cả con trỏ và dấu phân cách unique_ptr. Hơn nữa, đó là sự hiểu biết của tôi (từ câu trả lời cho câu hỏi gốc) rằng loại ::pointer của unique_ptr<T[]>T*, nên chuyển đổi thành T* của shared_ptr<T>::pointer. Vì vậy, các deleter chỉ nên được sao chép ngay từ đối tượng unique_ptr, phải không?


Đã thử nghiệm của tôi chỉ làm việc vì nó không thực sự tương đương với chức năng của std::make_shared<T[]>, hoặc là cú pháp

std::shared_ptr<T> mySharedArray = std::make_unique<T[]>(16); 

một tốt, ngoại lệ an toàn (và sạch hơn) thay thế cho

std::shared_ptr<T> mysharedArray(new T[16], [](T* ptr){delete[] ptr;}); 

và ilk của nó, khi tôi không thể sử dụng số điện thoại shared array của Boost và muốn tránh bao gồm cả số vector hoặc tiêu đề array h mã của tôi?

+1

N.B. mặc dù mảng được đảm bảo được xóa chính xác, có một vấn đề an toàn tiềm năng khi chuyển đổi 'unique_ptr '->' shared_ptr '->' shared_ptr ', xem http://stackoverflow.com/q/32483375/981959 –

Trả lời

6

Có, ví dụ của bạn là hợp lệ vì những lý do bạn đã nói. unique_ptr::pointerint * và bạn đang cố chuyển quyền sở hữu đó cho một số shared_ptr<int>, do đó trình tạo chuyển đổi bạn đã liệt kê sẽ tham gia vào quá trình phân giải quá tải và sẽ tạo bản sao của deleter (std::default_delete<int[]>) vì đó không phải là loại tham chiếu.

Vì vậy, sau đây là hợp lệ, và sẽ gọi delete[] khi đếm shared_ptr tài liệu tham khảo đi đến số không

std::shared_ptr<T> mySharedArray = std::make_unique<T[]>(16); 

Một cách khác để viết những dòng này, khác hơn so với lambda bạn đã thể hiện, là

std::shared_ptr<T> mySharedArray(new T[16], std::default_delete<int[]>()); 

sẽ cho kết quả là mySharedArray có cùng deleter giống như dòng trước đó.

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