Smart Pointers trong C++ Modern

Tìm hiểu về unique_ptr, shared_ptr, weak_ptr và cách sử dụng smart pointers để quản lý bộ nhớ an toàn trong C++

Smart Pointers trong C++ Modern

Smart Pointers trong C++ Modern

Smart pointers là một công cụ mạnh mẽ trong C++ để quản lý bộ nhớ động một cách tự động và an toàn. Từ C++11 trở đi, standard library cung cấp ba loại smart pointers chính: unique_ptr, shared_ptr, và weak_ptr.

Tại sao cần Smart Pointers?

Với raw pointers truyền thống, chúng ta phải tự quản lý việc newdelete:

// Cách cũ - dễ gây memory leak
MyClass* obj = new MyClass();
// ... sử dụng obj ...
delete obj;  // Dễ quên!

Vấn đề xảy ra khi:

  • Quên delete → memory leak
  • delete nhiều lần → undefined behavior
  • Exception xảy ra trước khi delete → memory leak

Smart pointers giải quyết các vấn đề này!

unique_ptr - Sole Ownership

unique_ptr đại diện cho exclusive ownership - chỉ một smart pointer được sở hữu object tại một thời điểm.

#include <memory>
#include <iostream>
 
class MyClass {
public:
    MyClass() { std::cout << "Constructor\n"; }
    ~MyClass() { std::cout << "Destructor\n"; }
    void doSomething() { std::cout << "Doing something\n"; }
};
 
void example_unique_ptr() {
    // Tạo unique_ptr
    std::unique_ptr<MyClass> ptr1 = std::make_unique<MyClass>();
    
    // Sử dụng như pointer thông thường
    ptr1->doSomething();
    
    // Di chuyển ownership
    std::unique_ptr<MyClass> ptr2 = std::move(ptr1);
    // ptr1 giờ là nullptr
    
    // Tự động delete khi ra khỏi scope
    // Không cần gọi delete!
}

Đặc điểm của unique_ptr

  • Zero overhead: Không tốn thêm bộ nhớ so với raw pointer
  • Move-only: Không thể copy, chỉ có thể move
  • Automatic cleanup: Tự động delete khi ra khỏi scope

shared_ptr - Shared Ownership

shared_ptr cho phép nhiều owners cùng sở hữu một object. Object chỉ bị delete khi tất cả shared_ptr đều ra khỏi scope.

#include <memory>
#include <vector>
 
void example_shared_ptr() {
    // Tạo shared_ptr
    std::shared_ptr<MyClass> ptr1 = std::make_shared<MyClass>();
    std::cout << "Reference count: " << ptr1.use_count() << "\n";  // 1
    
    {
        // Copy - tăng reference count
        std::shared_ptr<MyClass> ptr2 = ptr1;
        std::cout << "Reference count: " << ptr1.use_count() << "\n";  // 2
        
        std::shared_ptr<MyClass> ptr3 = ptr1;
        std::cout << "Reference count: " << ptr1.use_count() << "\n";  // 3
    }
    // ptr2 và ptr3 ra khỏi scope
    std::cout << "Reference count: " << ptr1.use_count() << "\n";  // 1
    
    // Object sẽ bị delete khi ptr1 ra khỏi scope
}

Use Cases cho shared_ptr

  • Nhiều components cần truy cập cùng một object
  • Container của smart pointers
  • Callback functions giữ reference đến object
  • Graph structures với nhiều connections

weak_ptr - Breaking Circular References

weak_ptr là "weak reference" đến object được quản lý bởi shared_ptr, không làm tăng reference count.

#include <memory>
 
class Node {
public:
    std::shared_ptr<Node> next;
    std::weak_ptr<Node> prev;  // Dùng weak_ptr để tránh circular reference
    int data;
    
    Node(int val) : data(val) {}
    ~Node() { std::cout << "Node " << data << " destroyed\n"; }
};
 
void example_weak_ptr() {
    auto node1 = std::make_shared<Node>(1);
    auto node2 = std::make_shared<Node>(2);
    
    node1->next = node2;
    node2->prev = node1;  // weak_ptr không tạo cycle
    
    // Sử dụng weak_ptr
    if (auto prev = node2->prev.lock()) {  // lock() trả về shared_ptr
        std::cout << "Previous node: " << prev->data << "\n";
    }
    
    // Tự động cleanup, không có memory leak
}

Best Practices

1. Ưu tiên make_unique và make_shared

// Good
auto ptr = std::make_unique<MyClass>();
auto shared = std::make_shared<MyClass>();
 
// Avoid
std::unique_ptr<MyClass> ptr(new MyClass());  // Rủi ro exception

2. Truyền smart pointers đúng cách

// Nếu function không giữ ownership
void process(const MyClass& obj);  // Best - pass by reference
void process(MyClass* obj);        // OK - raw pointer
 
// Nếu function giữ shared ownership
void store(std::shared_ptr<MyClass> ptr);  // Pass by value
 
// Nếu function take ownership
void take(std::unique_ptr<MyClass> ptr);   // Pass by value

3. Sử dụng đúng loại smart pointer

Use CaseSmart Pointer
Exclusive ownershipunique_ptr
Shared ownershipshared_ptr
Temporary referenceweak_ptr
Factory functionsunique_ptr (return value)

Performance Considerations

// unique_ptr: No overhead
sizeof(std::unique_ptr<int>) == sizeof(int*)  // true
 
// shared_ptr: Extra overhead cho reference counting
sizeof(std::shared_ptr<int>) == 2 * sizeof(int*)  // true
  • unique_ptr: Không có overhead
  • shared_ptr: Reference counting có overhead nhỏ
  • weak_ptr: Tương tự shared_ptr

Kết luận

Smart pointers là công cụ essential trong Modern C++. Chúng giúp:

  • Tránh memory leaks
  • Exception-safe
  • Express ownership semantics rõ ràng
  • Không có performance overhead (unique_ptr)

Rule of thumb: Luôn sử dụng smart pointers thay vì raw pointers khi quản lý ownership!

4 min read
C++Smart PointersMemory ManagementModern C++

Bài viết liên quan