You are on page 1of 18

RETURN CLASS

I) MỘT SỐ PHƯƠNG THỨC KHỞI TẠO VÀ HÀM TRONG CLASS


Ở phần trước tôi đã giới thiệu cho các bạn về một số cách dùng hàm trong class. Và để dễ hiểu,
tôi đã đưa mọi thuộc tính (các biến) của class về dạng public nhưng bản thân class ở dạng
public thì lại giống hệt struct và không mang những đặc tính của class thực sự. Do class thì cần
che giấu thông tin. Vì vậy hôm nay chúng ta sẽ thao tác lại với class theo đúng nghĩa của nó.
a) Một số đặc tính của hàm và biến trong class
Thứ nhất : Mọi thuộc tính (biến) hay phương thức (hàm) của class sẽ đều mặc định ở
dạng private (riêng tư).
Do đó ở bài trước, tôi đã phải thêm từ khóa public : phía trên các biến để chúng ta dễ thao tác
như đối với struct.
Vậy điều gì sẽ xảy ra khi không có dòng public:
ví dụ sau sẽ làm bạn dễ hình dung :

Vậy “riêng tư” có nghĩa là gì ?


Ở phía không có từ khóa public thì hệ thống sẽ báo lỗi vi phạm quyền riêng tư (private) của dữ
liệu.
Nói cách khác, khi các biến của class nằm ở dạng riêng tư thì chúng ta không thể động đến một
cách trực tiếp được. Dù là nhập vào hay xuất ra chỉ để xem cũng đều không được.
Điều này về sau sẽ nhằm mục đích ngăn chặn việc rò rỉ dữ liệu. Cho dù một kẻ trộm có ăn cắp
được khối dữ liệu đó thì cũng không có cách nào để mở ra xem hay thay đổi nội dung của nó.
Thứ 2 : Chỉ có những hàm thuộc (nằm trong) class hoặc hàm bạn (friend) thì mới có thể
truy cập được các thuộc tính của class.
Trước tiên chúng ta sẽ nói về những hàm nằm trong class.
Do 2 tính chất (in đậm) kể trên mà chúng ta thường bắt gặp những hàm vô vị như get(), set() vv
mà nếu không hiểu rõ class thì sẽ chẳng biết tại sao lại có mấy cái hàm tồ lô như này.

Như đã nói, chính vì không thể nào động đến một cách trực tiếp các biến của class nên ta buộc
phải thông qua các hàm để cài đặt giá trị (set) cho biến hoặc lấy giá trị của biến ra để sử dụng
(get) hoặc xem. Nói cách khác, các hàm này chính là chìa khóa để giải mã class.
Thông thường thì các hàm này sẽ được đặt tên phức tạp (như password) chứ không đơn giản là
get, set (vì chìa khóa mà đơn giản thì còn ý nghĩa gì). Tuy nhiên ở đây các bạn chỉ đang học và
làm quen với class cơ bản nên sẽ đặt là get, set cho nhanh.
ví dụ :
Vậy giờ chúng ta sẽ viết thêm 2 hàm getten() và setten()
để lấy giá trị hoặc gán giá trị cho biến ten trong class sv
Lưu ý :
phải có từ khóa public: thì các hàm
getten() vơi setten() mới có thể được
gọi ra trong hàm main. Các bạn
nên tự thử lại trên upcoder nhưng không có từ khóa
public: để xem chuyện gì sẽ xảy ra.
Luyện Tập :
Tự viết lại class trên và thêm các hàm get set với các biến năm sinh hoặc điểm. Sau đó dùng các
hàm này nhập năm sinh và điểm cho các biến trên.
b) Phương thức khởi tạo
Khởi tạo là gì ?
Khởi tạo có nghĩa là tạo ra những giá trị ban đầu cho các biến (thuộc tính) nằm bên trong class
mà không cần phải dùng hàm gán hay bất cứ phương thức nào khác.
Ví dụ : Khi bạn khai báo: int t; trong hàm main thì giá trị của t sẽ khá bất định và không thể biết
trước bằng bao nhiêu.

