2015-07-26 33 views
10

Thỉnh thoảng tôi cần shared_ptr trường hợp có dấu phân cách không có op, vì API mong đợi một trường hợp shared_ptr mà nó muốn lưu trữ trong một thời gian giới hạn nhưng tôi được cung cấp một con trỏ thô mà tôi không được phép sở hữu trong một thời gian lớn hơn những gì tôi đang chạy.Có phải một bí danh rỗng shared_ptr là một lựa chọn tốt cho việc xóa bỏ shared_ptr không-op không?

Đối với trường hợp này, tôi đã được sử dụng một deleter không-op, chẳng hạn như [](const void *){}, nhưng hôm nay tôi phát hiện ra rằng có một sự thay thế cho rằng, sử dụng (hoặc lạm dụng?) Các aliasing constructor của shared_ptr:

void f(ExpectedClass *ec) { 
    std::shared_ptr<ExpectedClass> p(std::shared_ptr<void>(), ec); 
    assert(p.use_count() == 0 && p.get() != nullptr); 
    apiCall(p); 
} 

Câu hỏi của tôi là, cách tốt hơn để làm điều này là gì và tại sao? Các kỳ vọng hiệu suất có giống nhau không? Với một deleter không có op tôi mong đợi để trả một số chi phí cho việc lưu trữ các deleter và tham chiếu, mà không xuất hiện là trường hợp khi sử dụng hàm tạo aliasing với shared_ptr rỗng.

+2

Vâng, có một lưu ý đề cập đến sự kỳ quặc này trong [util.smartptr.shared.const]/16. IMHO nó là kinda hệ quả của thực tế là bạn có thể có một không có sản phẩm nào 'shared_ptr' lưu trữ null (' shared_ptr ((T *) nullptr) '). Có khả năng một chút khó hiểu, nhưng hợp lệ. Một vấn đề với cách sử dụng này là bản sao của 'p' không chia sẻ quyền sở hữu với nó, bởi vì cả hai đều không sở hữu bất cứ thứ gì. Điều đó có thể quan trọng đối với một số ứng dụng mong đợi để kiểm tra các đối tượng chia sẻ quyền sở hữu (thông qua 'owner_less') nhưng có lẽ không phải là vấn đề cho việc sử dụng bạn mô tả. –

+0

@JonathanWakely cảm ơn ghi chú. Các lưu ý trước đó/15) confuses me. Tại sao người dùng phải đảm bảo rằng 'p' vẫn hợp lệ cho đến khi nhóm quyền sở hữu' r' bị hủy trong 'shared_ptr <> (r, p)'? Tôi nghĩ đó là việc kinh doanh của 'shared_ptr'. Nếu tôi chỉ lưu trữ nó đi, con trỏ bí danh sẽ luôn hợp lệ vì nó ngăn chặn nhóm quyền sở hữu 'r' khỏi bị phá hủy, để người dùng có thể" sao chép và quên "về nó, hoặc tôi thiếu cái gì đó? –

+1

Ah tôi hiểu ý nghĩa của nó. Trong trường hợp thời gian tồn tại của 'p' không bị ràng buộc với thời gian tồn tại của' r' (được cho phép), thì sự sống của 'r' không ngụ ý sự sống của' p' và sau đó người dùng phải đảm bảo điều đó. –

Trả lời

6

Về hiệu suất, điểm chuẩn sau đây cho thấy con số thất thường:

#include <chrono> 
#include <iostream> 
#include <limits> 
#include <memory> 

template <typename... Args> 
auto test(Args&&... args) { 
    using clock = std::chrono::high_resolution_clock; 
    auto best = clock::duration::max(); 

    for (int outer = 1; outer < 10000; ++outer) { 
     auto now = clock::now(); 

     for (int inner = 1; inner < 20000; ++inner) 
      std::shared_ptr<int> sh(std::forward<Args>(args)...); 

     auto time = clock::now()-now; 
     if (time < best) { 
      best = time; 
      outer = 1; 
     } 
    } 

    return best.count(); 
} 

int main() 
{ 
    int j; 

    std::cout << "With aliasing ctor: " << test(std::shared_ptr<void>(), &j) << '\n' 
       << "With empty deleter: " << test(&j, [] (auto) {}); 
} 

Output trên máy tính của tôi với clang++ -march=native -O2:

With aliasing ctor: 11812 
With empty deleter: 651502 

GCC với các tùy chọn giống hệt nhau đưa ra một tỷ lệ lớn hơn, 5921: 465.794.
Và Clang với -stdlib=libc++ mang lại con số khổng lồ 12: 613175.

+0

Trong thực tế với libC++, các phần shared_ptr được tối ưu hóa hoàn toàn khi sử dụng bí danh hack: http://coliru.stacked-crooked.com/a/84b6d94a03d180b5 –

+0

@ JohannesSchaub-litb… Ngôn ngữ đó phát ra là gì? – Columbo

+0

là ngôn ngữ trung gian (LLVM IR) trước khi được chuyển đổi thành x86.Tôi tìm thấy nó dễ dàng hơn để nắm bắt, đặc biệt là các vòng, vì nó vẫn còn ở dạng SSA. –

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