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

Những nhược điểm của lập trình hướng thủ tục

Cách tiếp cận lập trình truyền thống là lập trình hướngthủ tục (LTHTT). Theo cách tiếp cận này thì một hệ thống

phần mềm được xem như là các công việc cần thực

hiện như đọc dữ liệu, tính toán, xử lý, lập báo cáo và in ấn

kết quả v.v. Mỗi công việc đó sẽ được thực hiện bởi một

số hàm nhất định. Như vậy trọng tâm của cách tiếp cận này

là các hàm chức năng. LTHTT sử dụng kỹ thuật phân

hàm chức năng theo cách tiếp cận trên xuống (top-down)

để tạo ra cấu trúc phân cấp. Các ngôn ngữ lập trình bậc cao

như COBOL, FORTRAN, PASCAL, C, v.v., là những

ngôn ngữ lập trình hướng thủ tục. Những nhược điểm chính

của LTHTT là:

Chương trình khó kiểm soát và khó khăn trong việc

bổ sung, nâng cấp chương trình. Chương trình được xây

dựng theo cách TCHTT thực chất là danh sách các câu lệnh

mà theo đó máy tính cần thực hiện. Danh sách các lệnh đó

được tổ chức thành từng nhóm theo đơn vị cấu trúc của

ngôn ngữ lập trình và được gọi là hàm/thủ tục. Trong

chương trình có nhiều hàm/thủ tục, thường thì có nhiều

thành phần dữ liệu quan trọng sẽ được khai báo tổng thể

(global) để các hàm/thủ tục có thể truy nhập, đọc và làm

thay đổi giá trị của biến tổng thể. Điều này sẽ làm cho

chương trình rất khó kiểm soát, nhất là đối với các chương

trình lớn, phức tạp thì vấn đề càng trở nên khó khăn hơn.

Khi ta muốn thay đổi, bổ sung cấu trúc dữ liệu dùng chung

cho một số hàm/thủ tục thì phải thay đổi hầu như tất cả các

hàm/thủ tục liên quan đến dữ liệu đó.

Mô hình được xây dựng theo cách tiếp cận hướngthủ tục không mô tả được đầy đủ, trung thực hệ thống trong

thực tế.

Phương pháp TCHTT đặt trọng tâm vào hàm là

hướng tới hoạt động sẽ không thực sự tương ứng với các

thực thể trong hệ thống của thế giới thực.

pdf 53 trang kimcuc 7560
Bạn đang xem 20 trang mẫu của tài liệu "Giáo trình Lập trình hướng đối tượng (Phần 1)", để tải tài liệu gốc về máy hãy click vào nút Download ở trên

Tóm tắt nội dung tài liệu: Giáo trình Lập trình hướng đối tượng (Phần 1)

Giáo trình Lập trình hướng đối tượng (Phần 1)
Lời nói đầu
Chương I: Các khái niệm cơ sở của lập trình hướng đối
tượng
Chương 2: các mở rộng của ngôn ngữ C++
Chương 3: Lớp
Chương 4: Toán tử tải bội
Chương 5:Kế thừa
Chương 6: Khuôn hình
Lời nói đầu
 Lập trình hướng đối tượng là phương pháp lập trình mới
trên bước đường tiến hóa của việc lập trình máy tính, nhằm
giúp chương trình trở nên linh hoạt, tin cậy và dễ phát triển.
Tư tưởng lập trình hướng đối tượng được áp dụng cho hầu
hết các ngôn ngữ lập trình chạy trên môi trường Windows
như Visual Basic, Java, Viual C... Vì vậy việc nghiên cứu
phương pháp lập trình mới này là thật sự cần thiết đối với
những người làm Tin học.
Giáo trình này là một thành phần của hệ thống giáo trình
của Khoa Công nghệ thông tin trường Đại học Khoa học
Huế. Giáo trình được biên soạn theo chương trình chính
quy cử nhân ngành Công nghệ thông tin. Nội dung của giáo
trình này gồm 6 chương. Chương 1 trình bày cách tiếp cận
hướng đối tượng, các nhược điểm của cách tiếp cận hướng
thủ tục, các khái niệm cơ bản của lập trình hướng đối
tượng. Các chương từ 2 đến 6 tập trung vào ngôn ngữ lập
trình C++ để minh họa cho việc lập trình theo cách tiếp cận
hướng đối tượng.
C++ là sự mở rộng của ngôn ngữ C, nó có nhiều ưu
điểm trong khả năng biểu diễn dữ liệu. Việc nắm bắt các
yếu tố cơ bản khi lập trình trong ngôn ngữ C++ sẽ là cơ sở
để nâng cao hiểu biết và kỹ năng lập trình bằng ngôn ngữ
Java, một công cụ không thể thiếu được trong việc phát
triển các ứng dụng trên mạng.
Trong quá trình biên soạn giáo trình này, tác giả đ• nhận
được nhiều ý kiến đóng góp quý báu về nội dung chuyên
môn của TS. Lê Mạnh Thạnh, TS. Nguyễn Mậu Hân, TS.
Hoàng Quang cùng nhiều đồng nghiệp khác trong khoa
Công nghệ thông tin, trường Đại học Khoa học Huế. Tác
giả xin chân thành cảm ơn sự giúp đỡ quý giá đó.
Mặc dù đ• hết sức cố gắng, nhưng chúng tôi nghĩ rằng sẽ
không tránh khỏi những thiếu sót, rất mong nhận được sự
góp ý của các sinh viên và đồng nghiệp để nâng cao chất
lượng giáo trình trong các lần tái bản.
Huế, ngày 28 tháng 4 năm 2004
 Trương
