Sự khác biệt giữa std rỗng và null :: shared_ptr trong C ++ là gì?


80

Các cplusplus.com shared_ptrtrang gọi ra một sự phân biệt giữa một trống std::shared_ptr và một rỗng shared_ptr . Các trang cppreference.com không rõ ràng gọi ra sự khác biệt, nhưng sử dụng cả "trống rỗng" và so sánh để nullptrtrong mô tả của std::shared_ptrhành vi.

Có sự khác biệt giữa giá trị rỗng và rỗng shared_ptrkhông? Có trường hợp sử dụng nào cho các con trỏ hành vi hỗn hợp như vậy không? Giá trị null không rỗng shared_ptrthậm chí có ý nghĩa? Có bao giờ có trường hợp trong cách sử dụng bình thường (tức là nếu bạn không xây dựng một cách rõ ràng) nơi bạn có thể kết thúc với một rỗng-nhưng-không-null shared_ptrkhông?

Và có bất kỳ câu trả lời nào trong số này thay đổi nếu bạn đang sử dụng phiên bản Boost thay vì phiên bản C ++ 11 không?

Câu trả lời:


80

Đó là một góc shared_ptrhành vi kỳ lạ . Nó có một nhà xây dựng cho phép bạn thực hiện một shared_ptrsở hữu một cái gì đó và điểm đến cái gì khác:

template< class Y > 
shared_ptr( const shared_ptr<Y>& r, T *ptr );

Cấu shared_ptrtrúc được xây dựng bằng cách sử dụng hàm tạo này chia sẻ quyền sở hữu với r, nhưng trỏ tới bất kỳ thứ gì ptrtrỏ đến (tức là gọi get()hoặc operator->()sẽ trả về ptr). Điều này rất hữu ích cho các trường hợp ptrtrỏ đến một subobject (ví dụ: một thành viên dữ liệu) của đối tượng do nó sở hữu r.

Trang bạn đã liên kết gọi a shared_ptrkhông có gì trống và a shared_ptrtrỏ đến không có gì (tức là có get() == nullptr) rỗng . ( Tiêu chuẩn sử dụng rỗng theo nghĩa này; null thì không.) Bạn có thể xây dựng null-but-not-blank shared_ptr, nhưng nó sẽ không hữu ích lắm. Một sản phẩm nào-nhưng-không-null shared_ptrvề bản chất là một con trỏ không sở hữu, có thể được sử dụng để làm một số điều kỳ lạ như đi qua một con trỏ đến một cái gì đó được phân bổ trên cây rơm vào một chức năng hy vọng mộtshared_ptr (nhưng tôi muốn đề nghị đấm bất cứ ai đặt shared_ptrbên trong API đầu tiên).

boost::shared_ptrcũng có hàm tạo này , mà họ gọi là hàm tạo răng cưa .


8
Đáng chú ý: C ++ 11 § 20.7.2.2.1 (p16) "Lưu ý: Hàm tạo này cho phép tạo một thể hiện trống shared_ptrvới con trỏ được lưu trữ không phải NULL." Cũng đáng đề cập đến lưu ý trước (p15), "Để tránh khả năng con trỏ treo lơ lửng, người dùng của hàm tạo này phải đảm bảo rằng nó pvẫn còn hiệu lực ít nhất cho đến khi nhóm sở hữu của rbị phá hủy." Một công trình hiếm khi được sử dụng thực sự.
WhozCraig

@Cubbi Một shared_ptrget()lợi nhuận nullptr không so sánh tương đương với nullptrbất kể nó sở hữu bất cứ điều gì.
TC

3
Một null-but-nonempty shared_ptrs thể hữu ích, để đảm bảo một số hàm được thực thi khi tất cả các con trỏ sở hữu hết phạm vi (ngay cả trong trường hợp ngoại lệ!). Không chắc, liệu bây giờ có một lớp học đặc biệt cho việc này hay không.
coldfix

@coldfix Một null-but-nonempty có shared_ptrthể làm gì mà một non-null-and-nonempty shared_ptrkhông làm được?
TC

