xem xét chương trình này:Nhiều shared_ptr lưu trữ cùng một con trỏ
#include <memory>
#include <iostream>
class X
: public std::enable_shared_from_this<X>
{
public:
struct Cleanup1 { void operator()(X*) const; };
struct Cleanup2 { void operator()(X*) const; };
std::shared_ptr<X> lock1();
std::shared_ptr<X> lock2();
};
std::shared_ptr<X> X::lock1()
{
std::cout << "Resource 1 locked" << std::endl;
return std::shared_ptr<X>(this, Cleanup1());
}
std::shared_ptr<X> X::lock2()
{
std::cout << "Resource 2 locked" << std::endl;
return std::shared_ptr<X>(this, Cleanup2());
}
void X::Cleanup1::operator()(X*) const
{
std::cout << "Resource 1 unlocked" << std::endl;
}
void X::Cleanup2::operator()(X*) const
{
std::cout << "Resource 2 unlocked" << std::endl;
}
int main()
{
std::cout << std::boolalpha;
X x;
std::shared_ptr<X> p1 = x.lock1();
{
std::shared_ptr<X> p2 = x.lock2();
}
}
tôi không thấy bất cứ điều gì trong C++ 11 tiêu chuẩn phần 20.7.2 cho thấy bất kỳ của việc này là không hợp lệ. Có một chút khác thường khi có hai đối tượng shared_ptr
lưu trữ cùng một con trỏ &x
nhưng không chia sẻ quyền sở hữu và sử dụng "dấu phân tách" không kết thúc tuổi thọ của *get()
, nhưng không có gì cấm. (Và nếu một trong những người hoàn toàn ngoài ý muốn, nó sẽ rất khó để giải thích tại sao một số shared_ptr
chức năng thành viên chấp nhận một giá trị std::nullptr_t
.) Và như mong đợi, các kết quả đầu ra chương trình:
Resource 1 locked
Resource 2 locked
Resource 2 unlocked
Resource 1 unlocked
Nhưng bây giờ nếu tôi thêm một chút để main()
:
int main()
{
std::cout << std::boolalpha;
X x;
std::shared_ptr<X> p1 = x.lock1();
bool test1(x.shared_from_this());
std::cout << "x.shared_from_this() not empty: " << test1 << std::endl;
{
std::shared_ptr<X> p2 = x.lock2();
}
try {
bool test2(x.shared_from_this());
std::cout << "x.shared_from_this() not empty: " << test2 << std::endl;
} catch (std::exception& e) {
std::cout << "caught: " << e.what() << std::endl;
}
}
thì mọi thứ trở nên phức tạp hơn. Với g ++ 4.6.3, tôi nhận được kết quả:
Resource 1 locked
x.shared_from_this() not empty: true
Resource 2 locked
Resource 2 unlocked
caught: std::bad_weak_ptr
Resource 1 unlocked
Tại sao cuộc gọi thứ hai để shared_from_this()
sẽ thất bại? Tất cả các yêu cầu của 20.7.2.4p7 được đáp ứng:
Yêu cầu:
enable_shared_from_this<T>
sẽ là một lớp cơ sở tiếp cận củaT
.*this
phải là một đối tượng con của một đối tượngt
loạiT
. Phải có ít nhất một ví dụshared_ptr
p
rằng sở hữu&t
.
[T
là X
, t
là x
, p
là p1
.]
Nhưng g ++ 's enable_shared_from_this
cơ bản sau việc thực hiện đề xuất từ (không quy chuẩn) "Ghi chú" trong 20.7.2.4p10, sử dụng một tư nhân weak_ptr
thành viên trong lớp enable_shared_from_this
. Và dường như không thể giải thích cho loại vấn đề này mà không làm điều gì đó phức tạp hơn đáng kể trong enable_shared_from_this
.
Đây có phải là lỗi trong tiêu chuẩn không? (Nếu vậy, không cần bình luận ở đây về giải pháp "nên" là gì: thêm yêu cầu để chương trình ví dụ gọi hành vi không xác định, thay đổi Ghi chú thành không thực hiện việc thực hiện đơn giản như vậy là đủ, ....)
Từ quan điểm tiêu chuẩn, tôi không chắc chắn. Lý do WHY là triển khai g ++ (và boost) mong rằng lần đầu tiên bạn tạo một con trỏ được chia sẻ từ một cá thể con trỏ thô của X sẽ là thời gian duy nhất, và biến private_ptr riêng được đặt để trỏ đến cá thể đã tạo đó. Khi bạn tạo một con trỏ chia sẻ mới thứ hai trên cùng một cá thể trong 'lock2()', nó sẽ ghi đè lên weak_ptr ban đầu, và khi nó mở khóa, con trỏ yếu trỏ vào không có gì, do đó lỗi. –
Ghi chú không quy chuẩn thể hiện một ví dụ thực hiện 'enable_from_this' kết luận (đoạn 11) với" Các constructor shared_ptr tạo ** con trỏ độc đáo ** có thể phát hiện sự hiện diện của một cơ sở 'enable_shared_from_this' và gán mới được tạo ra' shared_ptr' vào thành viên '__weak_this' của nó." [nhấn mạnh mỏ] Tôi thấy nó đáng chú ý rằng lưu ý này đã không được worded với một cái gì đó để tác dụng của "các nhà xây dựng tạo ra * sở hữu * con trỏ", và tôi tự hỏi những gì là một duy nhất 'shared_ptr'. –
@Luc Tôi tin rằng đó là đề cập đến các nhà xây dựng sau khi tạo ra sẽ trở lại đúng từ 'duy nhất()' của họ. Về cơ bản, các nhà xây dựng có quyền sở hữu ban đầu từ một con trỏ thô hoặc một unique_ptr. –