Công Tuấn
Chương I: Các khái niệm cơ sở của lập trình
hướng đối tượng
 Chương 1 trình bày những vấn đề sau:
 Thảo luận về cách tiếp cận hướng đối tượng, những
nhược điểm của lập trình truyền thống và các đặc điểm của
lập trình hướng đối tượng.
 Các khái niệm cơ sở của phương pháp hướng đối
tượng:
• Đối tượng
• Lớp
• Trừu tượng hóa dữ liệu và bao gói thông tin
• Kế thừa
• Tương ứng bội
• Liên kết động
• Truyền thông báo
 Các bước cần thiết để thiết kế chương trình theo
hướng đối tượng
 Các ưu điểm của lập trình hướng đối tượng
 Các ngôn ngữ hướng đối tượng
 Một số ứng dụng của lập trình hướng đối tượng
1.1. Giới thiệu
1.1.1. Tiếp cận hướng đối tượng
Trong thế giới thực, chung quanh chúng ta là những đối
tượng, đó là các thực thể có mối quan hệ với nhau. Ví dụ
các phòng trong một công ty kinh doanh được xem như
những đối tượng. Các phòng ở đây có thể là: phòng quản
lý, phòng bán hàng, phòng kế toán, phòng tiếp thị,... Mỗi
phòng ngoài những cán bộ đảm nhiệm những công việc cụ
thể, còn có những dữ liệu riêng như thông tin về nhân viên,
doanh số bán hàng, hoặc các dữ liệu khác có liên quan đến
bộ phận đó. Việc phân chia các phòng chức năng trong
công ty sẽ tạo điều kiện dễ dàng cho việc quản lý các hoạt
động. Mỗi nhân viên trong phòng sẽ điều khiển và xử lý dữ
liệu của phòng đó. Ví dụ phòng kế toán phụ trách về lương
bổng nhân viên trong công ty. Nếu bạn đang ở bộ phận tiếp
thị và cần tìm thông tin chi tiết về lương của đơn vị mình
thì sẽ gởi yêu cầu về phòng kế toán. Với cách làm này bạn
được đảm bảo là chỉ có nhân viên của bộ phận kế toán được
quyền truy cập dữ liệu và cung cấp thông tin cho bạn. Điều
này cũng cho thấy rằng, không có người nào thuộc bộ phận
khác có thể truy cập và thay đổi dữ liệu của bộ phận kế
toán. Khái niệm như thế về đối tượng hầu như có thể được
mở rộng đối với mọi lĩnh vực trong đời sống x• hội và hơn
nữa - đối với việc tổ chức chương trình. Mọi ứng dụng có
thể được định nghĩa như một tập các thực thể - hoặc các đối
tượng, sao cho quá trình tái tạo những suy nghĩa của chúng
ta là gần sát nhất về thế giới thực.
Trong phần tiếp theo chúng ta sẽ xem xét phương pháp
lập trình truyền thống để từ đó thấy rằng vì sao chúng ta
cần chuyển sang phương pháp lập trình hướng đối tượng.
1.1.2. Những nhược điểm của lập trình hướng thủ tục
Cách tiếp cận lập trình truyền thống là lập trình hướng
thủ tục (LTHTT). Theo cách tiếp cận này thì một hệ thống
phần mềm được xem như là d•y các công việc cần thực
hiện như đọc dữ liệu, tính toán, xử lý, lập báo cáo và in ấn
kết quả v.v... Mỗi công việc đó sẽ được thực hiện bởi một
số hàm nhất định. Như vậy trọng tâm của cách tiếp cận này
là các hàm chức năng. LTHTT sử dụng kỹ thuật phân r•
hàm chức năng theo cách tiếp cận trên xuống (top-down)
để tạo ra cấu trúc phân cấp. Các ngôn ngữ lập trình bậc cao
như COBOL, FORTRAN, PASCAL, C, v.v..., là những
ngôn ngữ lập trình hướng thủ tục. Những nhược điểm chính
của LTHTT là:
 Chương trình khó kiểm soát và khó khăn trong việc
bổ sung, nâng cấp chương trình. Chương trình được xây
dựng theo cách TCHTT thực chất là danh sách các câu lệnh
mà theo đó máy tính cần thực hiện. Danh sách các lệnh đó
được tổ chức thành từng nhóm theo đơn vị cấu trúc của
ngôn ngữ lập trình và được gọi là hàm/thủ tục. Trong
chương trình có nhiều hàm/thủ tục, thường thì có nhiều
thành phần dữ liệu quan trọng sẽ được khai báo tổng thể
(global) để các hàm/thủ tục có thể truy nhập, đọc và làm
thay đổi giá trị của biến tổng thể. Điều này sẽ làm cho
chương trình rất khó kiểm soát, nhất là đối với các chương
trình lớn, phức tạp thì vấn đề càng trở nên khó khăn hơn.
Khi ta muốn thay đổi, bổ sung cấu trúc dữ liệu dùng chung
cho một số hàm/thủ tục thì phải thay đổi hầu như tất cả các
hàm/thủ tục liên quan đến dữ liệu đó.
 Mô hình được xây dựng theo cách tiếp cận hướng
thủ tục không mô tả được đầy đủ, trung thực hệ thống trong
thực tế.
 Phương pháp TCHTT đặt trọng tâm vào hàm là
