[Smart Pointers] std::shared_ptr trong C++

std::shared_ptr là gì ?

std::shared_ptr là một trong số các Smart Pointers của C++, được support từ C++11, định nghĩa trong file header <memory>. std::shared_ptr là một class template (khuôn mẫu lớp) dùng để quản lý lifetime của dynamic object (đối tượng được cấp phát động trong runtime) chứa trong nó. Một dynamic object thì có thể được chia sẻ sở hữu giữa các std::shared_ptr (điều này trái ngược với std::unique_ptr).

Cơ chế chia sẻ dynamic object của shared_ptr được thực hiện thông qua một kỹ thuật được gọi là bộ đếm tham chiếu (reference counter), trong đó số lượng các shared_ptr đang sở hữu đối tượng sẽ được lưu trữ vào một vùng nhớ chung được chia sẻ giữa các shared_ptr. Khi có thêm một shared_ptr sở hữu đối tượng thì bộ đếm này được tăng thêm 1, khi một shared_ptr đang sở hữu đối tượng này bị huỷ hoặc được gán bằng đối tượng khác thì bộ đếm giảm 1. Khi bộ đếm này về đến 0 (tức là khi shared_ptr cuối cùng sở hữu đối tượng này bị huỷ hoặc được gán bằng đối tượng khác), đối tượng sẽ tự động bị hủy.

 

Sử dụng std::shared_ptr như thế nào

*** Giả sử ta có class Test. Để tạo ra shared pointer đầu tiên sở hữu một dynamic object của class Test ta làm như sau →

*** Để tạo các shared pointers khác chia sẻ object đã tạo ở trên thì cần tạo thêm std::shared_ptr và alias đến firstShared bằng một trong 2 cách sau

secondShared sẽ là một shared pointer chia sẻ sở hữu dynamic object với firstShared.

*** Bạn có thể truy cập và sử dụng std::shared_ptr giống như con trỏ thông thường vì nó đã overload các toán tử * và ->. Và khi shared pointer cuối cùng sở hữu dynamic object đi ra khỏi scope của nó thì destructor của Test sẽ được call.

Các bạn hãy xem full chương trình ví dụ sau 

Biên dịch và chạy chương trình này (nhớ là dùng trình biên dịch support C++11 trở lên) sẽ cho ra output như sau 

Từ kết quả chúng ta có thể kiểm chứng lại lý thuyết: firstShared và secondShared chia sẻ chung 1 object của class Test (call hàm setX() thông qua firstShared, và call hàm getX() để lấy ra giá trị thông qua secondShared)Khi ra khỏi scope của firstShared (lúc đó cả 2 shared pointers là firstShared, và secondShared đều bị xoá) thì lúc đó dynamic object mà chúng đồng sở hữu sẽ tự động được giải phóng, lúc đó hàm huỷ của class Test được call.

Chú ý: Không giống như std::make_unique – có từ C++14, std::make_shared có từ C++11. Tất nhiên chúng ta có thể dùng std::shared_ptr với toán tử new nhưng std::make_shared vẫn được khuyên dùng vì xử lý của std::make_shared tối ưu bộ nhớ tốt hơn.

*** Sử dụng std::shared_ptr với array

****** Với Version ≥ C++11 và < C++17 thì không dùng thể std::make_shared để cấp phát array được mà phải dùng toán tử new kết hợp với std::default_delete. Ví dụ, để cấp phát động 1 array 10 phần tử kiểu int và gán quyền sở hữu nó cho một shared_ptr ta làm như sau 

****** Với Version ≥ C++17 thì không bắt buộc phải chỉ định array-deleter một cách tường minh và để truy cập array thông qua shared_ptr thì không cần phải dùng hàm get() như ở trên nữa mà truy cập như thể shared_ptr là một array thực thụ 

Xem thêm

— Phạm Minh Tuấn (Shun) —