STARTING
CONTROL STATEMENT
FUNCTION
ARRAY & POINTER
OOP
STL
ITERATORS
OTHER FEATURES
CÁC CHỦ ĐỀ
BÀI MỚI NHẤT
MỚI CẬP NHẬT

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.

test php

banquyen png
Bài viết này được đăng tại freetuts.net, không được copy dưới mọi hình thức.

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_ptr sẽ quản lý. Sử dụng từ khóa std::shared_ptr cù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;
}

t E1 BA A3i 20xu E1 BB 91ng 20 4  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àm std::make_shared hoặc sử dụng toán tử new để cấp phát bộ nhớ động và gán cho shared_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_ptr sử dụng đếm tham chiếu để theo dõi số lượng các shared_ptr đang sở hữu một đối tượng. Khi không còn shared_ptr nà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_ptr từ 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_ptr và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_ptr tự độ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_ptr cho 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_ptr có 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ượng shared_ptr đang trỏ đến một vùng nhớ.

So sánh với unique_ptr

  • Sở hữu độc quyền: Unique_ptr sở 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_ptr có chi phí overhead thấp hơn so với shared_ptr vì không cần bộ đếm tham chiếu.
  • Quản lý tài nguyên: Unique_ptr phù 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 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_ptr tự độ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_ptr trỏ đế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_ptr sử 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_ptr cần một bộ đếm tham chiếu (reference counter) để theo dõi số lượng shared_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_ptr thí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ùng chuyên mục:

Các hàm xử lý mảng đa chiều (array.h) trong C/C++

Các hàm xử lý mảng đa chiều (array.h) trong C/C++

Các hàm xử lý ngày tháng (datetime.h) trong C/C++

Các hàm xử lý ngày tháng (datetime.h) trong C/C++

Các hàm xử lý số thực (float.h) trong C/C++

Các hàm xử lý số thực (float.h) trong C/C++

Các hàm xử lý số nguyên lớn (bigint.h) trong C/C++

Các hàm xử lý số nguyên lớn (bigint.h) trong C/C++

Các hàm xử lý thời gian (time.h) trong C

Các hàm xử lý thời gian (time.h) trong C

Các hàm xử lý chuỗi (string.h) trong C/C++

Các hàm xử lý chuỗi (string.h) trong C/C++

Thread Pools và Parallel Algorithms trong C++

Thread Pools và Parallel Algorithms trong C++

Tạo và quản lý các Multithreading trong C++

Tạo và quản lý các Multithreading trong C++

Xử lý ngoại lệ khi làm việc với Memory Allocation trong C++

Xử lý ngoại lệ khi làm việc với Memory Allocation trong C++

Try, Catch, và Throw của Exception Handling trong C++

Try, Catch, và Throw của Exception Handling trong C++

Cách sử dụng Lambda Expressions trong C++

Cách sử dụng Lambda Expressions trong C++

Sử dụng weak_ptr trong C++

Sử dụng weak_ptr trong C++

Sử dụng unique_ptr trong C++

Sử dụng unique_ptr trong C++

Tổng quan về Smart Pointers trong C++

Tổng quan về Smart Pointers trong C++

Sử dụng Iterators trong STL của C++

Sử dụng Iterators trong STL của C++

[Iterator] Sử dụng Vector trong C++

[Iterator] Sử dụng Vector trong C++

[Iterator] Sử dụng trong List trong C++

[Iterator] Sử dụng trong List trong C++

[STL] Sử dụng Vector trong C++

[STL] Sử dụng Vector trong C++

Tổng quan về Iterators trong C++

Tổng quan về Iterators trong C++

[STL] Các hàm thường dùng của lớp Vector trong C++

[STL] Các hàm thường dùng của lớp Vector trong C++

Top