2016-05-17 24 views
15

Vì vậy, khi sử dụng shared_ptr<Type> bạn có thể viết:Tại sao không shared_ptr phép giao trực tiếp

shared_ptr<Type> var(new Type()); 

Tôi tự hỏi tại sao họ không cho phép một đơn giản hơn nhiều và tốt hơn (IMO):

shared_ptr<Type> var = new Type(); 

Thay vào đó, để đạt được chức năng như vậy, bạn cần sử dụng .reset():

shared_ptr<Type> var; 
var.reset(new Type()); 

Tôi thường Lớp OpenCV Ptr là một con trỏ thông minh cho phép gán trực tiếp và mọi thứ hoạt động tốt

+3

Vì hàm tạo của 'std :: shared_ptr' lấy con trỏ là' tường minh' và không có con trỏ 'operator ='. – Jarod42

+9

Nó không phải là một nhiệm vụ. – LogicStuff

Trả lời

20

Vấn đề với phép một con trỏ liệu được ngầm chuyển đổi thành một std::shared_ptr thể được chứng minh với

void foo(std::shared_ptr<int> bar) { /*do something, doesn't matter what*/ } 

int main() 
{ 
    int * bar = new int(10); 
    foo(bar); 
    std::cout << *bar; 
} 

Bây giờ nếu chuyển đổi ngầm đã làm việc bộ nhớ bar điểm đến sẽ bị xóa bởi các destructor shared_ptr ở cuối foo(). Khi chúng ta truy cập vào nó trong std::cout << *bar;, bây giờ chúng ta có hành vi không xác định như chúng ta đang dereferencing một con trỏ bị xóa.

Trong trường hợp của bạn, bạn tạo con trỏ trực tiếp tại trang web cuộc gọi để nó không quan trọng nhưng như bạn có thể thấy từ ví dụ nó có thể gây ra vấn đề.

8

Tại sao [không] shared_ptr cho phép gán trực tiếp [copy initialization]?

Vì đó là explicit, hãy xem herehere.

Tôi tự hỏi lý do cơ bản đằng sau nó là gì? (Từ một bình luận tại loại bỏ)

TL; DR, thực hiện bất kỳ nhà xây dựng (hoặc cast) explicit là để ngăn chặn nó từ tham gia vào chuỗi chuyển đổi ngầm.

Yêu cầu đối với explicit được minh họa tốt hơn với shared_ptr<> là một đối số cho hàm.

void func(std::shared_ptr<Type> arg) 
{ 
    //... 
} 

Và được gọi là;

Type a; 
func(&a); 

Điều này sẽ biên dịch và được viết và không mong muốn và sai; nó sẽ không hoạt động như mong đợi.

Nó trở nên phức tạp hơn với việc thêm chuyển đổi do người dùng xác định (ẩn) vào các kết hợp.

struct Type { 
}; 

struct Type2 { 
    operator Type*() const { return nullptr; } 
}; 

Sau đó các chức năng sau (nếu không muốn nói rõ ràng) sẽ biên dịch, nhưng cung cấp một lỗi khủng khiếp ...

Type2 a; 
func(a); 
16

phép này cho phép bạn gọi chức năng với các đối số con trỏ trực tiếp, đó là dễ bị lỗi bởi vì bạn không nhất thiết phải biết tại trang web cuộc gọi mà bạn đang tạo một con trỏ được chia sẻ từ nó.

void f(std::shared_ptr<int> arg); 
int a; 
f(&a); // bug 

Thậm chí nếu bạn bỏ qua này, bạn tạo tạm thời vô hình tại địa điểm cuộc gọi, và tạo shared_ptr là khá tốn kém.

+1

Trừ khi bạn không tạo nó bằng 'mới' bởi vì sau đó bạn là' xóa' bộ nhớ bạn chưa có 'mới'. – milleniumbug

+0

@ giò, Nó có thể gây ra sự mơ hồ quá tải, và làm cho một 'shared_ptr' là đắt hơn hầu hết mọi người muốn nếu nó được ngầm định. Đối với các lớp khác, nó cũng có thể là một sự chuyển đổi không rõ ràng. Ví dụ, truyền '5' và biến nó thành' std :: vector' sẽ rất không trực quan. – chris

28

Cú pháp:

shared_ptr<Type> var = new Type(); 

copy initialization. Đây là kiểu khởi tạo được sử dụng cho các đối số hàm.

Nếu được phép, bạn có thể vô tình truyền con trỏ đơn giản đến một hàm lấy con trỏ thông minh. Hơn nữa, nếu trong quá trình bảo trì, ai đó đã thay đổi void foo(P*) thành void foo(std::shared_ptr<P>) sẽ biên dịch tốt, dẫn đến hành vi không xác định.

Vì thao tác này chủ yếu là lấy quyền sở hữu một con trỏ đơn giản, thao tác này phải được thực hiện một cách rõ ràng. Đây là lý do tại sao hàm tạo shared_ptr lấy một con trỏ đơn giản được thực hiện explicit - để tránh các chuyển đổi ngầm ngẫu nhiên.


Giải pháp thay thế an toàn hơn và hiệu quả hơn là:

auto var = std::make_shared<Type>(); 
+4

@ giò Đó thực sự là một vấn đề lớn. –

+1

@ giò Một con trỏ chia sẻ giả sử nó sở hữu con trỏ của nó (và chỉ chia sẻ nó với các con trỏ được chia sẻ khác).Nếu bạn đã chuyển một con trỏ thô tới một con trỏ được chia sẻ, rất có thể là một đoạn mã khác giả định nó sở hữu con trỏ thô và cố gắng xóa nó tại một số điểm. Nhưng con trỏ chia sẻ cũng vậy! Vì vậy, yeah, xóa đôi là không tốt cả. – KABoissonneault

8

Tôi tự hỏi tại sao họ không cho phép một đơn giản hơn nhiều và tốt hơn ...

Ý kiến ​​của bạn sẽ thay đổi khi bạn trở nên có kinh nghiệm hơn và gặp phải nặng hơn bằng văn bản, mã lỗi.

shared_ptr<>, giống như tất cả các đối tượng thư viện chuẩn được viết theo cách khó làm cho hành vi không xác định (tức là khó tìm lỗi làm lãng phí thời gian của mọi người và phá hủy ý chí của chúng tôi).

xem xét:

#include<memory> 

struct Foo {}; 

void do_something(std::shared_ptr<Foo> pfoo) 
{ 
    // ... some things 
} 

int main() 
{ 
    auto p = std::make_shared<Foo>(/* args */); 
    do_something(p.get()); 
    p.reset(); // BOOM! 
} 

Mã này không thể biên dịch, và đó là một điều tốt. Bởi vì nếu nó đã làm, chương trình sẽ thể hiện hành vi không xác định.

Điều này là do chúng tôi sẽ xóa cùng một Foo hai lần.

Chương trình này sẽ biên dịch và được tạo đúng.

#include<memory> 

struct Foo {}; 

void do_something(std::shared_ptr<Foo> pfoo) 
{ 
    // ... some things 
} 

int main() 
{ 
    auto p = std::make_shared<Foo>(/* args */); 
    do_something(p); 
    p.reset(); // OK 
} 
Các vấn đề liên quan