Sử dụng shared_ptr trong C++
Quản lý bộ nhớ là một phần quan trọng và thách thức đối với các nhà phát triển phần mềm. Việc sử dụng raw pointers có thể dễ dàng dẫn đến các lỗi như memory leaks và dangling pointers, gây ra các vấn đề về hiệu suất và ổn định của chương trình. Để giải quyết vấn đề này, C++11 đã giới thiệu một số loại smart pointers, trong đó có shared_ptr.

Trong bài viết này, mình sẽ tìm hiểu về shared_ptr trong C++ và cách sử dụng nó để quản lý bộ nhớ một cách an toàn và hiệu quả. Mình sẽ đi sâu vào từng khía cạnh của shared_ptr, bao gồm cách khai báo, sử dụng, ưu và nhược điểm, cũng như so sánh với các loại smart pointers khác và raw pointers. Cuối cùng, mình sẽ xem xét một số lưu ý quan trọng khi sử dụng shared_ptr trong thực tế.
shared_ptr trong C++ là gì?
shared_ptr là một smart pointer được sử dụng để quản lý bộ nhớ động một cách tự động và an toàn. Đặc điểm chính của shared_ptr là nó sử dụng kỹ thuật đếm tham chiếu (reference counting) để theo dõi số lượng các shared_ptr đang sở hữu một đối tượng, và tự động giải phóng bộ nhớ khi không còn shared_ptr nào sở hữu đối tượng đó.
Khi một shared_ptr được khởi tạo để quản lý một đối tượng, nó sẽ tạo ra một đếm tham chiếu với giá trị ban đầu là 1. Mỗi khi một shared_ptr mới được tạo từ shared_ptr hiện có hoặc được gán bằng một shared_ptr khác, đếm tham chiếu sẽ tăng lên. Khi một shared_ptr bị hủy hoặc không còn được sử dụng nữa, đếm tham chiếu sẽ giảm đi. Khi đếm tham chiếu đạt đến 0, tức là không có shared_ptr nào sở hữu đối tượng nữa, bộ nhớ được giải phóng tự động.
Bài viết này được đăng tại [free tuts .net]
Do đó, shared_ptr cung cấp một cơ chế tự động và an toàn để quản lý bộ nhớ động, giúp tránh các vấn đề như memory leaks và dangling pointers.
Khai báo và sử dụng shared_ptr trong C++
Khai báo shared_ptr
- Để khai báo một
shared_ptr, cần định nghĩa kiểu dữ liệu màshared_ptrsẽ quản lý. Sử dụng từ khóastd::shared_ptrcùng với kiểu dữ liệu của đối tượng cụ thể.
#include <memory>
int main() {
// Khai báo shared_ptr quản lý một đối tượng kiểu int
std::shared_ptr<int> ptr;
//Bài viết được đăng tại freetuts.net
return 0;
}
.png)
Cấp phát bộ nhớ động cho shared_ptr
- Để cấp phát bộ nhớ động cho một
shared_ptr, mình có thể sử dụng hàmstd::make_sharedhoặc sử dụng toán tử new để cấp phát bộ nhớ động và gán choshared_ptr.
#include <memory>
int main() {
// Cấp phát bộ nhớ động cho shared_ptr sử dụng make_shared
std::shared_ptr<int> ptr1 = std::make_shared<int>(5);
// Cấp phát bộ nhớ động cho shared_ptr sử dụng new
std::shared_ptr<int> ptr2(new int(10));
//Bài viết được đăng tại freetuts.net
return 0;
}
Sử dụng shared_ptr để quản lý bộ nhớ
shared_ptrsử dụng đếm tham chiếu để theo dõi số lượng cácshared_ptrđang sở hữu một đối tượng. Khi không cònshared_ptrnào sở hữu đối tượng nữa, bộ nhớ được giải phóng tự động.
#include <iostream>
#include <memory>
int main() {
// Tạo shared_ptr quản lý một đối tượng int
std::shared_ptr<int> ptr(new int(5));
//Bài viết được đăng tại freetuts.net
// In giá trị của con trỏ và giá trị mà nó trỏ tới
std::cout << "Giá trị của con trỏ: " << ptr.get() << std::endl;
std::cout << "Giá trị của đối tượng: " << *ptr << std::endl;
return 0;
}
Output:
Giá trị của con trỏ: 0x56431e66fc40 Giá trị của đối tượng: 5
Trong ví dụ trên, shared_ptr được sử dụng để quản lý một đối tượng kiểu int. Sau khi không còn shared_ptr nào sở hữu đối tượng, bộ nhớ được giải phóng tự động.
Chuyển shared_ptr trong C++
Chuyển shared_ptr từ một đối tượng sang đối tượng khác
- Để chuyển sở hữu của một
shared_ptrtừ một đối tượng sang đối tượng khác, mình có thể sử dụng toán tử gán (=) hoặc hàm reset().
#include <memory>
#include <iostream>
int main() {
// Tạo shared_ptr quản lý một đối tượng int
std::shared_ptr<int> ptr1(new int(5));
//Bài viết được đăng tại freetuts.net
// Chuyển shared_ptr từ ptr1 sang ptr2
std::shared_ptr<int> ptr2 = ptr1;
// ptr1 không còn quản lý đối tượng nữa
std::cout << "ptr1: " << ptr1.get() << std::endl; // nullptr
// ptr2 quản lý đối tượng
std::cout << "ptr2: " << ptr2.get() << std::endl; // Địa chỉ của đối tượng
return 0;
}
Output:
ptr1: 0x55b3aacd9c40 ptr2: 0x55b3aacd9c40
Chuyển shared_ptr vào và ra khỏi hàm
- Mình có thể truyền
shared_ptrvào và ra khỏi hàm một cách tương tự như các tham số thông thường.
#include <memory>
#include <iostream>
void processSharedPtr(std::shared_ptr<int> ptr) {
// Xử lý shared_ptr ở đây
}
//Bài viết được đăng tại freetuts.net
int main() {
// Tạo shared_ptr quản lý một đối tượng int
std::shared_ptr<int> ptr(new int(5));
// Truyền shared_ptr vào hàm
processSharedPtr(ptr);
return 0;
}Trong ví dụ trên, truyền shared_ptr vào hàm processSharedPtr() để xử lý. Điều này cho phép chia sẻ sở hữu của đối tượng và tự động giải phóng bộ nhớ khi không còn cần thiết.
Ví dụ shared_ptr trong C++
Ví dụ về cấp phát bộ nhớ động và giải phóng bộ nhớ bằng shared_ptr
#include <memory>
#include <iostream>
int main() {
// Cấp phát bộ nhớ động cho shared_ptr
std::shared_ptr<int> ptr(new int(10));
//Bài viết được đăng tại freetuts.net
// Sử dụng shared_ptr
std::cout << "Giá trị của con trỏ: " << *ptr << std::endl;
// Khi kết thúc phạm vi của ptr, bộ nhớ sẽ được tự động giải phóng
return 0;
}
Output:
Giá trị của con trỏ: 10
Ví dụ về chuyển shared_ptr giữa các đối tượng
#include <memory>
#include <iostream>
int main() {
// Tạo shared_ptr quản lý một đối tượng int
std::shared_ptr<int> ptr1(new int(5));
//Bài viết được đăng tại freetuts.net
// Chuyển shared_ptr từ ptr1 sang ptr2
std::shared_ptr<int> ptr2 = ptr1;
// ptr1 không còn quản lý đối tượng nữa
std::cout << "ptr1: " << ptr1.get() << std::endl; // nullptr
// ptr2 quản lý đối tượng
std::cout << "ptr2: " << ptr2.get() << std::endl; // Địa chỉ của đối tượng
return 0;
}
Output:
ptr1: 0x55bc4cff9c40 ptr2: 0x55bc4cff9c40
Trong ví dụ A, mình cấp phát bộ nhớ động cho shared_ptr và sử dụng nó để lưu trữ một giá trị nguyên. Khi ptr ra khỏi phạm vi, bộ nhớ sẽ tự động được giải phóng.
Trong ví dụ B, mình chuyển sở hữu của một shared_ptr sang một shared_ptr khác bằng cách gán hoặc sao chép. Điều này dẫn đến việc sử dụng chung bộ nhớ giữa các shared_ptr, và bộ nhớ sẽ được tự động giải phóng khi không còn ai sở hữu nữa.
So sánh với raw pointers và unique_ptr trong C++
Sự khác biệt giữa shared_ptr và raw pointers
- Quản lý bộ nhớ:
Shared_ptrtự động giải phóng bộ nhớ khi không còn ai sở hữu nữa, trong khi raw pointers không có cơ chế tự động giải phóng bộ nhớ, cần phải được giải phóng thủ công để tránh memory leaks. - Chia sẻ dữ liệu:
Shared_ptrcho phép nhiều đối tượng cùng trỏ đến một vùng nhớ, trong khi raw pointers không có cơ chế sở hữu chia sẻ, dễ dẫn đến vấn đề dangling pointers khi một đối tượng được giải phóng nhưng raw pointer vẫn trỏ đến vùng nhớ đã bị giải phóng. - Chi phí overhead:
Shared_ptrcó chi phí overhead cao hơn so với raw pointers do cần một bộ đếm tham chiếu để theo dõi số lượngshared_ptrđang trỏ đến một vùng nhớ.
So sánh với unique_ptr
- Sở hữu độc quyền:
Unique_ptrsở hữu độc quyền cho một vùng nhớ, chỉ có thể chuyển sở hữu cho một unique_ptr khác hoặc release nó, trong khi shared_ptr cho phép nhiều shared_ptr trỏ đến cùng một vùng nhớ. - Chi phí overhead:
Unique_ptrcó chi phí overhead thấp hơn so vớishared_ptrvì không cần bộ đếm tham chiếu. - Quản lý tài nguyên:
Unique_ptrphù hợp cho việc quản lý tài nguyên mà chỉ có một đối tượng cần truy cập, trong khi shared_ptr thích hợp cho việc chia sẻ tài nguyên giữa nhiều đối tượng.
Ví dụ:
#include <iostream>
#include <memory>
void rawPointerExample() {
int* rawPtr = new int(5);
delete rawPtr; // Need manual memory management
rawPtr = nullptr; // Avoid dangling pointers
}
//Bài viết được đăng tại freetuts.net
void uniquePtrExample() {
std::unique_ptr<int> uniquePtr(new int(5));
// Ownership transfer
std::unique_ptr<int> newUniquePtr = std::move(uniquePtr);
// Can't use uniquePtr anymore
}
void sharedPtrExample() {
std::shared_ptr<int> sharedPtr1 = std::make_shared<int>(5);
// Ownership shared
std::shared_ptr<int> sharedPtr2 = sharedPtr1;
// Both sharedPtr1 and sharedPtr2 own the memory
}
int main() {
rawPointerExample();
uniquePtrExample();
sharedPtrExample();
return 0;
}Trong ví dụ trên, rawPointerExample sử dụng raw pointers, uniquePtrExample sử dụng unique_ptr và sharedPtrExample sử dụng shared_ptr để quản lý bộ nhớ.
Ưu điểm và nhược điểm của shared_ptr trong C++
Ưu điểm:
- Quản lý tự động bộ nhớ:
Shared_ptrtự động giải phóng bộ nhớ khi không còn ai sở hữu nữa, giúp tránh memory leaks. - Sử dụng chung dữ liệu: Cho phép nhiều
shared_ptrtrỏ đến cùng một vùng nhớ, giúp chia sẻ dữ liệu một cách an toàn. - Thực hiện cơ chế sở hữu chia sẻ:
Shared_ptrsử dụng kỹ thuật sở hữu chia sẻ để quản lý bộ nhớ, giúp tối ưu hóa việc sử dụng tài nguyên.
Nhược điểm:
- Chi phí overhead:
Shared_ptrcần một bộ đếm tham chiếu (reference counter) để theo dõi số lượngshared_ptrđang trỏ đến một vùng nhớ, điều này có thể gây ra chi phí overhead. - Khả năng xảy ra cyclic references: Nếu có các chu trình tham chiếu giữa các đối tượng, có thể dẫn đến vấn đề memory leaks.
- Không phù hợp cho mọi tình huống:
Shared_ptrthích hợp cho việc chia sẻ tài nguyên, nhưng không phải mọi tình huống đều cần thiết sử dụng nó.
Lưu ý khi sử dụng shared_ptr
Quản lý vấn đề chi phí overhead: Tránh tạo ra quá nhiều shared_ptr và sao chép chúng một cách không cần thiết để giảm bớt chi phí overhead.
Xử lý vấn đề cyclic references: Sử dụng weak_ptr để giải quyết vấn đề cyclic references, tránh tạo ra chu trình tham chiếu giữa các đối tượng.
Sử dụng shared_ptr khi cần chia sẻ tài nguyên: Shared_ptr là lựa chọn tốt khi bạn cần chia sẻ một vùng nhớ giữa nhiều đối tượng mà không cần phải lo lắng về việc giải phóng bộ nhớ.
Kết bài
Trên đây là dàn bài chi tiết cho chủ đề "Sử dụng shared_ptr trong C++", hy vọng nó sẽ giúp bạn hiểu rõ hơn về cách sử dụng shared_ptr và các khía cạnh liên quan. Bằng cách sử dụng shared_ptr một cách hiệu quả, bạn có thể quản lý bộ nhớ động trong các ứng dụng C++ của mình một cách an toàn và linh hoạt.

Các kiểu dữ liệu trong C ( int - float - double - char ...)
Thuật toán tìm ước chung lớn nhất trong C/C++
Cấu trúc lệnh switch case trong C++ (có bài tập thực hành)
ComboBox - ListBox trong lập trình C# winforms
Random trong Python: Tạo số random ngẫu nhiên
Lệnh cin và cout trong C++
Cách khai báo biến trong PHP, các loại biến thường gặp
Download và cài đặt Vertrigo Server
Thẻ li trong HTML
Thẻ article trong HTML5
Cấu trúc HTML5: Cách tạo template HTML5 đầu tiên
Cách dùng thẻ img trong HTML và các thuộc tính của img
Thẻ a trong HTML và các thuộc tính của thẻ a thường dùng