Sử dụng unique_ptr trong C++
Trong quá trình phát triển phần mềm, việc không kiểm soát được bộ nhớ động có thể dẫn đến những vấn đề như memory leaks, dangling pointers, hoặc sự phân mảnh bộ nhớ.

Để giải quyết những vấn đề này, ngôn ngữ C++ cung cấp một loạt các smart pointers, trong đó unique_ptr là một trong những công cụ quan trọng và hiệu quả nhất. Trong phần này, mình sẽ đi vào chi tiết về unique_ptr, đồng thời tìm hiểu về ý nghĩa, vai trò, cũng như cách sử dụng và lợi ích của việc sử dụng unique_ptr trong lập trình C++. Hãy cùng tìm hiểu!
unique_ptr trong C++ là gì?

unique_ptr là một loại smart pointer được sử dụng để quản lý độc quyền (unique ownership) của một đối tượng được cấp phát động trên heap. Unique ownership đồng nghĩa với việc chỉ có một unique_ptr duy nhất có thể quản lý một vùng nhớ động trong thời điểm cụ thể. Ý nghĩa của unique_ptr là đảm bảo rằng chỉ có một con trỏ duy nhất trỏ đến một vùng nhớ động, và khi unique_ptr bị hủy hoặc chuyển sở hữu, vùng nhớ động được giải phóng một cách tự động và an toàn.
Một số điểm quan trọng về unique_ptr:
Bài viết này được đăng tại [free tuts .net]
-
Quản lý bộ nhớ:
unique_ptrtự động giải phóng bộ nhớ động của đối tượng mà nó quản lý khi không cần thiết nữa. -
Tránh memory leaks: Do
unique_ptrchỉ có một con trỏ duy nhất, không có rủi ro xảy ra memory leaks do mất mát quản lý bộ nhớ. -
Tránh dangling pointers:
unique_ptrđảm bảo rằng con trỏ sẽ không trỏ đến một vùng nhớ không hợp lệ sau khi vùng nhớ đã được giải phóng. -
Chuyển quyền sở hữu:
unique_ptrcho phép chuyển quyền sở hữu từ mộtunique_ptrsang mộtunique_ptrkhác hoặc sang một smart pointer khác nhưshared_ptrhoặcweak_ptr. -
Tăng tính hiệu quả và an toàn: Sử dụng
unique_ptrgiúp tăng tính hiệu quả và an toàn trong việc quản lý bộ nhớ động, giúp tránh được nhiều lỗi phổ biến liên quan đến quản lý bộ nhớ.
Tóm lại, unique_ptr là một công cụ mạnh mẽ trong C++ để quản lý bộ nhớ động một cách an toàn và hiệu quả.
Khai báo và sử dụng unique_ptr trong C++
Khai báo unique_ptr
- Để khai báo một
unique_ptr, ta sử dụng cú pháp sau:
std::unique_ptr<T> ptr;

