30/09/2018, 17:04

[C++] Sử dụng auto_ptr để đơn giản hoá việc huỷ đối tượng

Giới Thiệu

Trong C++ việc cấp phát và huỷ đối tượng khá dễ dàng gây ra rò rỉ bộ nhớ (memory leaks). Các đoạn code để hủy đối tượng được tạo ra thường khá rối và dễ gây lỗi. Vấn đề đặt ra là làm sao để quản lý đối tượng tạo ra một cách tốt và hiệu quả nhất, và cho mã code của mình gọn gàng (clean) nhất. Để đạt được yêu cầu trên có thể phải kết hợp nhiều kĩ thuật khác nhau, ở bài này mình sẽ giới thiệu kĩ thuật sử dụng lớp std::auto_ptr của thư viện chuẩn.

std::auto_ptr được sử dụng bắt đầu từ chuẩn C++98, đến chuẩn C++11 thì nó đã bị cảnh báo loại bỏ và cung cấp một lớp mới std::unique_ptr có tính năng tương tự nhưng bổ sung thêm một số tính năng mới trong đó có hỗ trợ việc huỷ một mảng. Tuy nhiên bạn vẩn có thể sử dụng std::auto_ptr cho đến phiên bản C++17 thì nó mới bị loại bỏ hoàn toàn. Do đó bạn hoàn toàn có sử dụng auto_ptr với các complier từ C++98 trở về sau mà không gặp trở ngại gì.

Cách sử dụng auto_ptr

Xem qua đoạn code sau:

void doSomething()
{
    Employee *employee = new Employee;

    // more code here

    delete employee;
}

Đoạn code trên có thể dẫn tới leak đối tượng employee nếu trước khi gọi delete employee có lệnh return, hoặc có exception xảy ra. Nếu bạn cẩn thận và delete employee trước khi return thì cũng làm cho đoạn code này lặp đi lặp lại nhiều lần.

Dưới đây là đoạn code sử dụng std::auto_ptr

void doSomething()
{
    std::auto_ptr<Employee> employee(new Employee);

    // more code here
}

Object employee lúc này sẽ được gọi delete lúc ra khỏi hàm

Truy xuất các member của Object dùng auto_ptr như thể nào?

Bạn có thể truy xuất các đối tượng thông qua operator-> tương tự như lúc chưa sử dụng auto_ptr vậy

std::string id = employee->getId();

Lấy con trỏ của đối tượng employee

Employee *pEmp = employee.get();

Lấy con trỏ và giải phóng đối tượng khỏi auto_ptr

Employee *getEmployeeFromDb()
{
	std::auto_ptr<Employee> employee(new Employee);

	// get employee from database code

	return employee.release()
}

Con trỏ của employee tạo ra được trả về, và employee sẽ không bị huỷ khi thoát khỏi hàm

Delete object mà auto_ptr đang chứa

std::auto_ptr<Employee> employee(new Employee);
employee.reset(0);

Delele object mà auto_ptr đang chứa và thay thể bằng object khác

Employee *a = new Employee;
std::auto_ptr<Employee> employee(new Employee);

employee.reset(a);

Khi gọi hàm reset thì đối tượng mà employee đang chứa được delete và thay bằng đối tượng a.

Phép gán auto_ptr

auto_ptr<int> a(new int);
auto_ptr<int> b = a;

Lúc này con trỏ mà a đang chứa sẽ được gán cho b, và a lúc này không còn giữ đối tượng ban đầu nữa (a.get() sẽ là NULL)

Hạn chế của auto_ptr

Không thể huỷ mảng của con trỏ

std::auto_ptr<char> arrPtr(new char[100]);  // Wrong

auto_ptr sẽ dùng delete chứ không phải delete [] để huỷ mảng char ở trên

Có thể dùng việc sử dụng vector để thay thế việc cấp phát như trên Xem ở đây. Hoặc tự viết một lớp tương tự auto_ptr sử dụng hàm delete [] để hủy con trỏ

Không dùng được trong các lớp container của thư viện STL

std::vetor<std::auto_ptr<int>> v; // Very dangerous

Do phép gán của auto_ptr không phải là phép gán thông thường, do nó không giữ lại giá trị của trỏ sau được gán cho phần tử khác

Recommend: auto_ptr rất ít được sử dụng bởi lập trình viên, điều này rất là đáng tiếc! mình khuyên mỗi lần bạn tạo mới đối tượng thì nên nghĩ đến việc sử dụng auto_ptr để tạo ra mã nguồn chất lượng hơn

Nguồn tham khảo:

  1. http://www.cplusplus.com/reference/memory/auto_ptr/
Mai Anh Dũng viết 19:07 ngày 30/09/2018

Tuy nhiên bạn vẩn có thể sử dụng std::auto_ptr cho đến phiên bản C++17 thì nó mới bị loại bỏ hoàn toàn

Vậy tại sao mình lại sử dụng nó trong khi nó sẽ bị thay thế bằng std::unique_ptr Huân?

Tran Huan viết 19:15 ngày 30/09/2018

Dùng auto_ptr thì compliler từ c++98 trở lên ở thời điểm hiện tại đều biên dịch được, nếu anh dùng std::unique_ptr thì không portable cho lắm

Mai Anh Dũng viết 19:14 ngày 30/09/2018

À, ra vậy, nếu vậy thì auto_ptr còn được dùng tới, ít nhất, 2020

P/S: Có lẽ nên thêm cái dòng này

Dùng auto_ptr thì compliler từ c++98 trở lên ở thời điểm hiện tại đều biên dịch được

Vào đoạn này

std::auto_ptr được sử dụng bắt đầu từ chuẩn C++98, đến chuẩn C++11 thì nó đã bị cảnh báo loại bỏ và cung cấp một lớp mới std::unique_ptr có tính năng tương tự nhưng bổ sung thêm một số tính năng mới trong đó có hỗ trợ việc huỷ một mảng. Tuy nhiên bạn vẩn có thể sử dụng std::auto_ptr cho đến phiên bản C++17 thì nó mới bị loại bỏ hoàn toàn


Huân có thể nói rõ hơn phần “Hạn chế của auto_ptr:”? Ví dụ như khai báo mảng như thế nào?

Bài liên quan
0