Con trỏ thông minh trong C++
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 15 trên 104.
Con trỏ thông minh là các lớp bao bọc tự động quản lý bộ nhớ heap cho bạn. Chúng loại bỏ nhu cầu gọi delete thủ công bằng cách giải phóng bộ nhớ khi con trỏ ra khỏi phạm vi.
Thêm header <memory> để sử dụng con trỏ thông minh.
unique_ptr - quyền sở hữu độc quyền. Mỗi lần chỉ một con trỏ có thể sở hữu tài nguyên.
#include <memory>
std::unique_ptr<Player> player = std::make_unique<Player>("Hero");
player->attack(); // Dùng như con trỏ thông thường
// Không thể sao chép, nhưng có thể chuyển quyền sở hữu
std::unique_ptr<Player> player2 = std::move(player);
// player giờ là nullptrshared_ptr - quyền sở hữu chung. Nhiều con trỏ có thể cùng sở hữu một tài nguyên. Bộ nhớ được giải phóng khi chủ sở hữu cuối cùng bị hủy.
std::shared_ptr<Player> p1 = std::make_shared<Player>("Hero");
std::shared_ptr<Player> p2 = p1; // Cả hai cùng sở hữu tài nguyên
std::cout << p1.use_count(); // In ra 2weak_ptr là đối tượng quan sát không sở hữu của shared_ptr. Hữu ích để phá vỡ các tham chiếu vòng.
std::shared_ptr<Player> shared = std::make_shared<Player>("Hero");
std::weak_ptr<Player> weak = shared;
if (auto locked = weak.lock()) { // Chuyển sang shared_ptr
locked->attack();
}Mặc định, hãy dùng unique_ptr để sở hữu riêng. Dùng shared_ptr khi nhiều đối tượng cần cùng chia sẻ một tài nguyên. Dùng weak_ptr để theo dõi mà không kéo dài thời gian tồn tại.
Ưu tiên dùng make_unique và make_shared thay vì new thuần.
Thử thách
DễHãy xây dựng một hệ thống chia sẻ tài liệu để minh họa các mô hình sở hữu khác nhau của con trỏ thông minh. Bạn sẽ thấy cách unique_ptr đảm bảo quyền sở hữu độc quyền, cách shared_ptr cho phép nhiều chủ sở hữu và cách weak_ptr có thể quan sát mà không ảnh hưởng đến thời gian tồn tại.
Bạn sẽ tạo hai tệp để sắp xếp code:
Document.h: Định nghĩa một lớpDocumentđại diện cho tài liệu có thể chia sẻ. Lớp này cần có:- Thuộc tính
titleở phạm vi private (chuỗi) - Hàm khởi tạo nhận tiêu đề và in ra
"Document created: <title>" - Hàm hủy in ra
"Document deleted: <title>" - Phương thức
getTitle()trả về tiêu đề
- Thuộc tính
main.cpp: Minh họa cả ba loại con trỏ thông minh bằng cách đọc tiêu đề tài liệu từ đầu vào, sau đó:- Tạo một
unique_ptrtrỏ đến một tài liệu có tiêu đề lấy từ đầu vào - In ra
"Unique owner: <title>" - Chuyển quyền sở hữu sang một
unique_ptrkhác bằngstd::move - In ra
"Ownership transferred" - Tạo một
shared_ptrtrỏ đến tài liệu mới có tiêu đề"SharedDoc" - Tạo một
shared_ptrthứ hai trỏ đến cùng tài liệu đó - In ra
"Shared owners: <count>"bằnguse_count() - Tạo một
weak_ptrtừ một trong các con trỏ chia sẻ - Dùng
lock()để truy cập tài liệu thông qua con trỏ yếu và in ra"Weak access: <title>"
- Tạo một
Nhớ thêm <memory> để dùng con trỏ thông minh. Dùng std::make_unique và std::make_shared để tạo con trỏ thông minh. Các thông báo từ hàm hủy sẽ giúp bạn biết khi nào mỗi tài liệu được tự động dọn dẹp vào cuối chương trình.
Thêm tệp tiêu đề vào main.cpp bằng #include "Document.h".
Tự làm thử
#include <iostream>
#include <memory>
#include <string>
#include "Document.h"
using namespace std;
int main() {
string inputTitle;
cin >> inputTitle;
// TODO: Tạo một unique_ptr trỏ đến Document với inputTitle
// bằng std::make_unique
// TODO: In ra "Unique owner: <title>"
// TODO: Chuyển quyền sở hữu sang một unique_ptr khác bằng std::move
// TODO: In ra "Ownership transferred"
// TODO: Tạo một shared_ptr trỏ đến Document mới có tiêu đề "SharedDoc"
// bằng std::make_shared
// TODO: Tạo một shared_ptr thứ hai trỏ đến cùng tài liệu
// TODO: In ra "Shared owners: <count>" bằng use_count()
// TODO: Tạo một weak_ptr từ một trong các shared_ptr
// TODO: Dùng lock() để truy cập tài liệu qua weak_ptr
// và in ra "Weak access: <title>"
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