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

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

Trong ngôn ngữ lập trình C++, cấp phát và giải phóng bộ nhớ động được thực hiện thông qua các toán tử như new, delete hoặc các hàm như malloc, free. Tuy nhiên, việc quản lý bộ nhớ động có thể gặp phải các vấn đề như memory leaks, dangling pointers, và còn nhiều rủi ro khác. Để giải quyết các vấn đề này và làm cho việc quản lý bộ nhớ động trở nên an toàn hơn, người ta thường sử dụng Exception Handling - một cơ chế cho phép xử lý các tình huống ngoại lệ một cách linh hoạt và hiệu quả.

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ề cách xử lý ngoại lệ khi làm việc với Memory Allocation trong ngôn ngữ lập trình C++. Mình sẽ tìm hiểu cách xử lý ngoại lệ khi cấp phát và giải phóng bộ nhớ động, cùng với việc sử dụng Smart Pointers như unique_ptr shared_ptr để quản lý bộ nhớ một cách an toàn và tự động. Ta cũng sẽ thực hiện các ví dụ minh họa và thực hành để nắm vững các kỹ thuật xử lý ngoại lệ trong Memory Allocation.

Memory Allocation là gì?

t E1 BA A3i 20xu E1 BB 91ng 20 1  jpg

Memory Allocation là quá trình trong đó chương trình cấp phát và sử dụng bộ nhớ động để lưu trữ dữ liệu trong quá trình thực thi. Trong ngôn ngữ lập trình C++, có hai cách phổ biến để cấp phát bộ nhớ động: sử dụng toán tử new và delete, hoặc các hàm như malloc và free. Quá trình cấp phát bộ nhớ động cho phép chương trình có thể tạo ra và quản lý các đối tượng có kích thước không biết trước vào thời điểm biên dịch.

Ý nghĩa và vai trò của Exception Handling trong C++

Bài viết này được đăng tại [free tuts .net]

Exception Handling là một cơ chế trong ngôn ngữ lập trình C++ cho phép xử lý các tình huống ngoại lệ một cách linh hoạt và hiệu quả. Khi một tình huống ngoại lệ xảy ra trong quá trình thực thi chương trình, Exception Handling cho phép mình bắt và xử lý ngoại lệ một cách kiểm soát, từ đó giúp tránh được sự gián đoạn của chương trình và cung cấp cơ hội để khôi phục hoặc báo cáo lỗi. Điều này rất hữu ích khi làm việc với Memory Allocation, vì ta có thể xử lý các vấn đề như memory leaks, dangling pointers, hay các lỗi khác một cách nhanh chóng và an toàn.

Xử lý ngoại lệ khi cấp phát bộ nhớ động trong C++

Xử lý ngoại lệ khi sử dụng toán tử new

Khi sử dụng toán tử new để cấp phát bộ nhớ động trong C++, nếu không đủ bộ nhớ để cấp phát, một ngoại lệ kiểu std::bad_alloc sẽ được sinh ra. Để xử lý ngoại lệ này, ta sử dụng cấu trúc try-catch. Dưới đây là một ví dụ minh họa:

try {
    // Bài viết được đăng tại freetuts.net
    int* ptr = new int[1000000000000]; // Cố gắng cấp phát một mảng lớn
    // Thực hiện các thao tác với ptr
    delete[] ptr;
} catch(const std::bad_alloc& e) {
    std::cerr << "Lỗi: Không đủ bộ nhớ để cấp phát!\n";
}

Xử lý ngoại lệ khi sử dụng hàm malloc

Trong trường hợp sử dụng hàm malloc, nếu không đủ bộ nhớ để cấp phát, hàm sẽ trả về con trỏ NULL. Ta có thể kiểm tra giá trị của con trỏ trả về để xử lý ngoại lệ:

int* ptr = (int*)malloc(sizeof(int) * 1000000000000); // Cố gắng cấp phát một mảng lớn
// Bài viết được đăng tại freetuts.net

if (ptr == nullptr) {
    std::cerr << "Lỗi: Không đủ bộ nhớ để cấp phát!\n";
} else {
    // Thực hiện các thao tác với ptr
    free(ptr);
}

Xử lý ngoại lệ khi sử dụng hàm calloc

Tương tự như malloc, nếu không đủ bộ nhớ để cấp phát, hàm calloc cũng sẽ trả về con trỏ NULL. Dưới đây là cách xử lý:

int* ptr = (int*)calloc(1000000000000, sizeof(int)); // Cố gắng cấp phát một mảng lớn
// Bài viết được đăng tại freetuts.net
if (ptr == nullptr) {
    std::cerr << "Lỗi: Không đủ bộ nhớ để cấp phát!\n";
} else {
    // Thực hiện các thao tác với ptr
    free(ptr);
}