Chúng ta có thể thấy biến t nhận các giá trị ngẫu nhiên bất kỳ, điều này khá bất tiện. Để cố định
giá trị của t thì ta thường lúc khai báo ta sẽ gán t cho một giá trị nào đó chúng ta muốn bằng các
câu lệnh như int t=0; int t=10 vv
Và vì kiểu int là kiểu dữ liệu cơ bản và chúng ta không thể nào thay đổi phương thức hoạt động
của nó nên chúng ta buộc phải chấp nhận điều này.
Nhưng class lại là thế giới của chúng ta, ta tạo ra class do đó ta có toàn quyền quyết định đối
với nó. Vì vậy chúng ta sẽ tạo ra một kiểu dữ liệu mà chỉ cần khai báo thôi là biến của nó sẽ
được nhận các giá trị mặc định thay vì giá trị ngẫu nhiên bất kỳ hay còn gọi là giá trị rác.
Cấu trúc của phương thức khởi tạo:
Có tên trùng với tên class
<class name>(có hoặc không có tham số)
{ //khối lệnh}
Ví dụ chúng ta tạo ra một kiểu dữ liệu phân số và chúng ta muốn mặc định tử của phân số này
phải bằng 0 và mẫu của nó bằng 1. Ta sẽ làm như sau:
Dễ thấy mặc dù chúng ta không động chạm Tất nhiên chúng ta cũng có thể cho tử bằng
gì đến biến a tuy nhiên các giá trị tử mẫu của a 3 mẫu bằng 8 hay bất cứ giá trị nào khác
đã mang sẵn giá trị mặc định

Luyện tập
Viết lại class trên, thêm các hàm nhập xuất và set tử mẫu. Sau đó dùng hàm nhập để nhập vào
giá trị cho tử mẫu của biến a sau đó dùng hàm xuất hoặc get tử mẫu để xuất các giá trị vừa nhập
ra màn hình theo định dạng phân số (tu/mau).

Phương thức khởi tạo như trên được gọi là khởi tạo mặc định.
Ngoài ra chúng ta còn có phương thức khởi tạo có tham số.
Ví dụ sẽ dễ hiểu hơn mọi lời diễn giải:
Tương tự muốn tạo ra một phân số có tử =10, mẫu bằng 6 thì cũng chỉ cần khai báo
phanso a(10,6);
Và ngoài ra chúng ta sẽ có phương thức khởi tạo sao chép như sau:
Phân số b dù chưa được nhập gì nhưng vẫn xuất ra được 7/9 vì đã sao chép từ phân số d trước
đó đã được nhập 2 giá trị này.
c) Một số ví dụ về hàm cơ bản trong class
 Cộng phân số :
 So sánh bằng nhau giữa hai phân số
 Hàm rút gọn
Trước tiên ta cần xây dựng một hàm tìm ước chung lớn nhất giữa 2 số nguyên.
Có nhiều cách, tuy nhiên ở đây tôi chọn cách dùng giải thuật Euclid bằng đệ quy.

Rồi sau đó rút gọn phân số bằng cách cho cả tử và mẫu chi cho ước chung lớn nhất của chúng
Luyện tập :
Đến giờ thì các bạn có thể làm tất cả các bài về

II)QUÁ TẢI TOÁN TỬ TRONG CLASS


a) Quá tải là gì ?
Thực chất thì nghĩa của từ “Quá tải” chẳng mấy liên quan gì đến “quá tải” :)), nó chỉ đơn thuần
là cách dịch khô khan từ tiếng Anh (Overloading) sang tiếng Việt nhưng được sử dụng rộng rãi
nên ta đành chấp nhận cụm từ Việt hóa này như một danh từ riêng.
Nói một cách dễ hiểu nhất thì “quá tải” toán tử chính là ta tự mình “định nghĩa” lại cách sử
dụng những toán tử (cộng, trừ, nhân, chia so sánh vv..) đối với kiểu dữ liệu mà chúng ta vừa
tự tạo ra thông qua class.
Ví dụ một toán tử cộng (+). Nếu ta cộng một số nguyên cho một số nguyên, ta sẽ được tổng 2 số
đó và gần như ta không thể nào can thiệp gì được vì nó là kiểu dữ liệu nguyên tố. Tuy nhiên giờ
đây ta là người tạo ra kiểu dữ liệu của chính mình nên ta có toàn quyền quyết định đối với cách
nó cộng trừ nhân chia hay abc xyz bất kỳ.
Giả sử ta có một class thitheo, trong đó chỉ có một biến số thực biểu diễn khối lượng thịt heo và
một biến số nguyên biểu diễn cho giá tiền như bên dưới :
Và khi ta muốn biến c là tổng của a và b
như dòng 15, thì theo các bạn, kết quả
sẽ như thế nào ?
Liệu c.kg sẽ bằng bao nhiêu sau phép cộng ?
hoặc biến c.gia sẽ bằng bao nhiêu ?
Liệu c.kg sẽ bằng a.kg + b.kg và
c.gia = a.gia + b.gia chăng ?

Và đây là câu trả lời của trình biên dịch :


