Thành viên đối tượng và thành viên tĩnh
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 27 trên 104.
Đến giờ, mọi biến thành viên bạn tạo đều thuộc về một đối tượng cụ thể. Khi tạo hai đối tượng Player, mỗi đối tượng có name và health riêng. Chúng được gọi là thành viên của instance: mỗi instance của lớp có các thành viên này riêng biệt.
Nhưng nếu bạn cần chia sẻ dữ liệu giữa tất cả các đối tượng thì sao? Ví dụ, theo dõi tổng số người chơi.
Đây là lúc thành viên tĩnh phát huy tác dụng. Thành viên tĩnh thuộc về chính lớp, không thuộc về một đối tượng cụ thể nào.
class Player {
std::string name; // Thành viên instance, mỗi người chơi có một thành viên riêng
int health; // Thành viên instance
static int playerCount; // Thành viên tĩnh, dùng chung cho TẤT CẢ người chơi
public:
Player(std::string n) : name(n), health(100) {
playerCount++; // Tăng lên khi có người chơi được tạo
}
~Player() {
playerCount--; // Giảm xuống khi có người chơi bị hủy
}
};Điểm khác biệt chính: bạn truy cập các thành viên của instance thông qua một đối tượng, còn có thể truy cập các thành viên static thông qua chính tên lớp. Hãy xem các thành viên của instance là “mỗi đối tượng có một bản” và các thành viên static là “mỗi lớp có một bản”.
| Thành viên của instance | Thành viên tĩnh |
|---|---|
| Riêng biệt với từng đối tượng | Dùng chung cho mọi đối tượng |
| Được tạo khi đối tượng được tạo | Tồn tại trước khi có bất kỳ đối tượng nào được tạo |
Truy cập qua đối tượng: player.health | Truy cập qua lớp: Player::playerCount |
Các thành viên tĩnh hữu ích để làm bộ đếm, lưu giá trị cấu hình hoặc lưu dữ liệu thuộc về lớp thay vì từng đối tượng.
Thử thách
DễHãy xây dựng một hệ thống kiểm kê sản phẩm theo dõi thông tin của từng sản phẩm và thống kê toàn bộ hàng tồn kho bằng các thành viên instance và thành viên static.
Bạn sẽ tạo hai tệp để sắp xếp code:
Product.h: Định nghĩa lớpProductđể quản lý từng sản phẩm, đồng thời theo dõi dữ liệu toàn bộ hàng tồn kho. Lớp của bạn cần có:- Các thành viên instance (instance):
name(chuỗi) vàprice(số thực), riêng biệt cho từng sản phẩm - Một thành viên static
totalProductsđể đếm số sản phẩm hiện có - Một thành viên static
totalValueđể theo dõi tổng giá của tất cả sản phẩm - Một hàm khởi tạo nhận tên và giá, khởi tạo các thành viên instance, đồng thời cập nhật cả hai bộ đếm static (tăng số lượng, cộng giá vào tổng giá trị)
- Một hàm hủy cập nhật các bộ đếm static khi một sản phẩm bị xóa (giảm số lượng, trừ giá khỏi tổng giá trị)
- Một phương thức
getInfo()trả về một chuỗi theo định dạng:"<name>: $<price>" - Một phương thức static
getInventoryStats()trả về chuỗi:"Products: <count>, Total Value: $<value>"
- Các thành viên instance (instance):
main.cpp: Minh họa cách các thành viên static được dùng chung cho mọi đối tượng, trong khi các thành viên instance vẫn riêng biệt. Đọc tên và giá của hai sản phẩm từ đầu vào (name1, price1, name2, price2, mỗi giá trị trên một dòng riêng), sau đó:- In thống kê hàng tồn kho ban đầu bằng tên lớp:
Product::getInventoryStats() - Tạo một khối phạm vi bằng dấu ngoặc nhọn, rồi tạo bên trong khối một
Productvới tên và giá đầu tiên. In thông tin của sản phẩm và thống kê hàng tồn kho hiện tại. - Sau khi kết thúc khối (sản phẩm đầu tiên bị hủy), in
"After first scope:", sau đó in thống kê hàng tồn kho - Tạo một
Productkhác với tên và giá thứ hai. In thông tin của sản phẩm và thống kê hàng tồn kho. - Tạo sản phẩm
Productthứ ba với tên"Bonus"và giá5.00. In thống kê hàng tồn kho cuối cùng.
- In thống kê hàng tồn kho ban đầu bằng tên lớp:
Nhớ định nghĩa các thành viên static bên ngoài lớp trong tệp tiêu đề (sau phần định nghĩa lớp). Định dạng giá với hai chữ số thập phân bằng std::fixed và std::setprecision(2) từ <iomanip>.
Thêm tệp tiêu đề vào main.cpp bằng #include "Product.h".
Tự làm thử
#include <iostream>
#include <string>
#include "Product.h"
using namespace std;
int main() {
// Đọc đầu vào
string name1;
double price1;
string name2;
double price2;
getline(cin, name1);
cin >> price1;
cin.ignore();
getline(cin, name2);
cin >> price2;
// TODO: In ra thống kê kho hàng ban đầu bằng Product::getInventoryStats()
// TODO: Tạo một khối lệnh với dấu ngoặc nhọn
// Bên trong khối lệnh:
// - Tạo một Product với name1 và price1
// - In ra thông tin của đối tượng bằng getInfo()
// - In ra thống kê kho hàng hiện tại
// TODO: Sau khi khối lệnh kết thúc, in ra "After first scope:"
// Sau đó in ra thống kê kho hàng
// TODO: Tạo một Product khác với name2 và price2
// In ra thông tin của đối tượng và thống kê kho hàng
// TODO: Tạo Product thứ ba có tên "Bonus" và giá 5.00
// In ra thống kê kho hàng cuối cùng
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