Con trỏ và tham chiếu
Thuộc phần Lập trình hướng đối tượng trong hành trình C++ của Coddy. Bài học 13 trên 104.
Bây giờ bạn đã hiểu về bộ nhớ stack và heap, hãy cùng khám phá các công cụ giúp bạn làm việc trực tiếp với các vị trí bộ nhớ: con trỏ và tham chiếu.
Con trỏ lưu địa chỉ bộ nhớ của một biến khác. Bạn khai báo con trỏ bằng * và lấy địa chỉ bằng &.
int value = 42;
int* ptr = &value; // ptr lưu địa chỉ của value
std::cout << ptr; // In ra (print) địa chỉ bộ nhớ
std::cout << *ptr; // Giải tham chiếu: in ra 42Tham chiếu là bí danh của một biến đã tồn tại. Sau khi được gán, nó luôn tham chiếu đến biến đó. Bạn khai báo tham chiếu bằng &.
int value = 42;
int& ref = value; // ref là bí danh của value
ref = 100; // Đổi value thành 100
std::cout << value; // In ra 100Điểm khác biệt chính:
| Con trỏ | Tham chiếu |
|---|---|
| Có thể là null | Phải được khởi tạo |
| Có thể được gán lại | Không thể được liên kết lại |
Dùng * để truy cập giá trị | Truy cập trực tiếp giá trị |
| Có thể không trỏ đến đâu | Luôn hợp lệ |
Cả hai đều rất cần thiết để truyền đối tượng vào hàm một cách hiệu quả mà không cần sao chép, cũng như để làm việc với bộ nhớ được cấp phát trên heap. Tham chiếu an toàn và đơn giản hơn, còn con trỏ linh hoạt hơn khi bạn cần gán lại hoặc kiểm tra giá trị null.
Thử thách
DễHãy xây dựng một hệ thống thao tác giá trị đơn giản để minh họa cách con trỏ và tham chiếu hoạt động khác nhau khi sửa đổi dữ liệu.
Bạn sẽ tạo hai tệp để sắp xếp code:
ValueOps.h: Định nghĩa ba hàm sửa đổi giá trị số nguyên theo những cách khác nhau:doubleByPointer(int* ptr): nhận một con trỏ và nhân đôi giá trị mà con trỏ trỏ đếntripleByReference(int& ref): nhận một tham chiếu và nhân ba giá trịswapValues(int* a, int* b): nhận hai con trỏ và hoán đổi các giá trị mà chúng trỏ đến
main.cpp: Đọc hai số nguyên từ đầu vào, sau đó minh họa cả con trỏ và tham chiếu:- In ra các giá trị ban đầu:
"Original: <first> <second>" - Dùng
doubleByPointervới giá trị thứ nhất, sau đó in ra:"After double: <first>" - Dùng
tripleByReferencevới giá trị thứ hai, sau đó in ra:"After triple: <second>" - Dùng
swapValuesđể hoán đổi hai giá trị, sau đó in ra:"After swap: <first> <second>"
- In ra các giá trị ban đầu:
Khi gọi hàm với con trỏ, hãy nhớ dùng toán tử lấy địa chỉ (&) để truyền địa chỉ của biến. Tham chiếu hoạt động tự nhiên hơn, bạn chỉ cần truyền trực tiếp biến.
Đưa tệp tiêu đề vào main.cpp bằng #include "ValueOps.h".
Tự làm thử
#include <iostream>
#include "ValueOps.h"
using namespace std;
int main() {
// Đọc hai số nguyên từ đầu vào
int first, second;
cin >> first;
cin >> second;
// In ra các giá trị ban đầu
cout << "Original: " << first << " " << second << endl;
// TODO: Dùng doubleByPointer cho giá trị thứ nhất (truyền địa chỉ bằng &)
// TODO: In ra các giá trị sau khi nhân đôi
// TODO: Dùng tripleByReference cho giá trị thứ hai (truyền trực tiếp biến)
// TODO: In ra các giá trị sau khi nhân ba
// TODO: Dùng swapValues để hoán đổi hai giá trị (truyền địa chỉ bằng &)
// TODO: In ra các giá trị sau khi hoán đổi
return 0;
}
Bài học này có một bài trắc nghiệm ngắn. Bắt đầu bài học để trả lời và theo dõi tiến độ.
Tất cả bài học trong Lập trình hướng đối tượng
1Nền tảng về OOP
Tệp bên ngoàiBiên dịch và tạo bản dựng C++Tệp header và tệp nguồnNamespace và phạm viGiới thiệu về OOP trong C++Lớp và đối tượngCon trỏ thisPhương thức (hàm thành viên)Thuộc tính (dữ liệu thành viên)Cơ bản về hàm khởi tạo và hàm hủyÔn tập: máy tính đơn giản4Thuộc tính của lớp
Thành viên đối tượng và thành viên tĩnhHàm truy xuất và hàm thiết lậpHàm thành viên hằngTừ khóa mutablePhương thức và biến tĩnhHàm và lớp bạnÔn tập: Trình quản lý tài khoản ngân hàng7Kế thừa
Kế thừa cơ bảnMức truy cập trong kế thừaThứ tự gọi hàm khởi tạo và hàm hủyGhi đè phương thứcHàm ảo và VTableĐa kế thừaKế thừa ảoÔn tập: hệ phân cấp nhân viên2Quản lý bộ nhớ
Bộ nhớ ngăn xếp và heapCon trỏ và tham chiếuBộ nhớ động (new/delete)Con trỏ thông minh trong C++RAII trong C++Ôn tập: Trình quản lý mảng động5Tính đóng gói
Phạm vi truy cập trong C++Tìm hiểu sâu về phạm vi truy cậpẨn thông tinStruct và lớpLớp lồng nhau và lớp bên trongÔn tập: hệ thống hồ sơ sinh viên8Đa hình
Đa hình lúc biên dịch và khi chạyNạp chồng hàmÔn lại hàm ảoHàm ảo thuần túyLớp trừu tượngThiết kế interface trong C++Ép kiểu động và RTTIÔn tập: Máy tính hình học3Hàm khởi tạo và hàm hủy
Hàm khởi tạo mặc địnhHàm khởi tạo có tham sốHàm khởi tạo sao chépHàm khởi tạo di chuyểnDanh sách khởi tạoHàm khởi tạo ủy quyềnTìm hiểu sâu về hàm hủyQuy tắc 3 / 5 / 0Ôn tập: lớp chuỗi6Nạp chồng toán tử
Giới thiệu về nạp chồng toán tửNạp chồng toán tử số họcNạp chồng toán tử so sánhToán tử luồngNạp chồng toán tử gánNạp chồng toán tử [] và ()Toán tử chuyển đổi kiểuÔn tập: lớp ma trận9Mẫu
Mẫu hàmMẫu lớpChuyên biệt hóa mẫuMẫu biến tham sốCơ bản về SFINAE và đặc điểm kiểuÔn tập: vùng chứa tổng quátTự luyện tập: Trình biên dịch C++ online