Trong cả ba trường hợp trên, ta kiểm tra trạng thái của con trỏ nhận được sau khi cấp phát. Nếu con trỏ đó không null, ta có thể tiến hành thực hiện các thao tác mong muốn. Ngược lại, nếu con trỏ là null, ta thông báo lỗi không đủ bộ nhớ.

Xử lý ngoại lệ khi giải phóng bộ nhớ trong C++

Xử lý ngoại lệ khi sử dụng toán tử delete

Khi sử dụng toán tử delete để giải phóng bộ nhớ động đã được cấp phát trước đó, nếu con trỏ trỏ tới nullptr hoặc đã được giải phóng trước đó, không có ngoại lệ nào được sinh ra. Tuy nhiên, nếu con trỏ trỏ tới một vùng bộ nhớ không hợp lệ hoặc đã được giải phóng trước đó, chương trình có thể gặp phải hành vi không xác định.

Dưới đây là một ví dụ về cách sử dụng toán tử delete một cách an toàn:

int* ptr = new int(10); // Cấp phát bộ nhớ động
// Bài viết được đăng tại freetuts.net
if (ptr != nullptr) {
    // Thực hiện các thao tác với ptr
    delete ptr; // Giải phóng bộ nhớ
} else {
    std::cerr << "Lỗi: Con trỏ không hợp lệ!\n";
}

Xử lý ngoại lệ khi sử dụng hàm free

Khi sử dụng hàm free để giải phóng bộ nhớ đã được cấp phát trước đó bằng hàm malloc hoặc calloc, không có ngoại lệ nào được sinh ra. Tuy nhiên, nếu con trỏ trỏ tới nullptr hoặc đã được giải phóng trước đó, hành vi của chương trình có thể không xác định.

Dưới đây là một ví dụ minh họa:

int* ptr = (int*)malloc(sizeof(int)); // Cấp phát bộ nhớ động
// Bài viết được đăng tại freetuts.net
if (ptr != nullptr) {
    // Thực hiện các thao tác với ptr
    free(ptr); // Giải phóng bộ nhớ
} else {
    std::cerr << "Lỗi: Con trỏ không hợp lệ!\n";
}

Trong cả hai trường hợp trên, ta kiểm tra trạng thái của con trỏ trước khi giải phóng bộ nhớ. Nếu con trỏ là nullptr, ta thông báo lỗi không hợp lệ.

Cách xử lý ngoại lệ khi quản lý bộ nhớ thông qua Smart Pointers trong C++

Sử dụng unique_ptr và xử lý ngoại lệ

Khi sử dụng unique_ptr, không cần phải xử lý ngoại lệ khi giải phóng bộ nhớ động, vì unique_ptr tự độ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 nữa. Tuy nhiên, nếu cố gắng chuyển quyền sở hữu của một unique_ptr sang một unique_ptr khác, hoặc nếu cố gắng sao chép hoặc gán một unique_ptr, có thể xảy ra ngoại lệ std::bad_weak_ptr.

Dưới đây là một ví dụ về cách sử dụng unique_ptr và xử lý ngoại lệ:

#include <iostream>
#include <memory>

int main() {
    try {
        // 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

        // Thực hiện các thao tác với ptr

        // Không cần gọi delete, unique_ptr sẽ tự động giải phóng bộ nhớ khi ra khỏi phạm vi
    } catch (const std::bad_weak_ptr& e) {
        std::cerr << "Lỗi: " << e.what() << "\n";
    }
    return 0;
}

Sử dụng shared_ptr và xử lý ngoại lệ

Khi sử dụng shared_ptr, không cần phải xử lý ngoại lệ khi giải phóng bộ nhớ động, vì shared_ptr tự động quản lý vòng đời của đối tượng được chỉ định và sẽ tự động giải phóng bộ nhớ khi không còn ai sử dụng nó nữa. Tương tự như unique_ptr, cố gắng chuyển quyền sở hữu hoặc sao chép shared_ptr có thể gây ra ngoại lệ std::bad_weak_ptr.

Dưới đây là một ví dụ về cách sử dụng shared_ptr và xử lý ngoại lệ:

#include <iostream>
#include <memory>

int main() {
    try {
        // Sử dụng shared_ptr để quản lý bộ nhớ động
        std::shared_ptr<int> ptr = std::make_shared<int>(42);

        // Thực hiện các thao tác với ptr
         // Bài viết được đăng tại freetuts.net
        // Không cần gọi delete, shared_ptr sẽ tự động giải phóng bộ nhớ khi không còn ai sử dụng nó nữa
    } catch (const std::bad_weak_ptr& e) {
        std::cerr << "Lỗi: " << e.what() << "\n";
    }
    return 0;
}

Sử dụng weak_ptr và xử lý ngoại lệ

weak_ptr không giữ quyền sở hữu của đối tượng, nên không cần xử lý ngoại lệ khi giải phóng bộ nhớ. Tuy nhiên, khi muốn truy cập đối tượng được quản lý bởi một weak_ptr, ta cần kiểm tra trạng thái của weak_ptr trước khi thao tác với đối tượng.