- Trong đó, T là kiểu dữ liệu của đối tượng mà
unique_ptrsẽ quản lý.
Ví dụ:
std::unique_ptr<int> ptr1; std::unique_ptr<std::string> ptr2;
Cấp phát bộ nhớ động
- Để cấp phát bộ nhớ động cho một đối tượng, ta sử dụng hàm
std::make_unique()hoặc truyền con trỏ đã được cấp phát vàounique_ptr.
Ví dụ:
// Sử dụng make_unique std::unique_ptr<int> ptr1 = std::make_unique<int>(10); //Bài viết được đăng tại freetuts.net // Sử dụng truyền con trỏ int* rawPtr = new int(20); std::unique_ptr<int> ptr2(rawPtr);
Sử dụng unique_ptr để quản lý bộ nhớ
unique_ptrtự động giải phóng bộ nhớ khi ra khỏi phạm vi hoặc khi không còn cần thiết.- Chúng ta có thể truy cập vào đối tượng được quản lý bởi
unique_ptrbằng toán tử * hoặc ->.
Ví dụ:
std::unique_ptr<int> ptr(new int(30)); std::cout << "Value of ptr: " << *ptr << std::endl; // Truy cập giá trị ptr.reset(); // Giải phóng bộ nhớ
Đây là ví dụ về cách khai báo, cấp phát và sử dụng unique_ptr trong C++.
Chuyển unique_ptr trong C++.
Chuyển unique_ptr từ một đối tượng sang đối tượng khác
- Để chuyển
unique_ptrtừ một đối tượng sang một đối tượng khác, ta có thể sử dụng hàmstd::move()để chuyển quyền sở hữu của bộ nhớ.
Ví dụ:
std::unique_ptr<int> ptr1 = std::make_unique<int>(10); std::unique_ptr<int> ptr2 = std::move(ptr1); // Chuyển ptr1 sang ptr2
Chuyển unique_ptr vào và ra khỏi hàm
- Để chuyển
unique_ptrvào hoặc ra khỏi một hàm, ta có thể sử dụng tham số và giá trị trả về của hàm để truyền và nhậnunique_ptr.
Ví dụ:
std::unique_ptr<int> createUniquePtr() {
return std::make_unique<int>(20);
}
void processUniquePtr(std::unique_ptr<int> ptr) {
std::cout << "Value inside processUniquePtr: " << *ptr << std::endl;
}
//Bài viết được đăng tại freetuts.net
int main() {
std::unique_ptr<int> ptr = createUniquePtr();
processUniquePtr(std::move(ptr)); // Chuyển ptr vào hàm
return 0;
}Ví dụ về sử dụng unique_ptr trong C++
Ví dụ về cấp phát bộ nhớ động và giải phóng bộ nhớ
#include <iostream>
#include <memory>
int main() {
// Cấp phát bộ nhớ động cho một unique_ptr
std::unique_ptr<int> ptr1 = std::make_unique<int>(42);
//Bài viết được đăng tại freetuts.net
// In ra giá trị của con trỏ
std::cout << "Giá trị của ptr1: " << *ptr1 << std::endl;
// Giải phóng bộ nhớ
ptr1.reset();
return 0;
}
Output:
Giá trị của ptr1: 42
Ví dụ về chuyển unique_ptr
#include <iostream>
#include <memory>
void processUniquePtr(std::unique_ptr<int> ptr) {
std::cout << "Giá trị bên trong hàm processUniquePtr: " << *ptr << std::endl;
}
//Bài viết được đăng tại freetuts.net
int main() {
// Cấp phát bộ nhớ động cho một unique_ptr
std::unique_ptr<int> ptr2 = std::make_unique<int>(100);
// Chuyển unique_ptr vào hàm
processUniquePtr(std::move(ptr2));
// Khi thoát khỏi hàm main, ptr2 sẽ không còn quản lý bộ nhớ nữa vì nó đã được chuyển
// Nếu ta cố gắng truy cập ptr2 ở đây, sẽ gây ra lỗi.
// std::cout << "Giá trị của ptr2: " << *ptr2 << std::endl; // Lỗi
return 0;
}
Output:
Giá trị bên trong hàm processUniquePtr: 100
Trong ví dụ này, unique_ptr ptr2 được chuyển vào hàm processUniquePtr bằng cách sử dụng std::move(). Sau khi chuyển, ptr2 không còn quản lý bộ nhớ nữa và ta không thể truy cập giá trị của ptr2 ở ngoài hàm.
So sánh với raw pointers và shared_ptr trong C++
Sự khác biệt giữa unique_ptr và raw pointers
Quản lý bộ nhớ:
Unique_ptrtự động giải phóng bộ nhớ khi nó ra khỏi phạm vi hoặc được reset, giảm nguy cơ memory leak.- Raw pointers yêu cầu phải giải phóng bộ nhớ thủ công, có nguy cơ gây ra memory leak nếu không được quản lý cẩn thận.
Ví dụ về unique_ptr:
#include <iostream>
#include <memory>
int main() {
// Sử dụng unique_ptr để quản lý bộ nhớ động
std::unique_ptr<int> ptr(new int(42));
//Bài viết được đăng tại freetuts.net
// Sử dụng raw pointer
int* rawPtr = new int(10);
// Khai báo raw pointer mới và gán bằng raw pointer đã tồn tại
int* anotherRawPtr = rawPtr;
// Giải phóng bộ nhớ
delete rawPtr; // Sẽ gây ra undefined behavior vì bộ nhớ đã được giải phóng bởi anotherRawPtr
return 0;
}So sánh với shared_ptr
Số lượng owner:
Unique_ptrchỉ có một owner, không thể chia sẻ quyền sở hữu bộ nhớ.Shared_ptrcó thể có nhiều owner, cho phép chia sẻ quyền sở hữu bộ nhớ.
Chi phí overhead:
Unique_ptrkhông có chi phí overhead do việc đếm tham chiếu.Shared_ptrcó chi phí overhead do việc đếm tham chiếu.
Ví dụ về shared_ptr:
#include <iostream>
#include <memory>
int main() {
// Sử dụng shared_ptr để chia sẻ quyền sở hữu bộ nhớ động
std::shared_ptr<int> ptr1 = std::make_shared<int>(42);
std::shared_ptr<int> ptr2 = ptr1; // ptr2 cũng trỏ đến cùng một vùng nhớ
//Bài viết được đăng tại freetuts.net
// Sử dụng unique_ptr
std::unique_ptr<int> uniquePtr = std::make_unique<int>(10);
return 0;
}Trong ví dụ trên, ptr1 và ptr2 là hai shared_ptr cùng trỏ đến một vùng nhớ, trong khi uniquePtr là một unique_ptr chỉ có một owner duy nhất.
Ưu điểm và nhược điểm của unique_ptr trong C++
Ưu điểm
- Tự động giải phóng bộ nhớ:
Unique_ptrtự động giải phóng bộ nhớ khi ra khỏi phạm vi hoặc được reset, giúp tránh memory leak. - Hiệu suất: Không có chi phí overhead do việc đếm tham chiếu như
shared_ptr. - Ownership:
Unique_ptrchỉ có một owner, giúp tránh việc xảy ra raceconditionvàdeadlockkhi làm việc với luồng đa luồng.
Nhược điểm
- Không chia sẻ:
Unique_ptrkhông cho phép chia sẻ quyền sở hữu bộ nhớ, điều này làm hạn chế khả năng sử dụng trong các trường hợp cần chia sẻ tài nguyên.
Lưu ý khi sử dụng unique_ptr
Không sử dụng unique_ptr cho các tài nguyên chia sẻ:
Unique_ptrkhông thích hợp cho các tài nguyên mà nhiều owner cần truy cập đến. Trong trường hợp này, nên sử dụng shared_ptr để chia sẻ quyền sở hữu bộ nhớ.
Sử dụng unique_ptr khi cần quản lý bộ nhớ động
Unique_ptrlà lựa chọn tốt khi cần quản lý bộ nhớ động và chỉ có một owner duy nhất. Việc sử dụng unique_ptr giúp loại bỏ nguy cơ memory leak và cung cấp mã nguồn dễ đọc và dễ bảo trì.
Kết bài
Trên đây là một cái nhìn tổng quan về unique_ptr trong C++, bao gồm định nghĩa, ưu điểm, nhược điểm và các lưu ý khi sử dụng. Unique_ptr là một công cụ mạnh mẽ trong việc quản lý bộ nhớ động và tránh các vấn đề liên quan đến memory leak trong lập trình C++. Tuy nhiên, cũng cần lưu ý sử dụng unique_ptr một cách cẩn thận, đặc biệt là trong các trường hợp cần chia sẻ tài nguyên. Để hiểu rõ hơn và áp dụng hiệu quả, việc thực hành và làm quen với unique_ptr trong các dự án thực tế là rất quan trọng.

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