hướng tới hoạt động sẽ không thực sự tương ứng với các
thực thể trong hệ thống của thế giới thực.
1.1.3. Lập trình hướng đối tượng
Lập trình hướng đối tượng (Object Oriented
Programming - LTHĐT) là phương pháp lập trình lấy đối
tượng làm nền tảng để xây dựng thuật giải, xây dựng
chương trình. Đối tượng được xây dựng trên cơ sở gắn cấu
trúc dữ liệu với các phương thức (các hàm/thủ tục) sẽ thể
hiện được đúng cách mà chúng ta suy nghĩ, bao quát về thế
giới thực. LTHĐT cho phép ta kết hợp những tri thức bao
quát về các quá trình với những khái niệm trừu tượng được
sử dụng trong máy tính.
Điểm căn bản của phương pháp LTHĐT là thiết kế
chương trình xoay quanh dữ liệu của hệ thống. Nghĩa là các
thao tác xử lý của hệ thống được gắn liền với dữ liệu và
như vậy khi có sự thay đổi của cấu trúc dữ liệu thì chỉ ảnh
hưởng đến một số ít các phương thức xử lý liên quan.
LTHĐT không cho phép dữ liệu chuyển động tự do
trong hệ thống. Dữ liệu được gắn chặt với từng phương
thức thành các vùng riêng mà các phương thức đó tác động
lên và nó được bảo vệ để cấm việc truy nhập tùy tiện từ bên
ngoài. LTHĐT cho phép phân tích bài toán thành tập các
thực thể được gọi là các đối tượng và sau đó xây dựng các
dữ liệu cùng với các phương thức xung quanh các đối
tượng đó.
Tóm lại LTHĐT có những đặc tính chủ yếu như sau:
1. Tập trung vào dữ liệu thay cho các phương thức.
2. Chương trình được chia thành các lớp đối tượng.
3. Các cấu trúc dữ liệu được thiết kế sao cho đặc tả được
các đối tượng.
4. Các phương thức xác định trên các vùng dữ liệu của
đối tượng được gắn với nhau trên cấu trúc dữ liệu đó.
5. Dữ liệu được bao bọc, che dấu và không cho phép các
thành phần bên ngoài truy nhập tự do.
6. Các đối tượng trao đổi với nhau thông qua các
phương thức.
7. Dữ liệu và các phương thức mới có thể dễ dàng bổ
sung vào đối tượng nào đó khi cần thiết.
8. Chương trình được thiết kế theo cách tiếp cận bottom-
up (dưới -lên).
1.2. Các khái niệm cơ bản của lập trình hướng đối tượng
Những khái niệm cơ bản trong LTHĐT bao gồm: Đối
tượng; Lớp; Trừu tượng hóa dữ liệu, bao gói thông tin; Kế
thừa; Tương ứng bội; Liên kết động; Truyền thông báo.
1.2.1. Đối tượng
Trong thế giới thực, khái niệm đối tượng được hiểu như
là một thực thể, nó có thể là người, vật hoặc một bảng dữ
liệu cần xử lý trong chương trình,... Trong LTHĐT thì đối
tượng là biến thể hiện của lớp.
1.2.2. Lớp
Lớp là một khái niệm mới trong LTHĐT so với kỹ thuật
LTHTT. Nó là một bản mẫu mô tả các thông tin cấu trúc dữ
liệu và các thao tác hợp lệ của các phần tử dữ liệu. Khi một
phần tử dữ liệu được khai báo là phần tử của một lớp thì nó
được gọi là đối tượng. Các hàm được định nghĩa hợp lệ
trong một lớp được gọi là các phương thức (method) và
chúng là các hàm duy nhất có thể xử lý dữ liệu của các đối
tượng của lớp đó. Mỗi đối tượng có riêng cho mình một
bản sao các phần tử dữ liệu của lớp. Mỗi lớp bao gồm:
danh sách các thuộc tính (attribute) và danh sách các
phương thức để xử lý các thuộc tính đó. Công thức phản
ánh bản chất của kỹ thuật LTHĐT là:
 Đối tượng = Dữ liệu + Phương
thức
Chẳng hạn, chúng ta xét lớp HINH_CN bao gồm các
thuộc tính: (x1,y1) toạ độ góc trên bên trái, d,r là chiều dài
và chiều rộng của HCN. Các phương thức nhập số liệu cho
HCN, hàm tính diện tích, chu vi và hàm hiển thị. Lớp
HINH_CN có thể được mô tả như sau:
HINH_CN
Thuộc tính :
x1,y1
d,r
Phương thức :
Nhập_sl
Diện tích
Chu vi
Hiển thị
HINH_CN
Thuộc tính
x1,y1
d,r
Phương thức
Nhập_sl
Diện tích
Chu vi
Hiển thị
 Hình 2.2 Mô tả lớp HINH_CN