2
Hàm tạo răng cưa bắt nguồn từ Bloomberg và được đề xuất cho tiêu chuẩn trước khi nó được triển khai trong Boost (xem N1851 ). Tôi thích "sở hữu cổ phiếu có kỳ hạn của tiêu chuẩn r" vào phân nhịp "sở hữu bất cứ rsở hữu"
Jonathan Wakely

9

Có sự khác biệt giữa shared_ptr rỗng và rỗng không?

Trống shared_ptrkhông có khối điều khiển và số lần sử dụng của nó được coi là 0. Bản sao của khối trống shared_ptrlà một khối trống khác shared_ptr. Cả hai đều là các khối riêng biệt shared_ptrkhông chia sẻ khối điều khiển chung vì chúng không có nó. Rỗng shared_ptrcó thể được xây dựng với hàm tạo mặc định hoặc với hàm tạo có nullptr.

Null không trống shared_ptrcó khối điều khiển có thể được chia sẻ với các shared_ptrs khác . Bản sao không rỗng shared_ptrlà bản shared_ptrsao có cùng khối điều khiển với bản gốc shared_ptrnên số lượng sử dụng không bằng 0. Có thể nói rằng tất cả các bản sao của shared_ptrchia sẻ giống nhaunullptr . Null không rỗng shared_ptrcó thể được xây dựng bằng con trỏ null của kiểu đối tượng (not nullptr)

Đây là ví dụ:

#include <iostream>
#include <memory>

int main()
{
    std::cout << "std::shared_ptr<int> ptr1:" << std::endl;
    {
        std::shared_ptr<int> ptr1;
        std::cout << "\tuse count before copying ptr: " << ptr1.use_count() << std::endl;
        std::shared_ptr<int> ptr2 = ptr1;
        std::cout << "\tuse count  after copying ptr: " << ptr1.use_count() << std::endl;        
        std::cout << "\tptr1 is " << (ptr1 ? "not null" : "null") << std::endl;
    }
    std::cout << std::endl;

    std::cout << "std::shared_ptr<int> ptr1(nullptr):" << std::endl;
    {
        std::shared_ptr<int> ptr1(nullptr);
        std::cout << "\tuse count before copying ptr: " << ptr1.use_count() << std::endl;
        std::shared_ptr<int> ptr2 = ptr1;
        std::cout << "\tuse count  after copying ptr: " << ptr1.use_count() << std::endl;        
        std::cout << "\tptr1 is " << (ptr1 ? "not null" : "null") << std::endl;
    }
    std::cout << std::endl;

    std::cout << "std::shared_ptr<int> ptr1(static_cast<int*>(nullptr))" << std::endl;
    {
        std::shared_ptr<int> ptr1(static_cast<int*>(nullptr));
        std::cout << "\tuse count before copying ptr: " << ptr1.use_count() << std::endl;
        std::shared_ptr<int> ptr2 = ptr1;
        std::cout << "\tuse count  after copying ptr: " << ptr1.use_count() << std::endl;        
        std::cout << "\tptr1 is " << (ptr1 ? "not null" : "null") << std::endl;
    }
    std::cout << std::endl;

    return 0;
}

Nó xuất ra:

std::shared_ptr<int> ptr1:
    use count before copying ptr: 0
    use count  after copying ptr: 0
    ptr1 is null

std::shared_ptr<int> ptr1(nullptr):
    use count before copying ptr: 0
    use count  after copying ptr: 0
    ptr1 is null

std::shared_ptr<int> ptr1(static_cast<int*>(nullptr))
    use count before copying ptr: 1
    use count  after copying ptr: 2
    ptr1 is null

http://coliru.stacked-crooked.com/a/54f59730905ed2ff


1
Tôi nghĩ câu trả lời này tốt hơn tại sao chúng ta phải kiểm tra null trong trình xóa tùy chỉnh của shared_ptr. Kiểm tra nullptr trong trình phân định tùy chỉnh của shared_ptr có hợp lý không?
David Lee,
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.