2012-03-12 18 views
6

Tôi đang sử dụng boost :: make_shared lần đầu tiên để tạo các đối tượng được trỏ đến bởi các con trỏ được chia sẻ. Chủ yếu là vì mã của chúng tôi quá chậm và phân bổ đơn lẻ thực sự giúp cải thiện hiệu suất.boost :: make_shared không gọi điện thoại (vị trí) nhà điều hành mới?

Sau khi sửa lỗi rò rỉ bộ nhớ "Tôi đã quyết định triển khai bộ dò tìm rò rỉ bộ nhớ đơn giản bằng cách ghi đè toán tử mới cho tất cả các lớp liên quan chỉ để đếm đối tượng nào vẫn còn sống tại các điểm cụ thể trong ứng dụng của chúng tôi. Tôi đã thực hiện điều này nhiều lần trước đây và đã rất ngạc nhiên khi thấy mã của tôi không còn phát hiện bất kỳ đối tượng nào.

I figured rằng tất cả những gì phải làm là ghi đè lên "vị trí mới" thay vì các nhà điều hành "bình thường" mới vì những điều sau đây từ tài liệu trang web thúc đẩy cho make_shared:

"Effects: bộ nhớ Phân bổ phù hợp đối với một đối tượng kiểu T và xây dựng một đối tượng trong nó thông qua vị trí biểu thức mới mới (pv) T() hoặc mới (pv) T (std :: forward (args) ...). sử dụng bản sao của bộ nhớ để cấp phát bộ nhớ. Nếu ngoại lệ được ném, không có hiệu lực . "

Vị trí mới của tôi cũng không được gọi. Tôi đã viết một chương trình thử nghiệm nhỏ để tạo lại hành vi:

#include <iostream> 
using namespace std; 
#include "boost/shared_ptr.hpp" 
#include "boost/make_shared.hpp" 

class Test 
{ 
public: 
    Test() { cout << "Test::Test()" << endl; } 

    void* operator new (std::size_t size) throw (std::bad_alloc) { 
     cout << "Test new" << endl; 
     return malloc(size); 
    } 

    void* operator new (std::size_t size, const std::nothrow_t& nothrow_constant) throw() { 
     cout << "Test non-throwing new" << endl; 
     return malloc(size); 
    } 

    void* operator new (std::size_t size, void* ptr) throw() { 
     cout << "Test non-throwing placement new" << endl; 
     return malloc(size); 
    } 
}; 

void* operator new (std::size_t size) throw (std::bad_alloc) { 
    cout << "Global new" << endl; 
    return malloc(size); 
} 

int main() { 
    cout << "..." << endl; 
    boost::shared_ptr<Test> t1(boost::make_shared<Test>()); 
    cout << "..." << endl; 
    boost::shared_ptr<Test> t2(new Test()); 
    cout << "..." << endl; 

    return 0; 
} 

nào làm cho đầu ra sau đây:

... 
Global new 
Test::Test() 
... 
Test new 
Test::Test() 
Global new 
... 

tôi đã mong đợi "Thử nghiệm phi ném vị trí mới" trên dòng thứ 3 của đầu ra. Bạn nghĩ hành vi nên như thế nào? Bạn có đồng ý rằng theo tài liệu của make_shared nó nên gọi vị trí điều hành mới của lớp Test của tôi? Hay tôi hiểu sai?

Tôi có thể sao chép triển khai tăng cường cục bộ và thêm cuộc gọi vào nhà điều hành vị trí mới của khóa học. Nhưng, điều đó có phù hợp không, hoặc nó có vi phạm các ngữ nghĩa dự định của vị trí mới không?

Cảm ơn trước về thời gian và sự trợ giúp của bạn.

+0

Bằng cách nhìn vào của boost, nó đang sử dụng toán tử mới vị trí toàn cục :: new (pv) T(). Đó là lý do tại sao vị trí cấp lớp của bạn không được gọi ... Bằng cách loại bỏ vòng loại toàn cầu '::' trước khi mới, hàm make_shared thực sự gọi trình điều khiển vị trí cấp lớp mới của bạn. – Gob00st

Trả lời

8

Nhìn như nguồn của make_shared, nó sử dụng toán tử vị trí toàn cầu new, thay vì toán tử mới do lớp của bạn cung cấp.

::new(pv) T(); 

Thật không may (như ít nhất trên OS X) (according to the standard), bạn không thể xác định vị trí toàn cầu điều hành mới của riêng bạn. Có vẻ như allocate_shared là nhiều hơn dọc theo dòng của những gì bạn đang tìm kiếm.

Sửa:

Một thay thế có thể là để thực sự viết một phiên bản của make_shared trong đó sử dụng vị trí của lớp mới thay vì một toàn cầu. Nó chỉ có khoảng 10 dòng mã, và sẽ ổn thôi miễn là bạn tôn vinh the license of the original code.

+0

Đáng chú ý rằng 'allocate_shared()' có thể có nghĩa là thay thế một vài cuộc gọi trong một codebase lớn hơn. –

+0

Cảm ơn câu trả lời hữu ích. Tôi không biết tôi không được phép ghi đè vị trí mới. Vì vậy, đây chắc chắn là một showstopper cho nỗ lực của tôi tại một giải pháp. Tôi đã điều tra tùy chọn allocate_shared, nhưng -as Georg đã nói - điều này có tác động quá cao đối với mã hiện có. –

4

Bạn không thể thay thế vị trí mới (§18.4. 1.3, xem ví dụ: this question), vì vậy, kết quả đầu ra có vẻ tốt.

Để thay thế cho việc sửa đổi tiêu đề Tăng, bạn có thể xem xét các công cụ bên ngoài như Valgrind.

3

bạn operator new thực hiện cho loại hình cụ thể của bạn sẽ chỉ được sử dụng trên biểu thức mà phần tử kiểu của bạn được cấp phát động với new, chẳng hạn như Test *p = new Test;. Bây giờ make_shared không động phân bổ một đối tượng kiểu của bạn, mà là một đệm chứa đầy đủ thông tin cho chia sẻ đếm (trong đó bao gồm quầy, deleter và một vài bit thêm và miếng) và đối tượng của bạn.

Sau đó, sử dụng vị trí mới để gọi hàm tạo của đối tượng của bạn. Lưu ý rằng vị trí mới trong trường hợp này không phân bổ bộ nhớ, nó chỉ là cú pháp hài hước trong C++ để gọi hàm dựng trên một khối bộ nhớ đã cấp phát. Điều này thực sự có thể là nguồn gây nhầm lẫn, như biểu thức new, operator newvị trí mới của bạn là ba khái niệm khác nhau xảy ra để chia sẻ tên.

+0

Mặc dù vậy, vẫn có thể cung cấp quá tải cho mỗi lớp ngay cả đối với vị trí mới. Bất thường có lẽ, nhưng sau đó một lần nữa, mà một phần của C++ * không phải là * khác thường :-) Điểm chính để mang về nhà là 'std :: allocator' không sử dụng bất kỳ chức năng phân bổ cho mỗi lớp, nhưng chỉ toàn cầu' :: mới'. –

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