Vietsub ra đại loại là, chúng tôi chả hiểu các anh muốn cộng là cộng cái gì ? Các anh muốn
chúng tôi + cái a.kg vs b.kg hay a.kg vs b.gia hay thậm chí là a.kg vs a.gia. Và cộng xong
rồi thì chúng tôi cũng chẳng biết trả lại(return) cái gì, các anh muốn trả về số nguyên, số thực
hay thitheo thì chúng tôi cũng chả biết. Do đó các anh phải tự định nghĩa ‘+’ là thế nào thì
chúng tôi mới theo đó mà làm được :))

Do đó chúng ta cần tự định nghĩa lại theo ý chúng ta. Và vì là theo ý chúng ta nên chúng ta
muốn sao cũng được. Ví dụ chúng ta muốn toán tử + của kiểu dữ liệu thitheo sẽ chẳng làm gì cả
ngoài việc xuất ra màn hình câu “Tôi ăn thịt heo” thì ta sẽ thao tác như sau :

Dòng 10 chính là thao tác Định Nghĩa


toán tử + Nó cũng giống hệt như cách
chúng ta viết hàm chỉ là phần tên hàm
sẽ được thay thế là operator (toán tử).
Trước tên hàm cũng sẽ có trị trả về, trong
trường hợp này là void, sau từ khóa
operator sẽ là toán tử chúng ta cần định nghĩa
trường hợp này là dấu +.
Phần tham số sẽ luôn mang theo ít hơn 1 ngôi
so với số ngôi của toán tử.
Ngôi là gì ?
Ngôi là số biến cần thiết để toán tử hoạt động.
Ví dụ toán tử nhân (*) nếu ta viết : a* thì chẳng
có ý nghĩa gì cả, a nhân là nhân mấy ?
hoặc *b cũng vậy. Do đó phải là a*b thì mới
đầy đủ ý nghĩa và toán tử có thể hoạt động bình
thường. Do đó toán tử * cần 2 ngôi.
Tương tự các toán tử + - / % cũng là toán tử 2 ngôi
Quay lại phía trên 1 chút .
Sở dĩ phần tham số sẽ mang ít hơn một ngôi là vì bản thân
chính các thuộc tính (biến) trong class sẽ đóng vai là ngôi còn lại, cũng giống như hàm trong
class. Điều này sẽ được nói rõ hơn về sau.
Quay lại đoạn code phía trên, ta có thể nhận thấy các phép cộng ở dòng 19 20 21 lại tương
đương với việc xuất ra những dòng chữ vô nghĩa “Tôi ăn thịt heo” chứ chẳng làm thao tác gì
khác. Điều này càng nhấn mạnh rằng, các bạn là chủ cuộc chơi, muốn định nghĩa sao là chuyện
của các bạn.
Hoặc thâm chí các bạn cũng có thể viết những thứ vô nghĩa như sau :

Rồi bây giờ chúng ta sẽ bắt đầu định nghĩa nghiêm túc một chút. Giả sử đề bài sẽ yêu cầu các
bạn khi cộng hai biến thitheo thì cần trả về kiểu dữ liệu thitheo nhưng biến kg của kết quả sẽ
mang giá trị tổng của hai biến được cộng và biến gia thì thành trung bình cộng của hai biến kể
trên ta sẽ thao tác như sau :
Tới đây tôi sẽ nói rõ về cách truyền tham số trong quá tải toán tử đối với class.
Thực chất thì quá tải cũng chính là một hàm, nhưng khi bạn muốn dùng hàm thông thường thì
bạn gọi tên hàm ra truyền các tham số (nếu có) vào, thì đối với quá tải toán tử các bạn chỉ cần
viết toán tử ra và cũng truyền tham số vào, tuy nhiên tham số trong quá tải toán tử sẽ ở dạng
ngôi chứ không được truyền trong dấu () như đối với hàm.
ví dụ toán tử nhân (+) khi bạn muốn dùng thì bạn sẽ viết như sau : a + b
Lúc này a và b chính là 2 ngôi và đóng vai trò như 2 tham số được truyền vào hàm quá tải cộng.
Vậy a sẽ được truyền vào đâu và b sẽ được truyền vào đâu ? Ta sẽ đi vào ví dụ để làm rõ hơn.
Ví dụ :
Để tránh nhầm lẫn tôi sẽ viết lại hàm quá tải cộng như sau : từ dòng 11 đến 16 (hình trái)
Ở dòng 22 khi chúng ta viết “a +” thì đây chính là hành vi gọi hàm quá tải (nó tương tự khi ta
viết “a.tong(b)” trong gọi hàm thông thường, “a.tong” trong tình huống này chính là gọi hàm
tong() trong a ra).
Quay lại “a +”, khi gọi lệnh này thì toán tử + sẽ chui vào bên trong a và thực hiện các lệnh của
hàm operator + bên trong a (dòng 11). Lúc này tham số x sẽ nhận giá trị của b (x sao chép giá
trị của b).
Và vì là đang bên trong a nên các biến kg, gia (được tô vàng) chính là của a. Nói cách khác, các
biến này chính là a.kg và a.gia, tuy nhiên do đang bên trong sẵn nên ko cần viết rõ.
Sau khi thực hiện xong các lệnh thì hàm quá tải + sẽ trả về kiểu dữ liệu thitheo. Và cũng như tất
cả các hàm khác, ta cũng có thể đối xử với quá tải như 1 hàm bằng cách thứ 2 (hình bên phải)
Trực tiếp dùng dấu chấm (.) để xuất luôn chứ không cần thông qua biến c như hình bên trái:
Đến đây có lẽ các bạn đã hiểu tại sao quá tải bên trong class thì số tham số sẽ bằng số ngôi giảm
đi một rồi chứ. Nếu vẫn không hiểu thì trời cứu :))

