Giáo trình C++ - Lập trình hướng đối tượng (Phần 2)

Chương 13. Lập trình hướng đối tượng  
CHƯƠNG 13. LẬP TRÌNH HƯỚNG ĐỐI  
TƯỢNG  
Lịch sử hình thành  
Trước khi kĩ thuật lập trình hướng đối tượng ra đời, con người đ~ trải qua  
các thời kì lập trình tuyến tính, lập trình hướng thủ tục.  
1. Lập trình tuyến tính  
M|y tính đầu tiên được lập trình bằng mã nhị phân, sử dụng các công  
tắc cơ khí để nạp chương trình. Cùng với sự xuất hiện của các thiết bị lưu  
trữ lớn và bộ nhớ m|y tính có dung lượng lớn, nên các ngôn ngữ lập trình  
cấp cao bắt đầu xuất hiện. Các ngôn ngữ lập trình n{y được thiết kế làm  
cho công việc lập trình trở nên đơn giản hơn. C|c chương trình ban đầu  
chủ yếu liên quan đến tính toán, chúng tương đối ngắn. Chúng chủ yếu  
Nhược điểm:  
o Nếu ta cần sử dụng một đoạn lệnh n{o đó nhiều lần, thì ta phải sao chép  
nó nhiều lần.  
o Không có khả năng kiểm soát phạm vi nhìn thấy của biến.  
o Chương trình dài dòng, khó hiểu, khó nâng cấp.  
2. Lập trình hướng thủ tục  
Với những nhược điểm trên, đòi hỏi có một ngôn ngữ lập trình mới thay  
Về bản chất, chương trình được chia nhỏ thành c|c modul (đơn vị chương  
trình). Mỗi đơn vị chương trình chứa các hàm hay thủ tục (nên gọi là  
hướng thủ tục). Tuy tách rời thành các modul riêng biệt, nhưng ngôn ngữ  
lập trình hướng thủ tục vẫn đảm bảo thông tin thông suốt giữa các modul  
nhờ v{o cơ chế hoạt động của h{m, cơ chế truyền theo tham biến và tham  
trị. Với lập trình hướng thủ tục, một chương trình lớn có thể được chia nhỏ  
Trang | 120  
Chương 13. Lập trình hướng đối tượng  
th{nh c|c modul, để mỗi lập trình viên có thể đảm nhận. Tiêu biểu trong số  
này là C, Pascal.  
Nhược điểm:  
o Các hàm và thủ tục thường gắn kết với nhau, nếu muốn nâng cấp  
chương trình, thường phải chỉnh sửa tất cả các hàm và thủ tục liên  
quan.  
o Không phù hợp với xu thế hiện đại vì không mô tả được thực thể trong  
cuộc sống thực.  
3. Lập trình hướng đối tượng  
a. Giới thiệu  
Với xu thế hiện đại, ngôn ngữ lập trình hướng đối tượng đ~ ra đời. Cơ  
sở của lập trình hướng đối tượng l{ đối tượng. Đối tượng là sự thể hiện của  
một thực thể trong thế giới thực. Một thực thể trong thế giới thực thường  
có: c|c đặc trưng v{ c|c h{nh động. Ví dụ: con người trong thế giới thực có  
c|c đặc trưng như - tên gọi, tuổi, màu tóc, màu mắt, m{u da… v{ c|c h{nh  
chính xác các sự vật, con người trong thế giới thực.  
Bây giờ, ta sẽ xét một vài ví dụ để cho thấy sự cần thiết của lập trình  
hướng đối tượng.  
Ví dụ 1. Chúng ta muốn xây dựng một chương trình quản lý sinh viên.  
Khi đó, ta cần lưu trữ c|c thông tin liên quan đến đối tượng sinh viên này:  
họ tên sinh viên, mã số sinh viên, ng{y th|ng năm sinh, quê qu|n, điểm các  
môn, điểm tổng kết,…. v{ rất nhiều thông tin khác liên quan. Sau khi kết  
thúc năm học, sinh viên sẽ nhận được đ|nh gi| kết quả học tập của mình.  
Chúng ta cần có phương thức tiếp nhận kết quả để sinh viên đó có thể phản  
học tập, tham gia vào các hoạt động của trường, của khoa… đó l{ những  
h{nh động mà mỗi sinh viên cần thực hiện.  
Ví dụ 2. Chúng ta sẽ điểm qua một số tính năng trong chương trình soạn  
thảo văn bản Word của Microsoft. Chúng ta sẽ thảo luận về c|c đối tượng  
Drawing trong Word. Mỗi đối tượng đều có các thuộc tính: màu viền, dạng  
đường viền, kích thước viền, màu sắc viền, màu nền, có văn bản hay không  
trong đối tượng drawing…Khi chúng ta biến đổi hình dạng của mỗi đối  
T r a n g | 121  
Chương 13. Lập trình hướng đối tượng  
tượng: kéo giãn, làm lệch xiêng, quay vòng… chúng ta cần đưa ra một  
thông điệp để c|c đối tượng hình thể n{y thay đổi theo. C|c h{nh động này  
thuộc quyền sở hữu của đối tượng.  
Trong hai ví dụ minh họa trên, chúng ta thấy rằng hướng tiếp cận theo  
lập trình hướng đối tượng là rất gần gũi với cuộc sống thực. Chúng ta  
không quan t}m đến những khía cạnh không cần thiết của đối tượng,  
chúng ta chỉ tập trung v{o c|c đặc trưng v{ c|c h{nh động của đối tượng.  
Kể từ thời điểm này trở đi, chúng ta sẽ gọi các đặc trưng của đối tượng là  
các thuộc tính thành viên của đối tượng đó (hoặc dữ liệu thành viên, biến  
thành viên của đối tượng) v{ c|c h{nh động của đối tượng l{ c|c phương  
thức thành viên (hay hàm thành viên) của đối tượng. Các cách gọi dữ liệu  
thành viên, thuộc tính thành viên, biến thành viên hay thuộc tính (tương  
ứng phương thức th{nh viên, h{m th{nh viên, phương thức) là không có  
sự phân biệt. Tôi chỉ đưa ra nhiều cách gọi kh|c nhau để chúng ta có thể  
quen khi tham khảo các giáo trình khác nhau. Bởi lẽ, nhiều giáo trình chọn  
lựa các cách gọi khác nhau. Các cách gọi n{y cũng tùy thuộc vào ngôn ngữ  
thành viên – member function, trong khi đó, c|c ngôn ngữ như Java, Delphi  
hay C# lại sử dụng khái niệm phương thức – method và thuộc tính –  
property). Khái niệm thành viên sẽ áp dụng cho cả dữ liệu thành viên lẫn  
hàm thành viên.  
Phương ch}m của lập trình hướng thủ tục theo gi|o sư Niklaus Wirth  
Chương trình = Cấu trúc dữ liệu + Giải thuật  
Còn phương ch}m của lập trình hướng đối tượng là  
Chương trình = Đối tượng + Dữ liệu  
Tiêu biểu trong số này là C++, Java, C#, Delphi, Python…  
phân tích và thiết kế c|c đối tượng. Từ sơ đồ cấu trúc nhận được, chúng ta  
có thể xây dựng nên chương trình. Chi tiết về cách thức phân tích và thiết  
kế đối tượng, chúng ta sẽ được tìm hiểu kĩ hơn trong học phần phân tích  
thiết kế hệ thống thông tin. Trong nội dung của giáo trình này, chúng ta chỉ  
thảo luận một phần nhỏ, để giúp các bạn có thể xây dựng nên một cấu trúc  
chương trình theo hướng đối tượng. Sơ đồ cấu trúc trong lập trình hướng  
đối tượng được sử dụng phổ biến l{ sơ đồ mô tả trên ngôn ngữ UML  
(Unified Modeling Languages). UML là ngôn ngữ chuyên dùng để mô hình  
T r a n g | 122  
Chương 13. Lập trình hướng đối tượng  
hóa c|c đối tượng. Nó không chỉ được áp dụng trong lập trình, m{ còn được  
sử dụng rộng r~i trong c|c lĩnh vực khác của cuộc sống. Trong UML có  
nhiều dạng sơ đồ được hoạch định. Nhưng trong phạm trù của lập trình  
hướng đối tượng, sơ đồ lớp là sự mô tả gần gũi nhất. Do đó, tôi sẽ trình bày  
cách xây dựng một chương trình được mô tả bằng sơ đồ lớp.  
Một số kí hiệu cần lưu ý trong UML. Trước khi tìm hiểu cách mô hình hóa  
một bài toán trong UML, chúng ta cần tìm hiểu một số kí hiệu được sử dụng  
trong UML. Các kí hiệu này có thể khác nhau trong nhiều chương trình mô  
phỏng. Những kí hiệu mà tôi sử dụng ở đ}y l{ kí hiệu dùng trên Visual  
Studio 2010.  
Kí hiệu  
Ý nghĩa  
Lớp  
Thuộc tính hoặc phương thức private  
Thuộc tính hoặc phương thức protected  
Thuộc tính hoặc phương thức public  
hướng về lớp cơ sở. Chiều còn lại luôn  
chỉ vào lớp con.  
Thuộc tính  
Phương thức  
Phân tích và thiết kế mô hình. Việc phân tích và thiết kế một mô hình là  
toán phân tích và thiết kế, không phải chỉ có duy nhất một mô hình kết quả,  
mà có thể có một vài, thậm chí là nhiều mô hình khác nhau. Tuy nhiên, công  
việc chọn lựa một mô hình tối ưu l{ điều cần thiết. Trong nội dung giáo  
trình này, tôi chỉ giới thiệu sơ qua về cách hoạch định một mô hình. Chúng  
ta sẽ không đi sâu nghiên cứu vấn đề này. Trong học phần phân tích và thiết  
kế hệ thống thông tin sẽ trình bày chi tiết và cụ thể hơn.  
Các bước phân tích và thiết kế. Để phân tích và thiết kế một mô hình  
hướng đối tượng, cần thực hiện c|c giai đoạn sau đ}y:  
T r a n g | 123  
Chương 13. Lập trình hướng đối tượng  
- Bước 1. Mô tả bài toán. Một bài toán sẽ được miêu tả dưới dạng ngôn ngữ  
tự nhiên. Nó chủ yếu dựa vào yêu cầu của khác hàng và sự trợ giúp khách  
hàng.  
- Bước 2. Đặc tả các yêu cầu. Sau khi phân tích các nhân tố tham gia vào  
trong mô hình, ta cần tiến h{nh xem xét c|c t|c nh}n t|c động vào từng  
nhân tố. Mối quan hệ giữa các nhân tố…  
- Bước 3. Trích chọn đối tượng. Sau khi tổng hợp các tác nhân và nhân tố  
trong mô hình. Chúng ta cần tiến hành lựa chọn chúng. Việc loại bỏ các  
nhân tố và tác nhân không cần thiết là rất quan trọng. Nó sẽ giúp cho mô  
hình tập trung vào các nhân tố quan trọng và cần thiết, tránh phân tích và  
thiết kế tràn lan.  
- Bước 3. Mô hình hóa các lớp đối tượng. Sau khi chọn lựa c|c đối tượng  
cần thiết. Chúng ta phân tích từng đối tượng. Khi ph}n tích đối tượng, ta cần  
lưu ý tập trung vào những thứ cốt lõi của mỗi đối tượng, tr|nh đưa v{o  
những phương thức và thuộc tính không cần thiết, không quan trọng của  
đối tượng – đó chính l{ tính trừu tượng hóa của dữ liệu. Khi phân tích,  
chúng ta cũng cần lưu ý đến các tính chất chung của từng đối tượng. Nếu  
c|c đối tượng có nhiều tính chất chung, chúng ta nên xây dựng một đối  
tượng mới, chứa các tính chất chung đó, mỗi đối tượng còn lại sẽ thừa kế từ  
tượng phải có c|c phương thức và thuộc tính riêng lẫn c|c phương thức và  
thuộc tính chia sẻ. C|c phương thức riêng chỉ có bản th}n đối tượng mới có  
quyền thay đổi. C|c phương thức chia sẻ có thể được truy cập bởi đối tượng  
khác theo các mức khác nhau.  
- Bước 5. Xây dựng và kiểm thử mô hình. Bắt tay vào xây dựng mô hình. Ở  
đ}y, chúng ta sử dụng ngôn ngữ UML để mô tả. Sau khi xây dựng xong mô  
hình, cần tiến hành kiểm thử mô hình. Kiểm thử các mô hình trong các tình  
huống thực tế là cần thiết để đảm bảo rằng mô hình nhận được là phù hợp,  
trước khi bắt tay vào viết chương trình.  
Trên đ}y, chỉ là những bước đề nghị để chúng ta có một cái nhìn tổng quát  
khi phân tích và thiết kế. Có thể có nhiều c|ch để phần tích và thiết kế một  
Mỗi khi có một lỗi xuất hiện, chúng ta cũng cần biết khoanh vùng để thu  
nhỏ phạm vi phát hiện lỗi.  
Chúng ta sẽ lấy một ví dụ nhỏ. Ph}n tích hướng đối tượng mô hình quản  
lý cửa hàng bán xe đạp. Trong mô hình này, ta cần quản lý c|c nhóm đối  
tượng sau: đối tượng xe đạp, đối tượng chi nhánh bán hàng, đối tượng  
kh|ch h{ng v{ đối tượng nhân viên bán hàng.  
- Đối tượng xe đạp: chúng ta cần quản lý mã số xe (mã số gồm hai phần:  
phần id chi nhánh bán hàng + mã số vạch), loại xe, màu sắc, gi| b|n, nước  
T r a n g | 124  
Chương 13. Lập trình hướng đối tượng  
sản xuất (các thuộc tính chung). Đối tượng xe đạp địa hình: số b|nh răng,  
c|ch lên răng (bằng tay/tự động), chống sooc. Đối tượng xe đạp du lịch: xe  
đơn/đôi, tự động (hỗ trợ tự chạy bằng điện hay không), chiếu sáng  
(có/không). Xe đua thể thao: điều chỉnh độ cao (có/không), các chế độ đạp  
(đạp thư giản, đạp tăng tốc, đạp chậm…).  
- Đối tượng khách hàng và nhân viên bán hàng: họ và tên, số CMND. Đối  
tượng khách hàng: cách thức thanh toán (tiền mặt/chuyển khoản), cách  
thức giao hàng (nhận tại chỗ/ đưa đến tận nh{). Đối tượng nhân viên bán  
h{ng: id chi nh|nh b|n h{ng, ng{y th|ng năm sinh, quê qu|n, địa chỉ, mã số  
thuế, lương…  
- Đối tượng chi nh|nh b|n h{ng: id chi nh|nh b|n h{ng, địa chỉ.  
Nếu yêu cầu quản lý nhiều hơn c|c thông tin của từng đối tượng, khi đó ta  
cần bổ sung thêm các thuộc tính tương ứng này.  
Đối với c|c phương thức thực thi h{nh động, tương ứng với mỗi thuộc tính,  
ta có hai phương thức để chỉ định và tiếp nhận. Ví dụ, đối tượng nhân viên,  
có họ v{ tên. Tương ứng với thuộc tính n{y, ta có phương thức chỉ định để  
đặt tên cho nh}n viên (đặt tên là thiết lập tên gọi trong phần mềm quản lý)  
và tiếp nhận tên khi có yêu cầu.  
Đối tượng kh|ch h{ng có phương thức quyết định (quyết định thực hiện  
h{ng còn đầy thì không tiếp nhận thêm).  
Theo như ph}n tích ở trên, đối tượng xe đạp l{ đối tượng chung. C|c đối  
tượng xe đạp thể thao, xe đạp du lịch, xe đạp địa hình kế thừa từ lớp xe đạp.  
Đối tượng con người để quản lý thông tin chung. V{ c|c đối tượng nhân  
viên và khách hàng thừa kế từ lớp con người. Cuối cùng l{ đối tượng chi  
nh|nh b|n h{ng. Theo như c|ch ph}n tích n{y, ta có sơ đồ lớp như sau:  
Hình 20 – Minh họa sơ đồ lớp  
T r a n g | 125  
Chương 13. Lập trình hướng đối tượng  
Trong sơ đồ n{y, c|c phương thức và thuộc tính của mỗi lớp đối tượng như  
đ~ ph}n tích ở trên. Để tr|nh rườm r{, c|c phương thức chỉ biểu diễn sơ  
lược.  
Lớp và đối tượng  
Lớp là sự biểu diễn của đối tượng trong lập trình v{ ngược lại đối  
tượng là sự thể hiện của lớp. Một đối tượng gồm có: thuộc tính v{ phương  
thức. Chúng ta có thể xem một lớp như l{ một kiểu dữ liệu, còn đối tượng là  
biến. Lớp được khai báo nhờ từ khóa class  
class tên_lớp{  
các_thuộc_tính  
các_phương_thức  
} [tên_đối_tượng];  
tên_lớp: là tên của lớp.  
tên_đối_tượng: là tên của đối tượng.  
Khai báo lớp tương đối giống khai báo struct.  
khai b|o như khai b|o h{m. Chúng có thể được chỉ định bằng một trong ba  
từ khóa: private, protected và public.  
o private: các thành viên (biến thành viên hoặc hàm thành viên) của cùng  
một lớp hoặc từ một hàm bạn của nó có thể truy cập.  
o protected: các thành viên của cùng một lớp hoặc từ một hàm bạn của nó  
hoặc từ một lớp dẫn xuất của nó hoặc bạn của một lớp dẫn xuất của nó  
đều có thể truy cập. Mức truy cập lớn nhất trong trường hợp này là bạn  
của lớp dẫn xuất. Chúng ta sẽ thảo luận thêm trong những phần sau.  
o public: các thành viên có thể truy cập lẫn nhau từ mọi lớp.  
class Humans{  
string name;  
int age;  
public:  
void setName(string);  
void setAge(string);  
string getName(void);  
int getAge(void);  
T r a n g | 126  
Chương 13. Lập trình hướng đối tượng  
}man;  
Humans là tên lớp, chứa các thuộc tính l{ name v{ age, chúng không được  
chỉ định từ khóa, nên private sẽ được sử dụng. C|c phương thức setName,  
setAge, getName và getAge được chỉ định là public. Trong trường hợp này,  
man là một đối tượng thể hiện của lớp Humans.  
Sau khi khai báo lớp, ta cần bổ sung phần thân lớp – tương ứng với các hàm  
thành viên. Hoặc ta có thể bổ sung trực tiếp vào trong lớp – tương tự khai  
báo hàm trực tiếp, hoặc sử dụng khai báo prototype. Đối với khai báo  
prototype, để x|c định một phương thức là của một lớp n{o đó, ta sử dụng  
toán tử phạm vi :: theo sau tên lớp.  
Chương trình  
Kết quả  
#include<iostream>  
The man: Jack, age 21  
using namespace std;  
class Humans{  
string name;  
int age;  
void setAge(int);  
string getName(void);  
int getAge(void);  
};  
void Humans::setName(string s){  
name = s;  
}
void Humans::setAge (int a){  
age = a;  
}
string Humans::getName(void){  
int Humans::getAge(void){  
return age;  
}
int main(){  
Humans man;  
man.setName(“Jack”);  
man.setAge(21);  
cout<<”The man: “<<man.getName()<<”, age  
T r a n g | 127  
Chương 13. Lập trình hướng đối tượng  
“<<man.getAge();  
return 0;  
}
Giải thích: Hàm setName sẽ gán biến s cho th{nh viên name, tương tự cho  
hàm setAge sẽ gán biến a cho thành viên age. Hàm getName trả về dữ liệu  
nhận được từ thành viên name và hàm getAge – nhận được dữ liệu từ  
thành viên age. Hai phương thức setName và setAge gọi l{ phương thức  
setter. Phương thức getName và getAge gọi l{ phương thức getter. Các  
phương thức setter dùng để nhập dữ liệu cho các thuộc tính thành viên, các  
phương thức getter dùng để nhận giá trị từ các thuộc tính th{nh viên đ~  
được nhập bởi setter (hoặc phương thức khởi tạo). Chúng ta cần lưu ý rằng,  
hàm main không thuộc lớp Humans, do đó, nó không thể truy xuất đến các  
thuộc tính th{nh viên trong trường hợp n{y, vì chúng được khai báo mặc  
định là private.  
Toán tử phạm vi :: sẽ giới hạn sự truy cập bất hợp lệ của những hàm  
không thuộc lớp Humans hay là bạn của Humans. Đ}y l{ một cách thức để  
quy địh mộpơng hức cphải lnh vin cmột lớp hay kô.  
chất, hai cách này không có sự khác biệt nào.  
Lớp cũng có t|c dụng như l{ một kiểu dữ liệu, do đó, ta cũng có thể  
khai báo nhiều đối tượng của cùng một lớp hay mảng c|c đối tượng.  
Chương trình  
Kết quả  
#include<iostream>  
using namespace std;  
Name and age of Student  
1
Nam  
21  
class Humans{  
string name;  
Name and age of Student  
22  
void setName(string);  
void setAge(int);  
string getName(void);  
int getAge(void);  
};  
Name and age of Student  
3
Xuan  
22  
Name and age of Student  
4
Tuan  
void Humans::setName(string s){  
name = s;  
T r a n g | 128  
Chương 13. Lập trình hướng đối tượng  
}
21  
void Humans::setAge (int a){  
Name and age of Student  
5
age = a;  
}
Lan  
22  
string Humans::getName(void){  
return name;  
=====Students=====  
The man: Nam, age 21  
The man: Binh, age 22  
The man: Xuan, age 22  
The man: Tuan, age 21  
The man: Lan, age 22  
}
int Humans::getAge(void){  
return age;  
}
#define MAX 5  
int main(){  
Humans man[MAX];  
for(int i=0; i<MAX; i++){  
string s;  
int a;  
cout<<"Name and age of Student "  
<<(i+1)<<endl;  
man[i].setName(s);  
man[i].setAge(a);  
}
cout<<"=====Students====="<<endl;  
for(int i=0; i<MAX; i++)  
cout<<"The man: "<<man[i].getName()<<",  
age "<<man[i].getAge()<<endl;  
return 0;  
}
Trong trường hợp này, biến man là một mảng c|c đối tượng Humans.  
Chương trình minh họa cho việc nhập tên sinh viên, tuổi của họ v{ lưu v{o  
lớp Humans, nhưng c|c đối tượng man[1], man[2],… có c|c thuộc tính hoàn  
toàn khác nhau.  
Cơ sở của lập trình hướng đối tượng là dữ liệu thành viên và các hàm  
thành viên của một đối tượng. Chúng ta hoàn toàn không sử dụng một tập  
các biến toàn cục để truyền qua một hàm (hay tập các biến cục bộ truyền  
theo tham biến), m{ thay v{o đó, chúng ta sử dụng c|c đối tượng cùng với  
T r a n g | 129  
Chương 13. Lập trình hướng đối tượng  
dữ liệu thành viên và hàm thành viên của nó. C|c h{m th{nh viên t|c động  
trực tiếp lên các dữ liệu thành viên.  
Hàm tạo và hàm hủy  
Trước khi sử dụng một đối tượng, chúng ta cần khởi tạo giá trị cho nó  
để tránh gặp phải những giá trị không mong muốn khi thực thi chương  
trình. Một cách thức m{ chúng ta đ~ sử dụng ở trên là sử dụng phương thức  
setter. Một phương thức đơn giản hơn, chúng ta có thể sử dụng hàm khởi  
tạo (hay gọi tắt là hàm tạo). Việc khai báo hàm tạo cũng tương tự như khai  
báo hàm thành viên khác, tuy nhiên nhất thiết tên hàm tạo phải trùng với  
tên lớp.  
Chương trình  
Ví dụ  
#include<iostream>  
The man: Jack, age 21  
using namespace std;  
class Humans{  
string name;  
int age;  
string getName(void);  
int getAge(void);  
};  
Humans::Humans (string s, int a){  
name = s;  
age = a;  
}
string Humans::getName(void){  
return name;  
}
int Humans::getAge(void){  
int main(){  
Humans man(“Jack”, 21);  
cout<<”The man: “<<man.getName()<<”, age  
“<<man.getAge();  
return 0;  
}
Hàm tạo không có kiểu dữ liệu trả về - tương ứng với kiểu void. Tuy nhiên  
chúng ta sẽ không sử dụng từ khóa void trước khai báo hàm tạo.  
T r a n g | 130  
Chương 13. Lập trình hướng đối tượng  
Nếu một đối tượng đ~ được tạo ra, nhưng ta không muốn sử dụng  
đến nó nữa, để thu hồi bộ nhớ, ta cần sử dụng một phương thức để hủy bỏ  
các dữ liệu thành viên của nó – đó l{ h{m hủy. Hàm hủy cũng l{ một hàm  
thành viên của lớp. Nó không có kiểu dữ liệu trả về, nhưng ta cũng không  
sử dụng từ khóa void trước khai báo hàm hủy. Hàm hủy có tên trùng với  
tên lớp v{ phía trước tên hàm hủy là dấu ~. Hàm hủy sẽ tự động được gọi  
khi phạm vi hoạt động của đối tượng kết thúc. Phạm vi hoạt động của một  
đối tượng cũng giống như phạm vi hoạt động của một biến cục bộ - khai báo  
trong phạm vi nào, thì chỉ có tác dụng trong phạm vi đó.  
Chương trình  
Ví dụ  
#include<iostream>  
The man: Jack, age 21  
using namespace std;  
class Humans{  
string name;  
int age;  
public:  
Humans(string, int);  
int getAge(void);  
};  
Humans::Humans (string s, int a){  
name = s;  
age = a;  
}
Humans::~Humans()  
{
//do something  
//delete pointer;  
}
}
int Humans::getAge(void){  
return age;  
}
int main(){  
Humans man(“Jack”, 21);  
cout<<”The man: “<<man.getName()<<”, age  
“<<man.getAge();  
T r a n g | 131  
Chương 13. Lập trình hướng đối tượng  
return 0;  
}
Chồng chất hàm tạo  
Cũng như c|c h{m kh|c trong C++ cho phép chồng chất hàm, hàm tạo  
cũng có thể bị chồng chất. Khi chồng chất hàm tạo thì danh sách tham số  
phải khác nhau (kiểu dữ liệu hoặc số lượng tham số). Chúng ta cần nhớ  
rằng, khi chồng chất hàm thì trình biên dịch sẽ gọi một h{m tương ứng với  
danh sách tham số của nó. Trong trường hợp chồng chất hàm tạo, thì quá  
trình gọi là tự động khi đối tượng được tạo ra, do đó, một hàm tạo sẽ được  
gọi tự động tương ứng với danh sách tham số của nó.  
Chương trình  
#include<iostream>  
using namespace std;  
class Humans{  
string name;  
Ví dụ  
The man: Jack, age 21  
The default: Default, age  
21  
int age;  
Humans(string, int);  
string getName(void);  
int getAge(void);  
};  
Humans::Humans (void){  
name = “Default”;  
age = 21;  
}
Humans::Humans (string s, int a){  
name = s;  
age = a;  
return name;  
}
int Humans::getAge(void){  
return age;  
}
int main(){  
Humans man(“Jack”, 21);  
Humans default;  
T r a n g | 132  
Chương 13. Lập trình hướng đối tượng  
cout<<”The man: “<<man.getName()<<”, age  
“<<man.getAge()<<endl;  
cout<<”The default: “<<default.getName()<<”,  
age “<<default.getAge();  
return 0;  
}
Giải thích: trong trường hợp ví dụ trên, để tạo đối tượng thuộc lớp  
Humans, ta có thể sử dụng một trong hai hàm tạo tương ứng:  
Humans(void) hoặc Humans(string, int). Nếu gọi theo phương thức hàm  
tạo không đối số, thì tên gọi và tuổi sẽ được tạo mặc định. Còn nếu gọi theo  
phương thức có đối số, thì tên gọi và tuổi sẽ được tạo theo tham số truyền  
vào. Ta cũng cần lưu ý trong c|ch gọi hàm tạo, đối với hàm tạo có đối số, thì  
sau tên đối tượng, chúng ta cần cung cấp tham số tương ứng với tham số  
hàm tạo bên trong dấu (). Còn đối với hàm tạo không đối số, thì hãy khai  
b|o nó như khai b|o biến mà không hề có bất kì dấu () nào.  
Humans man(); //Sai  
Humans man; //Đúng  
khai báo hàm tạo mặc định có tham số, thì việc khai báo một đối tượng theo  
c|ch Humans man; l{ không được phép. Nếu ta không tạo ra một hàm tạo  
n{o, thì điều này là hợp lệ.  
Sao chép hàm tạo  
Một đối tượng có thể được tạo ra từ hàm tạo theo cách khởi gán các  
dữ liệu thành viên của nó cho một giá trị n{o đó. Chúng ta hoàn toàn có thể  
khởi tạo một đối tượng từ một đối tượng khác bằng cách sử dụng toán tử  
gán. Tuy nhiên, trong thực tế, nếu dữ liệu của đối tượng lớn, phức tạp, thì  
việc sử dụng toán tử gán sẽ thực thi rất chậm và có thể gây ra một số lỗi liên  
ta có thể sử dụng hàm tạo sao chép. Khi sử dụng hàm tạo sao chép, trình  
biên dịch sẽ sao chép toàn bộ dữ liệu thành viên của đối tượng đó sang đối  
tượng khởi tạo.  
Humans man(“Jack”, 21);  
//Sao chép trực tiếp  
Humans man2 = man;  
//Sao chép hàm tạo  
T r a n g | 133  
Chương 13. Lập trình hướng đối tượng  
Humans::Humans(const Humans& m){  
name = m.name;  
age = m.age;  
}
Humans man2(man);  
Chúng ta lưu ý rằng, việc sao chép hàm tạo sẽ được quy định theo tham  
chiếu hằng const Humans&. Nếu ta không viết hàm sao chép hàm tạo, thì  
trình biến dịch sẽ tự động làm giúp(nghĩa l{ ta luôn có thể sử dụng cách  
khởi tạo đối tượng theo kiểu Object newObj(oldObj); với oldObj l{ đối  
tượng thuộc lớp Object đ~ được tạo, dù ta có tạo ra phương thức sao chép  
hàm tạo hay không). Hay nói cách khác, hàm tạo sao chép là mặc định đối  
với đại đa số trình biên dịch ANSI C++ hiện đại (GCC, Visual C++,  
Borland C++, Intel C++).  
Tham chiếu hằng. Phương thức sao chép hàm tạo (hoặc tổng quát là các  
phương thức có sử dụng tham chiếu hằng đến lớp đối tượng) có thể thực  
hiện theo tham chiếu hoặc tham chiếu hằng (tương ứng với không hoặc có  
từ khóa const), nhưng hãy luôn quy định là tham chiếu (có toán tử &). Khi  
đối tượng gốc. Dữ liệu của đối tượng tham chiếu sẽ được ánh xạ theo địa chỉ  
của đối tượng được tham chiếu (không thực hiện việc sao chép trực tiếp mà  
là gián tiếp thông qua địa chỉ của biến tham chiếu). Tuy nhiên, cũng vì lí do  
n{y m{ đối tượng được tham chiếu có thể bị thay đổi giá trị (tương tự như  
truyền theo tham biến). Điều này làm vi phạm tính đóng gói trong lập trình  
hướng đối tượng. Cũng vì lí do n{y, C++ cung cấp cho ta từ khóa const để  
quy định một đối tượng khi được tham chiếu sẽ không bị làm thay đổi dữ  
liệu th{nh viên v{ nó được gọi là tham chiếu hằng. Như vậy, ta cần phân  
biệt ba cách truyền tham số đối tượng trong một phương thức: truyền theo  
tham trị – dữ liệu của đối tượng có thể được thay đổi bên trong phương  
trường hợp này là thực thi trực tiếp nên thường chỉ áp dụng cho các kiểu  
dữ liệu nguyên thủy đơn giản; truyền theo tham chiếu – dữ liệu của đối  
tượng có thể bị thay đổi trong phương thức và nó được lưu lại, nó thực hiện  
việc sao chép dữ liệu một cách gián tiếp nên dữ liệu có cấu trúc phức tạp  
(như lớp đối tượng, con trỏ) có thể được sao chép nhanh hơn rất nhiều so  
với truyền theo tham trị; tham chiếu hằng – tương tự như tham chiếu  
nhưng không cho phép thay đổi dữ liệu của đối tượng ngay cả trong  
phương thức.  
T r a n g | 134  
Chương 13. Lập trình hướng đối tượng  
Hợp lệ  
Không hợp lệ  
…
…
class Humans{  
class Humans{  
string name;  
int age;  
string name;  
int age;  
public:  
Humans(string, int);  
public:  
Humans(string, int);  
Humans(const Humans&);  
string getName(void);  
Humans(const Humans&);  
~Human();  
int getAge(void);  
string getName(void);  
int getAge(void);  
};  
Humans::Humans(const Humans&  
m){  
};  
Humans::Humans(const Humans&  
name = m.name;  
age = m.age;  
m){  
name = m.name;  
}
…
age = m.age = 22;//Error  
}
…
Ta có thể thấy trong trường hợp không hợp lệ, chúng ta quy định đối  
đổi thuộc tính age của nó. Trong trường hợp n{y, chương trình sẽ phát sinh  
lỗi. Nếu quy định là tham chiếu bình thường (bỏ đi từ khóa const) thì khai  
b|o được xem là hợp lệ (tuy nhiên vi phạm tính đóng gói).  
Khi một phương thức của lớp đối tượng sử dụng tham số chính là đối tượng  
của lớp đó, chúng ta có thể truy cập trực tiếp đến thuộc tính của đối tượng  
tham chiếu kể cả nó được quy định là private (cả tham chiếu lẫn không  
tham chiếu). Bên cạnh đó, nếu ta quy định là tham chiếu bình thường, thì ta  
có thể sử dụng c|c phương thức như getter v{ setter để truy cập đến các  
thuộc tính của nó. Nhưng nếu ta sử dụng tham chiếu hằng, thì không được  
ta chỉ có thể truy cập đến các phương thức hằng của đối tượng tham chiếu  
hằng n{y. Phương thức hằng là những phương thức được bổ sung vào từ  
khóa const vào cuối khai b|o phương thức trong tiêu đề hàm prototype và  
tiêu đề trong khai b|o h{m đầy đủ. Hãy quan sát các ví dụ sau đ}y.  
Ví dụ Tham chiếu Hằng  
Ví dụ Tham chiếu  
class PhanSo  
…
…
class PhanSo  
T r a n g | 135  
Chương 13. Lập trình hướng đối tượng  
{
{
private:  
private:  
int Tu;  
int Mau;  
int Tu;  
int Mau;  
public:  
public:  
//Khai báo các hàm tạo  
PhanSo Nhan(const PhanSo&);  
int GetTu(void);  
int GetMau(void);  
//Khai báo các hàm tạo  
PhanSo Nhan(PhanSo&);  
int GetTu(void);  
int GetMau(void);  
};  
};  
PhanSo PhanSo::Nhan(const  
PhanSo& p)  
PhanSo PhanSo::Nhan(PhanSo& p)  
{
{
return PhanSo(Tu*p.GetTu(),  
Mau*p.GetMau());  
//hoặc PhanSo(Tu*p.Tu,  
Mau*p.Mau);  
return PhanSo(Tu*p.Tu,  
Mau*p.Mau);  
//không được phép viết  
PhanSo(Tu*p.GetTu(),  
Mau*p.GetMau());  
}
int PhanSo::GetTu(void)  
return Tu;  
{
}
return Tu;  
}
int PhanSo::GetMau(void)  
{
int PhanSo::GetMau(void)  
{
return Mau;  
}
return Mau;  
…
}
…
Trong trường hợp ta muốn sử dụng phương thức cho đối tượng tham chiếu  
Khi đó, chúng ta sử dụng cú ph|p sau đ}y:  
int GetTu(void) const;  
int GetMau(void) const;  
T r a n g | 136  
Chương 13. Lập trình hướng đối tượng  
…
class PhanSo  
{
private:  
int Tu;  
int Mau;  
public:  
//Khai báo các hàm tạo  
PhanSo Nhan(const PhanSo&);  
int GetTu(void) const;  
int GetMau(void) const;  
};  
PhanSo PhanSo::Nhan(const PhanSo& p)  
{
return PhanSo(Tu*p.Tu, Mau*p.Mau);  
//hoặc PhanSo(Tu*p.GetTu(), Mau*p.GetMau());  
}
return Tu;  
}
int PhanSo::GetMau(void) const  
{
return Mau;  
}
…
Việc bổ sung từ khóa const v{o sau khai b|o phương thức sẽ giúp cho đối  
tượng tham chiếu hằng có thể gọi phương thức hằng này.  
biến – ta có thể gán trực tiếp một giá trị biến cho phương thức đó m{ không  
gặp phải một trở ngại nào.  
Chương trình  
#include <iostream>  
using namespace std;  
T r a n g | 137  
Chương 13. Lập trình hướng đối tượng  
class complex{  
private:  
float img;  
float real;  
public:  
complex();  
complex(float, float);  
float &getimg();  
float &getreal();  
};  
complex::complex( float img, float real )  
{
this->img = img;  
this->real = real;  
}
complex::complex()  
{
}
float & complex::getreal()  
{
return real;  
}
float & complex::getimg()  
{
return img;  
}
complex c;  
c.getreal() = 2;  
c.getimg() = 1;  
cout<<c.getreal()<<" + I*"<<c.getimg();  
}
Trong ví dụ này, chúng ta thấy c|c phương thức getter được khai báo là  
phương thức tham chiếu. Ta có thể gán trực tiếp giá trị cho c|c phương  
thức n{y. Trong trường hợp n{y, phương thức getter có cả hai tính năng:  
T r a n g | 138  
Chương 13. Lập trình hướng đối tượng  
vừa là getter vừa là setter. Nhưng với tính năng của một phương thức setter  
đơn (tức chỉ thiết lập một giá trị duy nhất).  
Tính đóng gói – Encapsulation  
Ví dụ trên đưa ra cho ta hai phương |n: nên hay không nên sử dụng  
từ khóa const. Câu trả lời là h~y nên quy định việc sao chép hàm tạo là  
truyền theo tham chiếu hằng, bởi lẽ c|c đối tượng khác nhau, không có  
quyền chỉnh sửa dữ liệu thành viên của nhau, nó chỉ có thể truyền thông  
điệp cho nhau mà thôi, việc chỉnh sửa dữ liệu thành viên là do bản thân của  
đối tượng đó. Điều này là sự thể hiện tính đóng gói trong lập trình hướng  
đối tượng. Tính đóng gói của lập trình hướng đối tượng còn thể hiện ở các  
mức độ cho phép truy cập đối với dữ liệu và hàm thành viên – tương ứng  
với từ kho| private, protected v{ public m{ ta đ~ thảo luận ở trên.  
Khái niệm: tính đóng gói l{ tính chất không cho phép người dùng hay  
đối tượng kh|c thay đổi dữ liệu thành viên của đối tượng nội tại. Chỉ có các  
hàm thành viên của đối tượng đó mới có quyền thay đổi trạng thái nội tại  
của nó mà thôi. C|c đối tượng khác muốn thay đổi thuộc tính thành viên của  
quyết định thay đổi hay không vẫn do đối tượng nội tại quyết định.  
Chúng ta có thể khảo sát ví dụ sau: nếu một bệnh nhân cần phải thay  
nội tạng để có thể sống, thì việc thay thế nội tạng đó cần phải có sự đồng ý  
của bệnh nhân. Không ai có thể tự động thực hiện điều này (chỉ khi bệnh  
nh}n đ~ rơi v{o tình trạng hôn mê, thì người nhà bệnh nhân mới quyết định  
thay họ). Nội tạng là các thuộc tính cố hữu của bệnh nhân. C|c phương thức  
thay thế nội tạng của đối tượng b|c sĩ không phải l{ phương thức thành  
viên của đối tượng bệnh nhân (bệnh nhân không thể tự thay thế nội tạng  
cho mình v{ b|c sĩ không có quyền thay thế nội tạng cho bệnh nhân nếu  
nhân). Phương thức đồng ý của bệnh nh}n n{y cũng không thể nào áp dụng  
cho bệnh nhân kia (bệnh nhân A không thể quyết định thay thế nội tạng cho  
bệnh nhân B). Như vậy, dữ liệu thành viên của đối tượng nào, thì chỉ có đối  
tượng đó mới có quyền thay đổi.  
Trong một vài giáo trình, tính chất này còn được gọi l{ tính đóng gói  
và ẩn dấu thông tin (encapsulation and information hiding).  
T r a n g | 139  
Tải về để xem bản đầy đủ
pdf 111 trang yennguyen 16000 Free
Bạn đang xem 20 trang mẫu của tài liệu "Giáo trình C++ - Lập trình hướng đối tượng (Phần 2)", để tải tài liệu gốc về máy hãy click vào nút Download ở trên

File đính kèm:

  • pdfgiao_trinh_lap_trinh_huong_doi_tuong_phan_2.pdf