Giới thiệu
Trước C++11 có 2 phương pháp khởi tạo giá trị cho đối tượng là Constructor và Copy Constructor, đến C++11 xuất hiện thêm Move Sematics, từ đó xuất hiện thêm loại constructor thứ 3 - Move Constructor.
Như vậy, hiện tại C++11 trở về sau hỗ trợ 3 loại constructor với mục đích sử dụng khác nhau nhằm tối ưu hóa cho từng trường hợp sử dụng cụ thể:
- Constructor: hàm tạo khởi tạo mới.
- Copy constructor: hàm tạo sao chép.
- Move constructor: hàm tạo dịch chuyển.
Ứng với 3 tên gọi này là mục đích sử dụng của nó. Cả 3 phương thức đều tự động gọi khi đối tượng được tạo ra.
Constructor là gì?
Constructor là phương thức khởi tạo khi muốn tạo 1 đối tượng độc lập và mới hoàn toàn.
Copy constructor là gì?
Copy constructor là phương thức khởi tạo khi muốn tạo 1 đối tượng và tại thời điểm khởi tạo sẽ sao chép dữ liệu từ 1 đối tượng cùng kiểu.
Move constructor là gì?
Move constructor là phương thức khởi tạo khi muốn tạo 1 đối tượng và tại thời điểm khởi tạo sẽ lấy dữ liệu từ đối tượng có cùng kiểu.
Các trường hợp sử dụng
Để hiểu được cách sử dụng của 3 loại constructor trên, xét ví dụ cụ thể trong trường hợp ma trận 4x4:
#include <string.h> class Matrix4x4 { private: double* data; public: Matrix4x4() { data = new double[4 * 4]; } Matrix4x4(const Matrix4x4& matrix) { data = new double[4 * 4]; memcpy(data, matrix.data, sizeof(double) * 4 * 4); } Matrix4x4(Matrix4x4&& matrix) { data = matrix.data; matrix.data = nullptr; } ~Matrix4x4() { if (data != nullptr) delete[] data; } Matrix4x4& operator=(const Matrix4x4& matrix) { memcpy(data, matrix.data, sizeof(double) * 4 * 4); return *this; } Matrix4x4 operator+(const Matrix4x4& matrix) { Matrix4x4 result = *this; for (int i = 0; i < 4 * 4; i++) result.data[i] += matrix.data[i]; return result; } };
int main() { Matrix4x4 m1, m2; Matrix4x4 m3 = m1 + m2; return 0; }
Matrix4x4()
Constructor được gọi trong trường hợp tạo mới 1 Matrix4x4 không truyền đối tượng Matrix4x4 khác. Ví dụ:
Matrix4x4 m1;
Matrix4x4 m2;
Constructor được sử dụng trong trường hợp tạo mới 1 Matrix4x4 và không có nhu cầu sao chép bất kỳ dữ liệu nào từ các đối tượng Matrix4x4 có sẵn.
Matrix4x4(const Matrix4x4 & matrix)
Copy constructor được gọi trong trường hợp khởi tạo đối tượng truyền vào 1 lvalue có kiểu là Matrix4x4. Ví dụ khởi tạo m2
và gọi copy constructor để sao chép giá trị của m1
.
Matrix4x4 m1;
Matrix4x4 m2(m1); // hoặc Matrix4x4 m2 = m1;
Copy constructor được sử dụng trong trường hợp tạo các giá trị mới nhưng cần sao chép ngay các dữ liệu của đối tượng có sẵn thay vì gọi constructor tốn kém hiệu năng và rồi gọi phương thức sao chép tốn kém thêm 1 lần hiệu năng nữa.
1 trường hợp điển hình sử dụng copy constructor thuận tiện là hoán đổi giá trị của 2 Matrix4x4
khi cần khai báo 1 Matrix4x4 temp
.
Matrix4x4 temp = m1;
m1 = m2;
m2 = temp;
Dòng Matrix4x4 temp = m1
gọi copy constructor khi khởi tạo temp
, trường hợp này dùng vậy rất thuật tiện và đỡ tốn hiệu năng.
Matrix4x4(Matrix4x4 && matrix)
Move constructor được gọi trong trường hợp khởi tạo đối tượng truyền vào 1 rvalue.
Matrix4x4 m1, m2; Matrix4x4 m3(m1 + m2);
Dòng Matrix4x4 m3(m1 + m2)
trong đó m1 + m2
sẽ trả về 1 đối tượng tạm, sau dòng code này đối tượng đó sẽ hủy, trước C++11 chỉ có thể gọi copy constructor, m3
sẽ phải khởi khởi tạo vùng nhớ và sao chép dữ liệu từ đối tượng m1 + m2
dù biết rằng m1 + m2
sẽ không dùng nữa nhưng vẫn không thể "chiếm dụng" kết quả này, nhưng với C++11 thì khác, có thể tiến hành gọi move constructor vì biết m1 + m2
là rvalue, như phần hiện thực của move constructor có thể thấy dữ liệu tạm được "sang nhượng" cho m3
. với chi phí thấp hơn copy constructor.