Chú ý: Trong LTHĐT thì lớp là khái niệm tĩnh, có thể
nhận biết ngay từ văn bản chương trình, ngược lại đối
tượng là khái niệm động, nó được xác định trong bộ nhớ
của máy tính, nơi đối tượng chiếm một vùng bộ nhớ lúc
thực hiện chương trình. Đối tượng được tạo ra để xử lý
thông tin, thực hiện nhiệm vụ được thiết kế, sau đó bị hủy
bỏ khi đối tượng đó hết vai trò.
1.2.3. Trừu tượng hóa dữ liệu và bao gói thông tin
Trừu tượng hóa là cách biểu diễn những đặc tính chính
và bỏ qua những chi tiết vụn vặt hoặc những giải thích. Khi
xây dựng các lớp, ta phải sử dụng khái niệm trừu tượng
hóa. Ví dụ ta có thể định nghĩa một lớp để mô tả các đối
tượng trong không gian hình học bao gồm các thuộc tính
trừu tượng như là kích thước, hình dáng, màu sắc và các
phương thức xác định trên các thuộc tính này.
Việc đóng gói dữ liệu và các phương thức vào một đơn
vị cấu trúc lớp được xem như một nguyên tắc bao gói thông
tin. Dữ liệu được tổ chức sao cho thế giới bên ngoài (các
đối tượng ở lớp khác) không truy nhập vào, mà chỉ cho
phép các phương thức trong cùng lớp hoặc trong những lớp
có quan hệ kế thừa với nhau mới được quyền truy nhập.
Chính các phương thức của lớp sẽ đóng vai trò như là giao
diện giữa dữ liệu của đối tượng và phần còn lại của chương
trình. Nguyên tắc bao gói dữ liệu để ngăn cấm sự truy nhập
trực tiếp trong lập trình được gọi là sự che giấu thông tin.
1.2.4. Kế thừa
Kế thừa là quá trình mà các đối tượng của lớp này được
quyền sử dụng một số tính chất của các đối tượng của lớp
khác. Sự kế thừa cho phép ta định nghĩa một lớp mới trên
cơ sở các lớp đ• tồn tại. Lớp mới này, ngoài những thành
phần được kế thừa, sẽ có thêm những thuộc tính và các
hàm mới. Nguyên lý kế thừa hỗ trợ cho việc tạo ra cấu trúc
phân cấp các lớp.
1.2.5. Tương ứng bội
Tương ứng bội là khả năng của một khái niệm (chẳng
hạn các phép toán) có thể sử dụng với nhiều chức năng
khác nhau. Ví dụ, phép + có thể biểu diễn cho phép “cộng”
các số nguyên (int), số thực (float), số phức (complex) hoặc
xâu ký tự (string) v.v... Hành vi của phép toán tương ứng
bội phụ thuộc vào kiểu dữ liệu mà nó sử dụng để xử lý.
Tương ứng bội đóng vai quan trọng trong việc tạo ra các
đối tượng có cấu trúc bên trong khác nhau nhưng cùng
dùng chung một giao diện bên ngoài (như tên gọi).
1.2.6. Liên kết động
 Liên kết động là dạng liên kết các thủ tục và hàm
khi chương trình thực hiện lời gọi tới các hàm, thủ tục đó.
Như vậy trong liên kết động, nội dung của đoạn chương
trình ứng với thủ tục, hàm sẽ không được biết cho đến khi
thực hiện lời gọi tới thủ tục, hàm đó.
1.2.7. Truyền thông báo
Các đối tượng gửi và nhận thông tin với nhau giống như
con người trao đổi với nhau. Chính nguyên lý trao đổi
thông tin bằng cách truyền thông báo cho phép ta dễ dàng
xây dựng được hệ thống mô phỏng gần hơn những hệ thống
trong thế giới thực. Truyền thông báo cho một đối tượng là
yêu cầu đối tượng thực hiện một việc gì đó. Cách ứng xử
của đối tượng được mô tả bên trong lớp thông qua các
phương thức.
Trong chương trình, thông báo gửi đến cho một đối
tượng chính là yêu cầu thực hiện một công việc cụ thể,
nghĩa là sử dụng những hàm tương ứng để xử lý dữ liệu đ•
được khai báo trong đối tượng đó. Vì vậy, trong thông báo
phải chỉ ra được hàm cần thực hiện trong đối tượng nhận
thông báo. Thông báo truyền đi cũng phải xác định tên đối
tượng và thông tin truyền đi. Ví dụ, lớp CONGNHAN có
thể hiện là đối tượng cụ thể được đại diện bởi Hoten nhận
được thông báo cần tính lương thông qua hàm
TINHLUONG đ• được xác định trong lớp CONGNHAN.
Thông báo đó sẽ được xử lý như sau:
Trong chương trình hướng đối tượng, mỗi đối tượng chỉ
tồn tại trong thời gian nhất định. Đối tượng được tạo ra khi
nó được khai báo và sẽ bị hủy bỏ khi chương trình ra khỏi
miền xác định của đối tượng đó. Sự trao đổi thông tin chỉ
có thể thực hiện trong thời gian đối tượng tồn tại.
1.3. Các bước cần thiết để thiết kế chương trình theo
hướng đối tượng
Chương trình theo hướng đối tượng bao gồm một tập
các đối tượng và mối quan hệ giữa các đối tượng với nhau.
Vì vậy, lập trình trong ngôn ngữ hướng đối tượng bao gồm
các bước sau:
1. Xác định các dạng đối tượng (lớp) của bài tóan.
2. Tìm kiếm các đặc tính chung (dữ liệu chung) trong
các dạng đối tượng này, những gì chúng cùng nhau chia xẻ.
3. Xác định lớp cơ sở dựa trên cơ sở các đặc tính chung
của các dạng đối tượng.
4. Từ lớp cơ sở, xây dựng các lớp dẫn xuất chứa các
thành phần, những đặc tính không chung còn lại của các
dạng đối tượng. Ngoài ra, ta còn đưa ra các lớp có quan hệ
với các lớp cơ sở và lớp dẫn xuất.
1.4. Các ưu điểm của lập trình hướng đối tượng
Cách tiếp cận hướng đối tượng giải quyết được nhiều
vấn đề tồn tại trong quá trình phát triển phần mềm và tạo ra
được những sản phẩm phần mềm có chất lượng cao. Những
ưu điểm chính của LTHĐT là:
1. Thông qua nguyên lý kế thừa, có thể loại bỏ được
những đoạn chương trình lặp lại trong quá trình mô tả các
lớp và mở rộng khả năng sử dụng các lớp đ• được xây
dựng.
2. Chương trình được xây dựng từ những đơn thể (đối
tượng) trao đổi với nhau nên việc thiết kế và lập trình sẽ
được thực hiện theo quy trình nhất định chứ không phải
dựa vào kinh nghiệm và kỹ thuật như trước. Điều này đảm
bảo rút ngắn được thời gian xây dựng hệ thống và tăng
năng suất lao động.
3. Nguyên lý che giấu thông tin giúp người lập trình tạo
ra được những chương trình an toàn không bị thay bởi
những đoạn chương trình khác.
4. Có thể xây dựng được ánh xạ các đối tượng của bài
toán vào đối tượng của chương trình.
5. Cách tiếp cận thiết kế đặt trọng tâm vào đối tượng,
giúp chúng ta xây dựng được mô hình chi tiết và gần với
dạng cài  ...  hưởng
đối với biến a và ngược lại.
Ví dụ: int a, &tong = a;
 tong =1; //a=1
 cout<< tong; //in ra số 1
 tong++; //a=2
 ++a; //a=3
 cout<<tong; //in ra số 3