Tôi sẽ ví dụ thêm một vài cách quá tải khác để các bạn đỡ bỡ ngỡ sau này :

Tôi muốn quá tải toán tử nhân cho class thitheo tuy nhiên trị trả về là số nguyên và là tổng của
giá hai loại thịt, ta sẽ làm như sau :

Hoặc tôi muốn quá tải phép trừ giữa kiểu dữ liệu thitheo với một số thực và kết quả trả về là
kiểu dữ liệu thitheo nhưng biến kg sẽ bị trừ đi một lượng bằng số thực kia.
Hoặc quá tải toán tử chia giữa kiểu dữ liệu thitheo và số nguyên nhưng trả về một số thực là kết
quả phép chia giữa biến kg và số nguyên kia :

Hoặc tôi muốn quá tải toán tử +


+, trị trả về là kiểu dữ liệu
thitheo nhưng khối lượng được
cộng thêm 1

Hoặc quá tải về


phép so sánh giữa 2
biến thitheo, ưu tiên
khối lượng sau đó là
đến giá. kg nào
nặng hơn thì lớn
hơn, sau đó là giá
bên nào cao hơn thì
lớn hơn.
Và tương tự như các toán tử khác, các bạn cũng thử so sánh thịt heo với số nguyên, với số thực
hoặc abc.. bất kỳ cái gì các bạn muốn. Hãy thử tự làm ví dụ để hiểu rõ vấn đề.
Tóm lại qua các ví dụ trên các bạn có thể thấy, bạn muốn làm gì với kiểu dữ liệu của mình cũng
được, tuy nhiên cần đúng cú pháp. Các bạn cần chú ý kỹ các trị trả về, số tham số để nắm rõ về
quá tải trong class.

Luyện tập :
1/ Để kiểm tra khả năng hiểu bài . Các bạn cần trả lời câu hỏi sau : Trong các ví dụ về quá tải
phía trên, tại sao đôi khi tôi dùng hàm xuat() để xuất và đôi khi lại dùng cout để xuất ?
2/Tạo một class sv có các thuộc tính (biến) ten (string), diemtoan (float), diemvan (float).
Xây dựng các hàm nhập xuất.
a) Quá tải toán tử cộng trả về kiểu dữ liệu sv, biến ten sẽ mang tên của ai có diemtoan cao hơn
trong hai biến thành phần, biến diemtoan và điểm văn cũng sẽ lấy giá trị của biến có điểm toán
cao hơn.
b)Quá tải toán tử trừ trả về kiểu dữ liệu sv như câu a, tuy nhiên thay vì so điểm toán thì lần này
chúng ta so điểm văn.
c) Quá tải toán tử nhân trả về kiểu số thực, là trung bình cộng của điểm toán từ hai biến thành
phần.
d) Quá tải toán tử chia trả về kiểu string là tên của người có thứ tự từ điển nhỏ hơn.
e) Xuất tên người có điểm toán cao nhất (sử dụng toán tử +).
f ) Xuất tên người có điểm văn cao nhất (Sử dụng toán tử -).
g) Xuất trung bình cộng điểm toán của 2 bạn có điểm toán và văn cao nhất kể trên.
h) Xuất tên của người có thứ tự từ điển nhỏ nhất.
Input nhập không biết trước số lượng nhiều dòng, mỗi dòng gồm thông tin tên (không khoảng
trắng) điểm toán, điểm văn cách nhau bởi khoảng trắng.
Output như các yêu cầu kể trên, xuất kết quả trên nhiều dòng.
Kể từ sau file này nếu đã thực hành tất cả các bài tập của những file trước thì các bạn bây giờ có
thể làm mọi bài tập của OOP trên upcoder cho đến chương quá tải. Và ngược lại nếu các bạn
chỉ đọc mà không thực hành thì chẳng ai cứu nổi các bạn.
Chúc các bạn may mắn!

You might also like