Thứ Sáu, 16 tháng 10, 2015

Con trỏ ưu việt hơn biến thường chỗ nào

Filled under:

"con trỏ ưu việt hơn biến thường chỗ nào"
Ví dụ: Với struct sau (Vì là ví dụ nên mình chọn struct thay vì class cho nó tiện, mọi thứ đều public):
struct Array {
__int a[100000];
__// default constructor, copy constructor, destructor
__// other members
};

Bạn có thể thấy là kích thước nó khá lớn. Với một hàm dạng như dưới đây:
void f(Array a);
và khi được gọi thế này:
Array myarray;
f(myarray);
Sẽ có một bản sao của myarray được tạo ra (tức là phải gọi copy constructor), và sau khi kết thúc hàm, destructor phải được gọi để hủy cái bản sao đó. Như vậy xét về thời gian thì truyền như trên phải mất thời gian cho constructor và destructor, xét về bộ nhớ thì nó tiêu tốn thêm sizeof(int) * 100000 bytes chưa kể phần của member khác.

Còn với void g(Array* a);
khi dùng:
Array myarray;
g(&myarray);
Vẫn sẽ có một bản sao được tạo ra, nhưng cái hay ở đây là nó không tạo bản sao của myarray, mà nó tạo bản sao của &myarray, tức là nó tạo một bản sao của con trỏ [1], mà con trỏ thì kích thước vô cùng nhỏ (cỡ 4 hoặc 8 bytes thôi). Như vậy việc copy đã được giảm chi phí thời gian và không gian đến một mức nhỏ hơn nhiều. Truyền tham số kiểu này sẽ KHÔNG gọi constructor, và kéo theo đó là không có lời gọi destructor nào diễn ra. Vậy nên khi lập trình với những object lớn thì người ta thường không truyền theo kiểu ở hàm f.

Tuy nhiên dùng con trỏ thì code trở nên khó đọc hơn so với dùng reference, nên người ta thường truyền object theo const ref (có thể không có const nếu cần thay đổi dữ liệu của object) chứ không dùng cách ở hàm f và g.

[Lề] Giữa reference và pointer, cái nào cũng có cái hay của nó. Reference thì làm cho code đơn giản dễ đọc và ít viết nhầm hơn, còn pointer thì lại giúp lời gọi hàm rõ nghĩa hơn vì có những hàm lưu kết quả vào một vùng nhớ được cung cấp sẵn qua tham số. Với trường hợp đó thì khi truyền tham số, cứ cái nào có dấu & (toán tử lấy địa chỉ) phía trước là người đọc hiểu ngay nó dùng để nhận kết quả của hàm. Ngoài ra pointer có thể nhận giá trị nullptr, còn reference thì không, tùy từng trường hợp mà nó có thể có lợi hoặc không.

[1]: Có thể hiểu con trỏ là một biến nguyên, và việc copy con trỏ chính là phép gán một biến nguyên cho một biến nguyên khác: a = b

0 nhận xét:

Đăng nhận xét