[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:
- http://www.cplusplus.com/reference/memory/auto_ptr/
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?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
À, ra vậy, nếu vậy thì
auto_ptr
còn được dùng tới, ít nhất, 2020P/S: Có lẽ nên thêm cái dòng này
Vào đoạn này
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?