Hàm khởi tạo ủy quyền
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 23 trên 104.
Khi có nhiều hàm khởi tạo dùng chung logic khởi tạo, bạn có thể dùng hàm khởi tạo ủy quyền để tránh lặp code. Hàm khởi tạo ủy quyền gọi một hàm khởi tạo khác của cùng lớp trong danh sách khởi tạo.
class Player {
std::string name;
int health;
int level;
public:
// Hàm khởi tạo chính (constructor): thực hiện công việc chính
Player(std::string n, int h, int lvl)
: name(n), health(h), level(lvl) {}
// Ủy quyền cho hàm khởi tạo chính
Player(std::string n) : Player(n, 100, 1) {}
// Hàm khởi tạo mặc định cũng ủy quyền
Player() : Player("Unknown") {}
};Việc ủy quyền diễn ra trong danh sách khởi tạo bằng cách dùng tên lớp theo sau là các đối số. Khi gọi Player(), nó ủy quyền cho Player(std::string), rồi hàm này ủy quyền cho hàm khởi tạo có ba tham số. Chuỗi ủy quyền chạy từ hàm khởi tạo đích ngược về hàm khởi tạo ban đầu.
Có một quy tắc quan trọng: hàm khởi tạo ủy quyền không thể có thành viên nào khác trong danh sách khởi tạo. Việc ủy quyền phải là mục duy nhất:
class Item {
int id;
int value;
public:
Item(int i, int v) : id(i), value(v) {}
// Lỗi! Không thể vừa ủy quyền khởi tạo vừa khởi tạo thành viên
// Item(int i) : Item(i, 0), id(i) {}
// Đúng: chỉ ủy quyền khởi tạo
Item(int i) : Item(i, 0) {}
};Các hàm khởi tạo ủy quyền giúp tập trung logic khởi tạo vào một nơi, nhờ đó bạn dễ bảo trì code hơn và giảm nguy cơ mắc lỗi do lặp code.
Thử thách
DễHãy xây dựng một hệ thống cài đặt kết nối để minh họa cách các hàm khởi tạo ủy quyền có thể loại bỏ code trùng lặp bằng cách nối các lệnh gọi hàm khởi tạo với nhau.
Bạn sẽ tạo hai tệp để sắp xếp code:
Connection.h: Định nghĩa lớpConnectionđại diện cho một kết nối mạng với nhiều tùy chọn cấu hình. Lớp của bạn cần có:- Các thành viên riêng tư:
host(chuỗi),port(int),timeout(int tính bằng giây) vàsecure(bool) - Một hàm khởi tạo chính nhận cả bốn tham số và khởi tạo tất cả thành viên bằng danh sách khởi tạo. Hàm khởi tạo này cần in ra
"Connection configured: <host>:<port>" - Một hàm khởi tạo chỉ nhận
hostvàport, ủy quyền cho hàm khởi tạo chính, với giá trị mặc định củatimeoutlà30và giá trị mặc định củasecurelàfalse - Một hàm khởi tạo chỉ nhận
host, ủy quyền cho hàm khởi tạo hai tham số, với giá trị mặc định củaportlà80 - Một hàm khởi tạo mặc định, ủy quyền cho hàm khởi tạo một tham số, với giá trị mặc định của
hostlà"localhost" - Một phương thức
getInfo()trả về chuỗi theo định dạng:"<host>:<port> (timeout: <timeout>s, secure: <yes/no>)"
- Các thành viên riêng tư:
main.cpp: Minh họa chuỗi ủy quyền bằng cách tạo các kết nối với những hàm khởi tạo khác nhau. Đọc tên máy chủ và số cổng từ đầu vào, mỗi giá trị trên một dòng riêng, sau đó:- Tạo một
Connectionbằng hàm khởi tạo mặc định - Tạo một
Connectionchỉ bằng tên máy chủ từ đầu vào - Tạo một
Connectionbằng cả tên máy chủ và số cổng từ đầu vào - Tạo một
Connectionvới tên máy chủ và số cổng từ đầu vào, giá trị timeout là60và secure được đặt thànhtrue - In ra
getInfo()cho từng kết nối, mỗi kết quả trên một dòng
- Tạo một
Chuỗi ủy quyền diễn ra như sau: hàm khởi tạo mặc định gọi hàm khởi tạo một tham số, hàm này gọi hàm khởi tạo hai tham số, rồi hàm này gọi hàm khởi tạo chính có bốn tham số. Điều này có nghĩa là thông báo "Connection configured" được in ra một lần cho mỗi đối tượng được tạo, luôn từ hàm khởi tạo chính.
Đối với trạng thái bảo mật trong getInfo(), hãy in ra "yes" nếu secure là true, nếu không thì in ra "no".
Thêm tệp tiêu đề vào main.cpp bằng #include "Connection.h".
Tự làm thử
#include <iostream>
#include <string>
#include "Connection.h"
using namespace std;
int main() {
// Đọc đầu vào
string host;
int port;
cin >> host;
cin >> port;
// TODO: Tạo một Connection bằng hàm khởi tạo mặc định
// TODO: Tạo một Connection chỉ với host từ đầu vào
// TODO: Tạo một Connection với cả host và port từ đầu vào
// TODO: Tạo một Connection với host và port từ đầu vào, timeout=60, secure=true
// TODO: In ra getInfo() cho từng kết nối, mỗi kết nối một dò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