Giao diện
🎯 unique_ptr — Exclusive Ownership
std::unique_ptr là lựa chọn mặc định cho smart pointers. Một object chỉ có một chủ sở hữu duy nhất. Analogy: Chìa khóa duy nhất
┌─────────────────────────────────────────────────────────────────┐
│ UNIQUE KEY ANALOGY │
├─────────────────────────────────────────────────────────────────┤
│ │
│ unique_ptr = Chìa khóa duy nhất của căn phòng │
│ │
│ • Chỉ CÓ 1 người giữ chìa → Chỉ 1 owner │
│ • Muốn đưa cho người khác → CHUYỂN chìa (move) │
│ • Không thể copy chìa → Không thể copy unique_ptr │
│ • Người giữ chìa đi → Phòng tự động khóa (destructor) │
│ │
│ ┌─────────┐ move ┌─────────┐ │
│ │ Owner A │ ──────────► │ Owner B │ │
│ │ 🔑 │ │ 🔑 │ │
│ │ │ (A no │ │ │
│ │ [empty]│ longer │ [owns] │ │
│ └─────────┘ owns) └─────────┘ │
│ │
└─────────────────────────────────────────────────────────────────┘Basic Usage
cpp
#include <memory>
// ✅ Create with make_unique (C++14)
auto ptr = std::make_unique<int>(42);
// ✅ Access value
std::cout << *ptr << "\n"; // 42
// ✅ Auto-deleted when ptr goes out of scopeCreating unique_ptr
cpp
#include <memory>
// Method 1: make_unique (PREFERRED)
auto p1 = std::make_unique<int>(42);
auto p2 = std::make_unique<std::string>("Hello");
auto p3 = std::make_unique<int[]>(10); // Array of 10 ints
// Method 2: Constructor (less safe)
std::unique_ptr<int> p4(new int(42));
// ❌ NEVER do this (not exception-safe)
func(std::unique_ptr<A>(new A()), std::unique_ptr<B>(new B()));
// If new B() throws, new A() leaks!
// ✅ Safe version
func(std::make_unique<A>(), std::make_unique<B>());Ownership Transfer (Move)
cpp
#include <memory>
auto ptr1 = std::make_unique<int>(42);
// ❌ Copy not allowed
// auto ptr2 = ptr1; // Compile error!
// ✅ Move ownership
auto ptr2 = std::move(ptr1);
// ptr1 is now nullptr!
if (!ptr1) {
std::cout << "ptr1 is empty\n";
}
// ptr2 owns the resource
std::cout << *ptr2 << "\n"; // 42Accessing the Object
cpp
auto ptr = std::make_unique<std::string>("Hello");
// Dereference
std::cout << *ptr << "\n"; // Hello
// Access member
std::cout << ptr->size() << "\n"; // 5
// Get raw pointer (non-owning)
std::string* raw = ptr.get();
// Check if valid
if (ptr) {
std::cout << "ptr is valid\n";
}Common Operations
cpp
auto ptr = std::make_unique<int>(42);
// Release ownership (returns raw pointer)
int* raw = ptr.release();
// ptr is now nullptr, YOU own raw and must delete it!
delete raw;
// Reset to new value
ptr.reset(new int(100)); // Old value deleted, now points to 100
// Reset to nullptr
ptr.reset(); // Deletes and becomes nullptr
// Swap
auto ptr2 = std::make_unique<int>(200);
ptr.swap(ptr2);unique_ptr with Classes
cpp
class Widget {
int id_;
public:
explicit Widget(int id) : id_(id) {
std::cout << "Widget " << id_ << " created\n";
}
~Widget() {
std::cout << "Widget " << id_ << " destroyed\n";
}
void doWork() { std::cout << "Working...\n"; }
};
int main() {
auto widget = std::make_unique<Widget>(1);
widget->doWork();
// Widget 1 destroyed when main() exits
}Output:
Widget 1 created
Working...
Widget 1 destroyedPassing unique_ptr
cpp
// Transfer ownership INTO function
void takeOwnership(std::unique_ptr<Widget> w) {
w->doWork();
} // w destroyed here
// Return ownership FROM function
std::unique_ptr<Widget> createWidget(int id) {
return std::make_unique<Widget>(id);
}
// Pass by reference (non-owning access)
void useWidget(const std::unique_ptr<Widget>& w) {
w->doWork(); // Don't take ownership
}
// Better: Just pass raw pointer for non-owning
void useWidgetBetter(Widget* w) {
if (w) w->doWork();
}
int main() {
auto w = createWidget(1);
useWidget(w);
useWidgetBetter(w.get());
takeOwnership(std::move(w)); // Must move!
// w is now nullptr
}unique_ptr with Arrays
cpp
// Array version
auto arr = std::make_unique<int[]>(100);
arr[0] = 1;
arr[99] = 100;
// ✅ Auto-calls delete[] (not delete)
// For dynamic arrays, prefer std::vector though!
std::vector<int> vec(100); // Usually betterCustom Deleter
cpp
// For resources that need special cleanup
struct FileDeleter {
void operator()(FILE* file) const {
if (file) fclose(file);
}
};
std::unique_ptr<FILE, FileDeleter> openFile(const char* path) {
return std::unique_ptr<FILE, FileDeleter>(
fopen(path, "r")
);
}
// Lambda deleter
auto file = std::unique_ptr<FILE, decltype(&fclose)>(
fopen("data.txt", "r"),
&fclose
);unique_ptr vs Raw Pointer
| Aspect | Raw Pointer | unique_ptr |
|---|---|---|
| Ownership | Unclear | Clear (exclusive) |
| Cleanup | Manual delete | Automatic |
| Copy | Danger! | Compile error |
| Move | Manual | Built-in |
| Overhead | None | None (zero-cost)! |
📚 Tổng kết
| Feature | Description |
|---|---|
| Ownership | Exclusive (single owner) |
| Copy | ❌ Disabled |
| Move | ✅ Supported |
| Overhead | Zero (same as raw pointer) |
| Default choice | ✅ Yes! |
➡️ Tiếp theo
Khi cần nhiều owners cùng sở hữu 1 object:
shared_ptr → — Shared ownership.