Chú ý:
• Trong khai báo biến tham chiếu phải chỉ rõ tham
chiếu đến biến nào.
• Biến tham chiếu có thể tham chiếu đến một phần
tử mảng, nhưng không cho phép khai báo mảng tham
chiếu.
• Biến tham chiếu có thể tham chiếu đến một hằng.
Khi đó nó sử dụng vùng nhớ của hằng và có thể làm thay
đổi giá trị chứa trong vùng nhớ này.
• Biến tham chiếu thường được sử dụng làm đối của
hàm để cho phép hàm truy nhập đến các tham biến trong
lời gọi hàm
2.6. Hằng tham chiếu
Cú pháp khai báo hằng tham chiếu như sau:
 const Kiểu dữ liệu &Biến =
Biến/Hằng;
Ví dụ: int n = 10;
 const int &m = n;
 const int &p = 123;
Hằng tham chiếu có thể tham chiếu đến một biến hoặc
một hằng.
Chú ý:
 Biến tham chiếu và hằng tham chiếu khác nhau ở
chỗ: không cho phép dùng hằng tham chiếu để làm thay đổi
giá trị của vùng nhớ mà nó tham chiếu.
Ví dụ: int y=12, z;
 const int &p = y //Hằng tham chiếu p tham chiếu
đến biến y
 p = p + 1; //Sai, trình biên dịch sẽ
thông báo lỗi
 Hằng tham chiếu cho phép sử dụng giá trị chứa
trong một vùng nhớ, nhưng không cho phép thay đổi giá trị
này.
 Hằng tham chiếu thường được sử dụng làm tham số
của hàm để cho phép sử dụng giá trị của các tham số trong
lời gọi hàm, nhưng tránh làm thay đổi giá trị tham số.
2.7. Truyền tham số cho hàm theo tham chiếu
Trong C chỉ có một cách truyền dữ liệu cho hàm là
truyền theo theo giá trị. Chương trình sẽ tạo ra các bản sao
của các tham số thực sự trong lời gọi hàm và sẽ thao tác
trên các bản sao này chứ không xử lý trực tiếp với các tham
số thực sự. Cơ chế này rất tốt nếu khi thực hiện hàm trong
chương trình không cần làm thay đổi giá trị của biến gốc.
Tuy nhiên, nhiều khi ta lại muốn những tham số đó thay
đổi khi thực hiện hàm trong chương trình. C++ cung cấp
thêm cách truyền dữ liệu cho hàm theo tham chiếu bằng
cách dùng đối là tham chiếu. Cách làm này có ưu diểm là
không cần tạo ra các bản sao của các tham số, do dó tiết
kiệm bộ nhớ và thời gian chạy máy. Mặt khác, hàm này sẽ
thao tác trực tiếp trên vùng nhớ của các tham số, do đó dễ
dàng thay đổi giá trị các tham số khi cần.
Ví dụ 2.7 Chương trình sau sẽ nhập d•y số thực, sắp xếp
d•y theo thứ tự tăng dần và hiển thị ra màn hình.
#include 
#include 
#include 
void nhapds(double *a,int n)
{
 for(int i=0;i<n;++i)
 {
 cout<<"\n Phan tu thu "<<i<<":";
 cin>>a[i];
 }
 }
 void hv(double &x,double &y)
 {
 double tam=x;x=y;y=tam;
 }
void sapxep(double *a,int n)
 {
 for(int i=0;i<n-1;++i)
 for(int j=i+1;j<n;++j)
 if(a[i]>a[j])
 hv(a[i],a[j]);
 }
void main()
{
double x[100];
int i,n;
clrscr();
cout<<"\n nhap so phan tu N = ";
cin>>n;
nhapds(x,n);
sapxep(x,n);
cout<<"\nCac phan tu mang sau khi sap xep :";
for(i=0;i<n;++i)
 printf("\n%6.2f",x[i]);
 getch();
}
Ví dụ 2.8 Chương trình sẽ nhập dữ liệu một danh sách
thí sinh bao gồm họ tên, điểm các môn 1, môn 2, môn 3 và
in danh sách thí sinh:
#include 
#include 
#include 
#include 
 struct TS
 {
 char ht[20]; 
 float d1,d2,d3,td;
 };
 void ints(const TS &ts)
 {
 cout<<setiosflags(ios::showpoint)<<setprecision(1);
 cout<<"\n ho ten"<<setw(20)<<ts.ht<<setw(6)<<ts.td;
 }
