2015-12-12 31 views
20

Vì C++ 11, vì nhiều lý do, các nhà phát triển có xu hướng sử dụng các lớp con trỏ thông minh cho các đối tượng sống động. Và với những lớp con trỏ thông minh mới, tiêu chuẩn, thậm chí đề nghị không sử dụng các nhà khai thác như new thay vào đó họ đề nghị sử dụng make_shared hoặc make_unique để tránh một số lỗi dễ xảy ra.Làm thế nào để vượt qua deleter để make_shared?

Nếu chúng ta muốn sử dụng một lớp con trỏ thông minh, giống như shared_ptr, chúng ta có thể xây dựng một mặt hàng như,

shared_ptr<int> p(new int(12)); 

Ngoài ra chúng tôi muốn vượt qua một deleter tùy chỉnh để thông minh lớp con trỏ,

shared_ptr<int> p(new int(12), deleter); 

Mặt khác, nếu chúng tôi muốn sử dụng make_shared để phân bổ, ví dụ: int, thay vì sử dụng newshared_ptr constructor, giống như trên các biểu hiện đầu tiên ở trên, chúng ta có thể sử dụng

auto ip = make_shared<int>(12); 

Nhưng nếu chúng ta thích cũng phải vượt qua một deleter tùy chỉnh để make_shared, là có một cách đúng đắn để làm điều đó? Có vẻ như trình biên dịch, ít nhất là gcc, đưa ra lỗi,

auto ip = make_shared<int>(12, deleter); 
+0

Viết của riêng bạn 'make_shared()' hỗ trợ điều này, nó là doable. – user1095108

Trả lời

30

Như những người khác đã nói, make_shared không thể được sử dụng với một deleter tùy chỉnh. Nhưng tôi muốn giải thích tại sao.

Đường dẫn tùy chỉnh tồn tại vì bạn đã phân bổ con trỏ theo một số cách đặc biệt và do đó bạn cần có khả năng giải quyết nó theo cách đặc biệt tương ứng. Vâng, make_shared phân bổ con trỏ với new. Đối tượng được phân bổ với new phải được deallocated với delete. Mà các deleter tiêu chuẩn dutifully nào.

Tóm lại, nếu bạn có thể sống với hành vi phân bổ mặc định, bạn cũng có thể sống với hành vi mặc định deallocation. Và nếu bạn không thể sống với hành vi phân bổ mặc định, bạn nên sử dụng allocate_shared, sử dụng phân bổ được cung cấp cho cả việc phân bổ và xử lý lưu trữ.

Ngoài ra, make_shared được phép (và gần như chắc chắn sẽ) cấp phát bộ nhớ cho T và khối điều khiển cho shared_ptr trong cùng một phân bổ. Đây là điều mà deleter của bạn không thể thực sự biết hoặc đối phó với. Trong khi allocate_shared có khả năng xử lý nó, kể từ khi cấp phát bạn cung cấp có thể làm nhiệm vụ phân bổ và deallocation.

+2

Được thăng hạng, tôi chưa bao giờ nghĩ về điều đó, nhưng nó hoàn toàn có ý nghĩa. Gợi ý tốt. – skypjack

+0

+1 nhưng tùy chỉnh deleters không chỉ để kiểm soát deallocation, con trỏ thuộc sở hữu của một 'shared_ptr' đã không nhất thiết phải được" phân bổ "ở tất cả. Bạn có thể muốn sử dụng 'fclose' để đóng một FILE *, ví dụ. Sử dụng 'allocate_shared' cho phép bạn kiểm soát việc cấp phát bộ nhớ, nhưng không sử dụng các tài nguyên tùy ý. Bạn vẫn phải xây dựng và hủy một đối tượng, không lấy "tài nguyên" từ một hàm và giải phóng nó bằng hàm khác (deleter). Ngay cả khi [lwg 2070] (http://cplusplus.github.io/LWG/lwg-active.html#2070) được khắc phục mà vẫn không cho phép các deleters tùy chỉnh tạo/allocate_shared. –

+2

Điểm mấu chốt là bởi vì make/allocate_shared không cho phép bạn thay đổi bước "có được một tài nguyên", nó không có ý nghĩa để họ hỗ trợ các cách tùy chỉnh để "giải phóng tài nguyên". Chúng được sử dụng để tạo/hủy đơn giản việc sử dụng đối tượng 'shared_ptr'. –

4

Bạn không thể. make_shared<T> chuyển tiếp các đối số đã cung cấp cho hàm tạo của loại T. Nó được sử dụng cho các trường hợp đơn giản khi bạn muốn các deleter mặc định.

10

Kể từ số documentation, make_shared chấp nhận danh sách đối số mà một phiên bản T sẽ được xây dựng.
Hơn nữa, các tài liệu nói rằng:

Chức năng này thường được sử dụng để thay thế cho std xây dựng :: shared_ptr (mới T (args ...)) của một con trỏ được chia sẻ từ con trỏ liệu trả về bởi một cuộc gọi mới.

Do đó, bạn có thể suy ra rằng bạn không thể đặt deleter tùy chỉnh.
Để thực hiện điều đó, bạn phải tự tạo shared_ptr cho chính mình bằng cách sử dụng đúng constructor.
Như một ví dụ của một nhà xây dựng từ danh sách đề xuất, bạn có thể sử dụng:

template< class Y, class Deleter > 
shared_ptr(Y* ptr, Deleter d); 

Do đó, mã sẽ được giống như:

auto ptr = std::shared_ptr(new MyClass{arg1, arg2}, myDeleter); 

Thay vì:

auto ptr = std::make_shared<MyClass>(arg1, arg2); 
3

Không xác định cách thức make_shared lấy bộ nhớ cho đối tượng (có thể sử dụng operator new hoặc malloc hoặc một số loại cấp phát) để không có cách nào một chuyên gia tùy chỉnh biết cách làm đúng. make_shared tạo đối tượng, vì vậy bạn cũng phải dựa vào nó để tiêu diệt đối tượng một cách chính xác và làm sạch thích hợp, bất kể đó là gì.

Cũng chúng tôi muốn vượt qua một deleter tùy chỉnh để các lớp con trỏ thông minh,

shared_ptr<int> p(new int(12), deleter);

tôi không nghĩ rằng đây là một ví dụ rất thực tế. Một deleter tùy chỉnh thường được sử dụng khi tài nguyên được lấy theo một số cách đặc biệt. Nếu bạn chỉ cần tạo ra nó với new như thế này thì tại sao bạn cần một deleter tùy chỉnh anyway?

Nếu bạn chỉ muốn một số mã được chạy trên phá hủy sau đó đặt nó trong một destructor! Bằng cách đó bạn vẫn có thể sử dụng nó với make_shared ví dụ:

struct RunSomethingOnDestruction { 
    RunSomethingOnDestruction(int n) : i(n) { } 
    ~RunSomethingOnDestruction() { /* something */ } 
    int i; 
}; 

auto px = std::make_shared<RunSomethingOnDestruction>(12); 
std:shared_ptr<int> p(px, px->i); 

này mang đến cho bạn một shared_ptr<int> đó được tạo ra bởi make_shared (để bạn có được optimisations bộ nhớ thực hiện bằng cách make_shared) mà sẽ chạy một số mã tùy chỉnh trên sự hủy diệt.

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