Dưới đây là một ví dụ về cách sử dụng weak_ptr và xử lý ngoại lệ:

#include <iostream>
#include <memory>

int main() {
    try {
        std::shared_ptr<int> sharedPtr = std::make_shared<int>(42);
        std::weak_ptr<int> weakPtr(sharedPtr);
// Bài viết được đăng tại freetuts.net
        // Kiểm tra trạng thái của weakPtr trước khi thao tác với đối tượng
        if (auto lockedPtr = weakPtr.lock()) {
            std::cout << "Giá trị: " << *lockedPtr << std::endl;
        } else {
            std::cout << "Đối tượng đã bị giải phóng.\n";
        }
    } catch (const std::bad_weak_ptr& e) {
        std::cerr << "Lỗi: " << e.what() << "\n";
    }
    return 0;
}

Output:

Giá trị: 42

Ví dụ Memory Allocation trong C++

Ví dụ về xử lý ngoại lệ khi cấp phát bộ nhớ động

Trong ví dụ này, mình sẽ sử dụng try, catch để xử lý ngoại lệ khi cấp phát bộ nhớ động bằng toán tử new.

#include <iostream>
#include <memory>
// Bài viết được đăng tại freetuts.net

int main() {
    try {
        int* ptr = new int[1000000000000000000]; // Cố ý cấp phát một lượng bộ nhớ lớn
        delete[] ptr;
    } catch (std::bad_alloc& e) {
        std::cerr << "Lỗi cấp phát bộ nhớ động: " << e.what() << '\n';
    }
    return 0;
}

Output:

Screenshot 202024 04 06 20115023 png

Ví dụ về xử lý ngoại lệ khi giải phóng bộ nhớ

Trong ví dụ này, sẽ sử dụng try, catch để xử lý ngoại lệ khi giải phóng bộ nhớ bằng toán tử delete.

#include <iostream>
// Bài viết được đăng tại freetuts.net

int main() {
    int* ptr = new int(42);
    try {
        delete ptr; // Cố gắng giải phóng bộ nhớ đã được giải phóng trước đó
    } catch (...) {
        std::cerr << "Lỗi khi giải phóng bộ nhớ.\n";
    }
    return 0;
}

Sử dụng Smart Pointers để quản lý bộ nhớ với Exception Handling

Trong thực hành này, sẽ sử dụng std::unique_ptr hoặc std::shared_ptr để quản lý bộ nhớ và xử lý ngoại lệ khi cần thiết.

#include <iostream>
#include <memory>

int main() {
    try {
        // Sử dụng unique_ptr để quản lý bộ nhớ động
        std::unique_ptr<int> ptr = std::make_unique<int>(42);
// Bài viết được đăng tại freetuts.net
        // Thực hiện các thao tác với ptr

        // Không cần gọi delete, unique_ptr sẽ tự động giải phóng bộ nhớ khi ra khỏi phạm vi
    } catch (const std::bad_weak_ptr& e) {
        std::cerr << "Lỗi: " << e.what() << "\n";
    }

    try {
        // Sử dụng shared_ptr để quản lý bộ nhớ động
        std::shared_ptr<int> ptr = std::make_shared<int>(42);

        // Thực hiện các thao tác với ptr

        // Không cần gọi delete, shared_ptr sẽ tự động giải phóng bộ nhớ khi không còn ai sử dụng nó nữa
    } catch (const std::bad_weak_ptr& e) {
        std::cerr << "Lỗi: " << e.what() << "\n";
    }

    return 0;
}

Kết bài

Trong bài viết này, mình đã tìm hiểu về cách xử lý ngoại lệ khi làm việc với Memory Allocation trong C++. Bạn đã tìm hiểu về các cách sử dụng try, catch và throw để xử lý ngoại lệ khi cấp phát và giải phóng bộ nhớ động bằng toán tử new, delete, malloc và free. Ngoài ra, cũng đã tìm hiểu cách sử dụng Smart Pointers như std::unique_ptr std::shared_ptr để quản lý bộ nhớ và xử lý ngoại lệ một cách an toàn và thuận tiện.

Việc hiểu và áp dụng các kỹ thuật xử lý ngoại lệ này trong quá trình làm việc với Memory Allocation là rất quan trọng để đảm bảo ứng dụng của bạn hoạt động ổn định và an toàn. Hy vọng rằng thông qua bài viết này, bạn đã có cái nhìn tổng quan về cách xử lý ngoại lệ khi làm việc với Memory Allocation trong C++.

Cùng chuyên mụ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++

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 shared_ptr trong C++

Sử dụng shared_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++

[STL] Các hàm thông dụng của Map trong C++

[STL] Các hàm thông dụng của Map trong C++

Top