void nhapsl(TS *ts,int n)
 {
 for(int i=0;i<n;++i)
 {
 cout<<"\n Thi sinh"<<i;
 cout<<"\n ho ten ";
 cin.ignore(1);
 cin.get(ts[i].ht,25);
 cout<<"Nhap diem cac mon thi : ";
 cin>>ts[i].d1>>ts[i].d2>>ts[i].d3;
 ts[i].td=ts[i].d1+ts[i].d2+ts[i].d3;
 }
 }
 void hvts(TS &ts1,TS &ts2)
 {
 TS tg=ts1;
 ts1=ts2;
 ts2=tg;
 }
 void sapxep(TS *ts,int n)
 {
 for(int i=0;i<n-1;++i)
 for(int j=i+1;j<n;++j)
 if(ts[i].td<ts[j].td)
 hvts(ts[i],ts[j]) ;
 }
void main()
{
 TS ts[100];
 int n,i;
 clrscr();
 cout<<"So thi sinh : ";
 cin>>n;
 nhapsl(ts,n);
 sapxep(ts,n);
 float dc;
 cout<<"\n\nDanh sach thi sinh \n";
 for(i=0;i<n;++i)
 if(ts[i].td>=dc)
 ints(ts[i]);
 else
 break;
 getch();
}
Ví dụ 2.9 Chương trình sau sẽ nhập một mảng thực kích
thước 20x20, in mảng đ• nhập và in các phần tử lớn nhất và
nhỏ nhất trên mỗi hàng của mảng.
#include 
#include 
#include 
#include 
void nhapmt(float a[20][20],int m,int n)
 {
 for(int i=0;i<m;++i)
 for(int j=0;j<n;++j)
 {
 cout<<"\n a["<<i<<","<<j<<"]=";
 cin>> a[i][j];
 }
 }
void inmt(float a[20][20],int m,int n)
 {
 cout<<setiosflags(ios::showpoint)<<setprecision(1);
 cout<<"\nMang da nhap : ";
 for(int i=0;i<m;++i)
 for(int j=0;j<n;++j)
 {
 if(j==0) cout<<"\n";
 cout<<setw(6)<<a[i][j];
 }
 }
 void maxminds(float *x,int n,int &vtmax,int &vtmin)
 {
 vtmax=vtmin=0;
 for(int i=1;i<n;++i)
 {
 if(x[i]>x[vtmax]) vtmax=i;
 if(x[i]<x[vtmin]) vtmin=i;
 }
 }
void main()
{
 float a[20][20];
 int m,n;
clrscr();
 cout<<"\n Nhap so hang va so cot : ";
cin>>m>>n;
 nhapmt(a,m,n);
 inmt(a,m,n);
 float *p=(float*) a;
 int vtmax,vtmin;
 for(int i=0;i<m;++i)
 {
 p=((float*)a)+i*20;
 maxminds(p,n,vtmax,vtmin);
 printf("\n Hang %d phan tu max=%6.1f tai cot
 %d",i,p[vtmax],vtmax);
 printf("\n Phan tu min=%6.1f tai cot 
 %d",p[vtmin],vtmin);
 }
getch();
 }
2.8. Hàm trả về giá trị tham chiếu
C++ cho phép hàm trả về giá trị là một tham chiếu, lúc
này định nghĩa của hàm có dạng như sau :
Kiểu &Tên hàm(...)
 { //thân hàm
 return ;
 }
Trong trường hợp này biểu thức được trả lại trong câu
lệnh return phải là tên của một biến xác định từ bên ngoài
hàm, bởi vì khi đó mới có thể sử dụng được giá trị của hàm.
Khi ta trả về một tham chiếu đến một biến cục bộ khai báo
bên trong hàm, biến cục bộ này sẽ bị mất đi khi kết thúc
thực hiện hàm. Do vậy tham chiếu của hàm sẽ không còn ý
nghĩa nữa.
Khi giá trị trả về của hàm là tham chiếu, ta có thể gặp
các câu lệnh gán hơi khác thường, trong đó vế trái là một
lời gọi hàm chứ không phải là tên của một biến. Điều này
hoàn toàn hợp lý, bởi vì bản thân hàm đó có giá trị trả về là
một tham chiếu. Nói cách khác, vế trái của lệnh gán có thể
là lời gọi đến một hàm có giá trị trả về là một tham
chiếu.Xem các ví dụ sau:
Ví dụ 2.10
#include 
#include 
 int z;
 int &f()// ham tra ve mot bi danh cua bien toan bo z
 {
 return z;
 }
void main()
{
 f()=50;//z=50
 cout<<"\nz="<<z;
 getch();
 } 
Ví dụ 2.11
#include 
#include 
#include 
#include 
int & max(int& a, int& b);
void main()
{
 clrscr();
 int b =10, a= 7, c= 20;
 cout << "Max a,b : "<<max(b,a) << endl;
 max(b,a)++;
 cout << "Gia tri b va a :"<< b <<" "<<a <<endl;
 max(b,c)=5;
 cout << "Gia tri b va a va c :"<<b<<" "<<a
 <<" "<<c<< endl;
 }
int &max(int &a, int &b)
{
 return a>b ? a:b;
}
Kết quả trên màn hình sẽ là :
Max a,b : 10
Gia tri cua b va a : 11 7
Gia tri cua b va a va c : 11 7 5
2.9. Hàm với tham số có giá trị mặc định
C++ cho phép xây dựng hàm với các tham số được khởi
gán giá trị mặc định. Quy tắc xây dựng hàm với tham số
mặc định như sau:
• Các đối có giá trị mặc định cần là các tham số cuối
cùng tính từ trái qua phải.
• Nếu chương trình sử dụng khai báo nguyên mẫu
hàm thì các tham số mặc định cần được khởi gán trong
nguyên mẫu hàm, không được khởi gán khởi gán lại cho
các đối mặc định trong dòng đầu của định nghĩa hàm.
void f(int a, float x, char *st=”TRUNG TAM”, int b=1,
double y = 1.234);
void f(int a, float x, char *st=”TRUNG TAM”, int b=1,
double y = 1.234)
 {
 //Các câu lệnh
 }
• Khi xây dựng hàm, nếu không khai báo nguyên
mẫu, thì các đối mặc định được khởi gán trong dòng đầu
của định nghĩa hàm, ví dụ:
void f(int a, float x, char *st=”TRUNG TAM”, int b=1,
double y = 1.234)
 {
 //Các câu lệnh
 }
Chú ý: Đối với các hàm có tham số mặc định thì lời gọi
hàm cần viết theo quy định: Các tham số vắng mặt trong lời
gọi hàm tương ứng với các tham số mặc định cuối cùng
(tính từ trái sang phải), ví dụ với hàm:
void f(int a, float x, char *st=”TRUNG TAM”, int b=1,
double y = 1.234);
thì các lời gọi hàm đúng:
 f(3,3.4,”TIN HOC”,10,1.0);//Đầy đủ tham số
 f(3,3.4,”ABC”); //Thiếu 2 tham số cuối
 f(3,3.4); //Thiếu 3 tham số cuối
 Các lời gọi hàm sai:
 f(3); 
 f(3,3.4, ,10); 
Ví dụ 2.12
#include 
#include 
 void ht(char *dc="TRUNG TAM",int n=5);
 void ht(char *dc,int n)
 {
 for(int i=0;i<n;++i)
 cout<<"\n" <<dc;
 }
 void main()
 {
 ht();// in dong chu "TRUNG TAM"tren 5 dong
 ht("ABC",3);// in dong chu "ABC"tren 3 dong
 ht("DEF");// in dong chu "DEF"tren 5 dong
 getch();
}
2.10. Các hàm nội tuyến (inline)
Việc tổ chức chương trình thành các hàm có ưu điểm
chương trình được chia thành các đơn vị độc lập, điều này
giảm được kích thước chương trình, vì mỗi đoạn chưong
trình thực hiện nhiệm vụ của hàm được thay bằng lời gọi
hàm. Tuy nhiên hàm cũng có nhược điểm là làm là chậm
tốc độ thực hiện chương trình vì phải thực hiện một số thao
tác có tính thủ tục mỗi khi gọi hàm như: cấp phát vùng nhớ
cho các tham số và biến cục bộ, truyền dữ liệu của các
tham số cho các đối, giải phóng vùng nhớ trước khi thoát
khỏi hàm.
C++ cho khả năng khắc phục được nhược điểm nói trên
bằng cách dùng hàm nội tuyến. Để biến một hàm thành
hàm nội tuyến ta viết thêm từ khóa inline vào trước khai
báo nguyên mẫu hàm.
Chú ý: Trong mọi trường hợp, từ khóa inline phải xuất
hiện trước các lời gọi hàm thì trình biên dịch mới biết cần
xử lý hàm theo kiểu inline.
 Ví dụ hàm f() trong chương trình sau sẽ không phải là
hàm nội tuyến vì inline viết sau lời gọi hàm. 
Ví dụ 2.13
#include 
#include 
void main()
{
 int s ;
 s=f(5,6);
 cout<<s;
 getch();
}
 inline int f(int a,int b)
 {
 return a*b;
 }
Chú ý:
 Chương trình dịch các hàm inline như tương tự như
các macro, nghĩa là nó sẽ thay đổi lời gọi hàm bằng một
đoạn chương trình thực hiện nhiệm vụ hàm. Cách làm này
sẽ tăng tốc độ chương trình do không phải thực hiện các
thao tác có tính thủ tục khi gọi hàm nhưng lại làm tăng khối
lượng bộ nhớ chương trình (nhất là đối với các hàm nội
tuyến có nhiều câu lệnh). Vì vậy chỉ nên dùng hàm inline
đối với các hàm có nội dung đơn giản.
 Không phải khi gặp từ khoá inline là chương trình
dịch nhất thiết phải xử lý hàm theo kiểu nội tuyến. Từ khoá
inline chỉ là một từ khoá gợi ý cho chương trình dịch chứ
không phải là một mệnh lệnh bắt buộc.
Ví dụ 2.14 Chưong trình sau sử dụng hàm inline để tính
chu vi và diện tích hình chữ nhật.
#include 
#include 
inline void dtcvhcn(int a,int b,int &dt,int &cv)
 {
 dt=a*b;
 cv=2*(a+b);
 }
void main()
{
 int a[20],b[20],cv[20],dt[20],n;
 cout<<"\n So hinh chu nhat";
 cin>>n;
 for(int i=0;i<n;++i)
 {
 cout<<"\n Nhap 2 canh cua hinh chu nhat"<<i<<":";
 cin>>a[i]>>b[i];
 dtcvhcn(a[i],b[i],dt[i],cv[i]);
 }
 clrscr();
 for(i=0;i<n;++i)
 {
 cout<<"\n Hinh chu nhat thu "<<i+1<<":";
 cout<<"\n Do dai hai canh "<<a[i]<<"va"<<b[i];
 cout<<"\n dien tich "<<dt[i];
 cout<<"\n chu vi "<<cv[i];
 }
 getch();
}
Ví dụ 2.15 Một cách viết khác của chương trình trong ví
dụ 2.14
#include 
#include 
inline void dtcvhcn(int a,int b,int &dt,int &cv);
void main()
{
 int a[20],b[20],cv[20],dt[20],n;
 cout<<"\n So hinh chu nhat";
 cin>>n;
 for(int i=0;i<n;++i)
 {
 cout<<"\n Nhap 2 canh cua hinh chu nhat"<<i<<":";
 cin>>a[i]>>b[i];
 dtcvhcn(a[i],b[i],dt[i],cv[i]);
 }
 clrscr();
 for(i=0;i<n;++i)
 {
 cout<<"\n Hinh chu nhat thu "<<i+1<<":";
 cout<<"\n Do dai hai canh "<<a[i]<<"va"<<b[i];
 cout<<"\n dien tich "<<dt[i];
 cout<<"\n chu vi "<<cv[i];
 }
 getch();
 }
void dtcvhcn(int a,int b,int &dt ,int &cv)
 {
 dt=a*b;
 cv=2*(a+b);
 }
2.11. Hàm tải bội
Các hàm tải bội là các hàm có cùng một tên và có tập
đối khác nhau (về số lượng các đối hoặc kiểu). Khi gặp lời
gọi các hàm tải bội thì trình biên dịch sẽ căn cứ vào số
lượng và kiểu các tham số để gọi hàm có đúng tên và đúng
các tham số tương ứng.
Ví dụ 2.16 Chương trình tìm max của một d•y số nguyên
và max của một dẫy số thực. Trong chương trình có 6 hàm:
hai hàm dùng để nhập d•y số nguyên và d•y số thực có tên
chung là nhapds, bốn hàm: tính max 2 số nguyên, tính max
2 số thực, tính max của dẫy số nguyên, tính max của dẫy số
thực được đặt chung một tên là max.
#include 
#include 
#include 
void nhapds(int *x,int n);
void nhapds(double *x,int n);
int max(int x,int y);
double max(double x,double y);
void nhapds(int *x,int n)
 {
 for(int i=0;i<n;++i)
 {
 cout<<"Phan tu "<<i<<" = ";
 cin>>x[i];
 }
 }
 void nhapds(double *x,int n)
 {
 for (int i=0;i<n;i++)
 {
 cout<<"Phan tu "<<i<<" = ";
 cin>>x[i];
 }
 }
int max(int x,int y)
 {
 return x>y?x:y;
 }
double max(double x,double y)
 {
 return x>y?x:y;
 }
int max(int *x,int n)
 {
 int s=x[0];
 for(int i=1;i<n;++i)
 s=max(s,x[i]);
 return s;
 }
double max(double *x,int n)
 {
 double s=x[0];
 for(int i=1;i<n;++i)
 s=max(s,x[i]);
 return s;
 }
void main()
 {
 int a[20],n,ni,nd,maxi;
 double x[20],maxd;
 clrscr();
 cout<<"\n So phan tu nguyen n: ";
 cin>>ni;
 cout<<"\n Nhap day so nguyen: ";
 nhapds(a,ni);
 cout<<"\n So phan tu so thuc: ";
 cin>>nd;
 cout<<"\n Nhap day so thuc: ";
 nhapds(x,nd);
 maxi=max(a,ni);
 maxd=max(x,nd);
 cout<<"\n Max day so nguyen ="<<maxi;
 cout<<"\n Max day so thuc="<<maxd;
 getch();
 }
Chú ý: Nếu hai hàm trùng tên và trùng đối thì trình biên
dịch không thể phân biệt được. Ngay cả khi hai hàm này có
cùng kiểu khác nhau thì trình biên dịch vẫn báo lỗi. Ví dụ
sau xây dựng hai hàm cùng có tên là f và cùng một đối
nguyên a, nhưng kiểu hàm khác nhau. Hàm thứ nhất có
kiểu nguyên( trả về a*a), hàm thứ hai có kiểu void. Chương
trình sau sẽ bị thông báo lỗi khi biên dịch.
Ví dụ 2.17
#include 
#include 
int f(int a);
void f(int a);
int f(int a)
 {
 return a*a;
 }
 void f(int a)
 {
 cout<<"\n"<<a;
 }
void main()
{
 int b = f(5);
 f(b);
 getch();
 }
Bài tập
1. Viết chương trình thực hiện các yêu cầu sau đây:
- Nhập dữ liệu cho các sinh viên (dùng cấu trúc danh
sách liên kết đơn), các thông tin của sinh viên bao gồm: m•
sinh viên, họ tên, lớp, điểm trung bình.
- Chương trình có sử dụng toán tử new và delete.
- In ra danh sách sinh viên có sắp xếp vị thứ theo điểm
trung bình.
2. Viết chương trình để sắp xếp một mảng thực hai chiều
theo thứ tự tăng dần, trong chương trình có có sử dụng toán
tử new và delete.
3. Viết các hàm tải bội để tính diện tích tam giác, diện
tích hình chữ nhật, diện tích hình tròn.
4. Viết chương trình nhân hai ma trận Amxn và Bnxp ,
mỗi ma trân được cấp phát động và các giá trị của chúng
phát sinh ngẫu nhiên.

File đính kèm:

  • pdfgiao_trinh_lap_trinh_huong_doi_tuong_phan_1.pdf