113
BỘ TÀI CHÍNH HỌC VỆN TÀI CHÍNH 2018 LẬP TRÌNH HƯỚNG ĐỐI TƯỢNG TS. VŨ BÁ ANH Ths. HÀ VĂN SANG [email protected] ACADEMY OF FINANCE

LẬP TRÌNH HƯỚNG ĐỐI TƯỢNG

  • Upload
    others

  • View
    6

  • Download
    0

Embed Size (px)

Citation preview

Page 1: LẬP TRÌNH HƯỚNG ĐỐI TƯỢNG

BỘ TÀI CHÍNH

HỌC VỆN TÀI CHÍNH

2018

LẬP TRÌNH HƯỚNG

ĐỐI TƯỢNG TS. VŨ BÁ ANH

Ths. HÀ VĂN SANG

[email protected]

A C A D E M Y O F F I N A N C E

Page 2: LẬP TRÌNH HƯỚNG ĐỐI TƯỢNG

LẬP TRÌNH HƯỚNG ĐỐI TƯỢNG

Trang 2

Mục lục

MỞ ĐẦU .............................................................................................................. 5

Chương 1. GIỚI THIỆU VỀ LẬP TRÌNH HƯỚNG ĐỐI TƯỢNG ................. 6

1. Tổng quan về các kỹ thuật lập trình ............................................................. 6

1.1. Lập trình tuyến tính ............................................................................... 6

1.2. Lập trình cấu trúc .................................................................................. 6

1.3. Lập trình mô đun hoá ............................................................................ 7

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

1.5. Lập trình hướng đối tượng .................................................................... 9

2. Một số khái niệm cơ bản ............................................................................ 11

2.1. Hệ thống hướng đối tượng .................................................................. 11

2.2. Đối tượng (Objects) ............................................................................. 12

2.3. Thuộc tính (Attribute) và Phương thức (method) ............................... 12

2.4. Lớp (Class) và lớp con (SubClass) ...................................................... 12

2.5. Lớp trừu tượng .................................................................................... 13

2.6. Truyền thông điệp ............................................................................... 13

2.7. Sự trừu tượng hoá (abstraction) ........................................................... 14

2.8. Sự đóng gói (encapsulation) ................................................................ 14

2.9. Tính kế thừa ......................................................................................... 14

1.10. Tính đa hình ....................................................................................... 15

3. Các bước cần thiết để thiết kế chương trình theo hướng đối tượng ........... 16

4. Các ưu điểm của lập trình hướng đối tượng ............................................... 16

5. Một số ngôn ngữ lập trình hướng đối tượng .............................................. 16

6. Một số ứng dụng của lập trình hướng đối tượng ........................................ 18

Chương 2. GIỚI THIỆU VỀ C++ .................................................................... 19

1. Lịch sử của C++ ......................................................................................... 19

2. Các mở rộng của C++................................................................................. 19

2.1. Lời chú thích ........................................................................................ 19

Page 3: LẬP TRÌNH HƯỚNG ĐỐI TƯỢNG

LẬP TRÌNH HƯỚNG ĐỐI TƯỢNG

Trang 3

2.2. Từ khoá mới ........................................................................................ 20

2.3. Kiểu dữ liệu char và int ....................................................................... 20

2.4. Khai báo biến ....................................................................................... 20

2.5. Chuyển đổi và ép kiểu ......................................................................... 21

2.6. Vào ra trong C++ ................................................................................. 22

2.7. Cấp phát và giải phóng bộ nhớ ............................................................ 26

2.8. Biến tham chiếu ................................................................................... 28

2.9. Hằng tham chiếu .................................................................................. 29

2.10. Truyền tham số cho hàm theo tham chiếu ......................................... 30

2.11. Hàm với tham số có giá trị mặc định ................................................ 34

2.12. Các hàm nội tuyến ............................................................................. 35

2.13. Hàm tải bội ........................................................................................ 36

Chương 3. LỚP VÀ ĐỐI TƯỢNG .................................................................. 39

1. Xây dựng lớp và đối tượng ......................................................................... 39

1.1. Khai báo lớp ........................................................................................ 39

1.2. Khai báo đối tượng .............................................................................. 42

1.3. Truy xuất các thành phần của đối tượng ............................................. 43

2. Các phương thức ........................................................................................ 46

2.1. Hàm khởi tạo - Constructor ................................................................. 46

2.2. Hàm hủy – Destructor ......................................................................... 49

3. Đa năng hóa tóan tử .................................................................................... 51

4. Mảng và con trỏ của lớp ............................................................................. 57

5. Các hàm bạn và lớp bạn ............................................................................. 59

5.1. Hàm bạn (friend function) ................................................................... 59

5.2. Lớp bạn ................................................................................................ 61

6. Thành phần tĩnh .......................................................................................... 63

6.1. Các thành phần dữ liệu tĩnh ................................................................. 63

6.2. Các hàm thành viên tĩnh ...................................................................... 64

7. Thành phần hằng ........................................................................................ 67

7.1. Dữ liệu hằng ........................................................................................ 67

Page 4: LẬP TRÌNH HƯỚNG ĐỐI TƯỢNG

LẬP TRÌNH HƯỚNG ĐỐI TƯỢNG

Trang 4

7.2. hàm thành phần (phương thức hằng) ................................................... 69

8. Thành phần là đối tượng ............................................................................. 69

Chương 4. TÍNH KẾ THỪA ........................................................................... 72

1. Khái niệm ................................................................................................... 72

2. Khai báo lớp dẫn xuất ................................................................................. 73

3. Các kiểu kế thừa ......................................................................................... 75

4. Định nghĩa lại quyền truy xuất ................................................................... 78

5. Hàm khởi tạo và hàm hủy của lớp cơ sở .................................................... 81

6. Đa kế thừa ................................................................................................... 84

7. Lớp cơ sở ảo và nhập nhằng trong đa kế thừa ............................................ 86

Chương 5. TÍNH ĐA HÌNH ............................................................................ 90

1. Phương thức ảo ........................................................................................... 90

2. Phương thức ảo thuần túy và lớp trừu tượng .............................................. 97

Chương 6. KHUÔN HÌNH MẪU .................................................................. 101

1. Template ................................................................................................... 101

a. Khuôn hình hàm ................................................................................... 101

b. Khuôn hình lớp ..................................................................................... 105

Page 5: LẬP TRÌNH HƯỚNG ĐỐI TƯỢNG

LẬP TRÌNH HƯỚNG ĐỐI TƯỢNG

Trang 5

MỞ ĐẦU

Cơ sở lập trình học phần 2 (CSLT) là một trong hai môn học quan trọng nhất

của sinh viên chuyên ngành Tin học Tài chính Kế toán, ngành Hệ thống thông tin quản

ly thuộc Học viện Tài chính; môn học này giúp cho sinh viên năm băt được những ly

thuyết chung về các khái niệm, phương pháp lập trình hướng đối tượng, sau đó, giúp

sinh viên thực hành cài đặt chương trình cho các bài toán trong lĩnh vực Tài chính Kê

toán trong Hệ thống thông tin kế toán của môi đơn vị.

Môn học “Cơ sở lập trình” dành cho sinh viên chuyên ngành Tin học Tài chính

Kế toán được chia thành 3 học phần sau đây:

- CSLT HP1: Nhập môn Cơ sở lập trình học phần 1. Học phần nhập môn se

trình bày những ly thuyết chung về CSLT, đặc biệt tập trung giới thiệu kỹ thuật lập

trình hướng cấu trúc được sử dụng phổ biến nhất hiện nay.

- CSLT HP2: Lập trình hướng đối tượng với C++. Học phần này se trang bị cho

sinh viên những nội dung cần thiết để xây dựng một chương trình hướng đối tượng

trong lĩnh vực tài chính kế toán.

- CSLT HP3: Lập trình ứng dụng web. Học phần này trong bị cho sinh viên

những kiến thức cơ bản để lập trình trên môi trường web thông qua mạng máy tình;

học phần cung giúp cho sinh viên có khả năng tích hợp các hệ quản trị CSLT HP2 với

các phần mềm khác (C#, VB, NET, Crystal Report) để quản trị một ứng dụng trên

mạng máy tính.

Bài giảng gốc “CSLT HP2” được biên soạn nhằm phục vụ cho nhu cầu học tập

và nghiên cứu của sinh viên chuyên ngành Tin học Tài chính Kế toán, khoa Hệ thống

thông tin kinh tế, Học viện Tài chính.

Trong quá trình biên soạn Bài giảng gốc “CSLT học phần 2”, ở chương 1, được

Tiến sĩ Vũ Bá Anh (chủ biên), trình bày những kiến thức cơ bản để làm nền tảng cho

lập trình cấu trúc và lập trình hướng đối tượng. Trong các chương từ chương 2 đến

chương 6, Thạc sỹ Hà Văn Sang (đồng chủ biên) se trình bày những kiến thức tổng

quan về lập trình hướng đối tượng, các điểm mới của ngôn ngữ lập trình C++, các khái

niệm cơ bản về lớp, đối tượng, tính kế thừa, đa hình và khuôn hình mẫu. Trong môi

chương đó, se giới thiệu ly thuyết chung về một thành phần trong hướng đối tượng,

sau đó lấy các ví dụ minh họa cho việc xây dựng thành phần tương ứng trong quá trình

xây dựng phần mềm tài chính Kế toán.

Page 6: LẬP TRÌNH HƯỚNG ĐỐI TƯỢNG

LẬP TRÌNH HƯỚNG ĐỐI TƯỢNG

Trang 6

Chương 1. GIỚI THIỆU VỀ LẬP TRÌNH HƯỚNG ĐỐI

TƯỢNG

Lập trình hướng đối tượng (Object-Oriented Programming-OOP), hay còn

gọi là lập trình định hướng đối tượng, là kỹ thuật lập trình hô trợ công nghệ đối

tượng. OOP được xem là là cách tiếp cận mới, hiệu quả hơn trong việc giải quyết các

vấn đề. Nó giúp tăng năng suất, đơn giản hoá độ phức tạp khi bảo trì cung như mở

rộng phần mềm bằng cách cho phép lập trình viên tập trung vào các đối tượng phần

mềm ở bậc cao hơn. Mục đích của OOP là giúp người lập trình giảm nhẹ các thao tác

viết mã, cho phép tạo ra các ứng dụng mà các yếu tố bên ngoài có thể tương tác với

các ứng dụng đó như là tương tác với các đối tượng vật ly.

Lập trình hướng đối tượng dễ học bởi nó dựa trên một vài khái niệm đơn giản

nhưng hết sức mạnh. Trước khi tìm hiểu về kỹ thuật lập trình hướng đối tượng là gì

chúng ta hãy tìm hiểu về lịch sử của các kỹ thuật lập trình.

1. Tổng quan về các kỹ thuật lập trình

1.1. Lập trình tuyến tính

Lập trình tuyến tính thường dùng để viết các chương trình nhỏ và đơn giản

trong đó chỉ gồm một chương trình chính. Chương trình chính gồm một dãy tuần tự

các câu lệnh dùng để thay đổi dữ liệu. Các ngôn ngữ lập trình thời kỳ này được thiết

kế để giải các bài tính toán tương đối đơn giản. Phần lớn các chương trình này ngăn, ít

hơn 100 dòng. Cung theo thời gian các ngôn ngữ này không đáp ứng được việc triển

khai phần mềm do các khuyết điểm của lập trình tuyến tính:

- Các ngôn ngữ này không có khả năng sử dụng lại các đoạn mã

- Không có khả năng kiểm soát phạm vi truy xuất của dữ liệu

- Mọi dữ liệu trong chương trình đều là toàn cục (global)

Hình 1.1 Lập trình tuyến tính

1.2. Lập trình cấu trúc

Lập trình cấu trúc ra đời vào những cuối năm 1970. Các chương trình cấu trúc

thường được tổ chức theo công việc mà chúng thường thực hiện. Chương trình được

chia nhỏ thành các chương trình con riêng re, môi chương trình con thực hiện một

Page 7: LẬP TRÌNH HƯỚNG ĐỐI TƯỢNG

LẬP TRÌNH HƯỚNG ĐỐI TƯỢNG

Trang 7

nhiệm vụ. Các chương trình con này thường được gọi là hàm hoặc thủ tục. Các hàm

thường độc lập với nhau, môi hàm có dữ liệu riêng. Thông tin được chuyển giao giữa

các hàm thông qua các tham số và các biến toàn cục. Các biến cục bộ trong hàm thì

không thể truy xuất bởi hàm khác. Ở lập trình cấu trúc đã băt đẫu xuất hiện khái niệm

trừu tượng hoá (Abstraction). Sự trừu tượng hoá có thể xem như khả năng quan sát các

sự vật mà không quan tâm tới chi tiết bên trong nó. Trong một chương trình có cấu

trúc ta chỉ quan tâm xem hàm hoặc thủ tục đó là được gì, cần tham số gì chứ không

quan tâm tới việc hàm đó thực hiện các lệnh gì. Các ngôn ngữ hiện nay đều hô trợ kỹ

thuật lập trình có cấu trúc bởi các chương trình có cấu trúc dễ viết, dễ bảo dưỡng hơn

so với chương trình không có cấu trúc. Một số ngôn ngữ cấu trúc điển hình là C,

Pascal, Foxpro…

Hình 1.2 Lập trình cấu trúc

1.3. Lập trình mô đun hoá

Với lập trình mô đun, các thủ tục có chung một chức năng được nhóm lại với

nhau thành các môđun riêng lẻ. Lúc này một chương trình không chỉ là một phần đơn

mà bây giờ nó được chia thành một số phần nhỏ hơn. Các phần này tương tác với nhau

thông qua việc gọi thủ tục.

Page 8: LẬP TRÌNH HƯỚNG ĐỐI TƯỢNG

LẬP TRÌNH HƯỚNG ĐỐI TƯỢNG

Trang 8

Hình 1.3 Lập trình mô đun hóa

Môi môđun có thể có dữ liệu của riêng nó. Điều này cho phép môi môđun quản

ly một dữ liệu trong nó, dữ liệu này được sửa đổi bởi việc gọi các thủ tục.

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

Một hệ thống phần mềm theo cách tiếp cận lập trình truyền thống đượ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ử ly, lập báo cáo

và in ấn kết quả … Môi công việc đó se được thực hiện bằng một hàm hoặc thủ tục

nhất định. Đó chính là các tiếp cận lập trình hướng thủ tục. Ta có thể thấy trọng tâm

của cách tiếp cận này là các hàm chức năng. Lập trình hướng thủ tục sử dụng kỹ thuật

phân ra hàm chức năng theo cách tiếp cận từ trên xuống để 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.. 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 lập trình hướng thủ

tục 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 được xây dựng theo cách tiếp cận hướng thủ tục 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 se được khai báo là toàn cục (global) để các hàm có thể truy nhập, đọc và làm

thay đổi giá trị của biến toàn cục. Điều này làm cho chương trình 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

Page 9: LẬP TRÌNH HƯỚNG ĐỐI TƯỢNG

LẬP TRÌNH HƯỚNG ĐỐI TƯỢNG

Trang 9

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 tới dữ liệu đó.

Chương trình chưa loại bỏ được các đoạn mã lặp lại.

Mô hình được xây dựng theo cách hướng thủ tục không mô tả đầy đủ, trung

thực hệ thống trong thực tế.

Phương pháp tổ chức hướng thủ tục đặt trọng tâm vào hàm là hướng tới hoạt

động se 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.5. Lập trình hướng đối tượng

Tiếp cận hướng đối tượng

Trong thế giới thực xung quanh chúng ta là những đối tượng, đó là những 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 ky, 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 công việc

cụ thể, còn có những dữ liệu riêng về thông tin nhân viên, doanh số bán hàng, hoặc các

dữ liệu khác có liên quan tới bộ phận đó. Việc phân chia các phòng chức năng trong

công ty se tạo điều kiện dễ dàng cho việc quản ly. Môi nhân viên trong phòng se điều

khiển và xử ly dữ liệu của phòng đó. Ví dụ phòng kế toán phụ trách về lương bổng của

nhân viên trong công ty. Nếu bạn đang ở bộ phận tiếp thị và cần thông tin chi tiết về

lương của đơn vị mình thì bạn gửi yêu cầu về phòng kế toán. Với cách làm việc này

bạn đả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 cung 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 trong mọi lĩnh vực đời sống xã hội và

hơn thế 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ĩ

của chúng ta là gần sát với thế giới thực.

Lập trình hướng đối tượng 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 (hàm/thủ tục) se thể hiện được đúng cách mà

chúng ta suy nghĩ, bao quát về thế giới thực. Lập trình hướng đối tượng 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 lập trình hướng đối tượng là thiết kế xoay

quanh dữ liệu của hệ thống. Nghĩa là các thao tác xử ly 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ấu trúc dữ liệu thì chỉ ảnh hưởng tới một số ít

các phương thức xử ly liên quan.

Page 10: LẬP TRÌNH HƯỚNG ĐỐI TƯỢNG

LẬP TRÌNH HƯỚNG ĐỐI TƯỢNG

Trang 10

Lập trình hướng đối tượng 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 tuỳ tiện từ bên

ngoài. Lập trình hướng đối tượng cho phép phân tính bài toán thành lậ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 cung với các phương thức

xung quanh các đối tượng đó.

Lập trình OOP cho phép bạn tạo đối tượng, các đối tượng này được lưu trữ

trong các thư viện và được sử dụng lại trong một công việc khác. Nó tiết kiệm thời

gian cho người lập trình, nhiều công ty phần mềm xây dựng các thư viện này và bán ra

thị trường, từ đó giúp cho người lập trình dễ dàng tạo ra các ngôn ngữ lập trình hướng

đối tượng sử dụng "OPC" (Other People's Code).

OOP cho phép bạn định nghĩa đối tượng trong đó bao gồm cấu trúc dữ liệu và

các phương thức xử ly dữ liệu.

Một phương thức có thể được định nghĩa như là một chương trình có cấu trúc

nhỏ bên trong đối tượng. Các phương thức này xác định đối tượng se hoạt động ra sao.

Một lớp lại định nghĩa một đối tượng, khái niệm đóng kín dữ liệu và phương thức

trong một đối tượng được gọi là sự đóng kín hay đóng gói.

Lập trình hướng đối tượng bao gồm kỹ thuật lập trình cấu trúc và cấu trúc dữ

liệu nâng cao. Nó giúp cho người phát triển có thể kết hợp dữ liệu và các phương thức

vào một thành phần gọi là đối tượng. Nói cách khác, một đối tượng bao gồm mọi thứ

cần thiết cho việc thực thi một tập hợp các hàm, bao gồm cả dữ liệu.

Hình 1.4 Lập trình hướng đối tượng

Lập trình hướng đối tượng là kỹ thuật lập trình mới. Nó được thiết kế nhằm tiết

kiệm thời gian và tiền bạc thông qua việc cho phép người lập trình sử dụng lại các

đoạn mã chương trình. Khả năng tái sử dụng này được hoàn thiện qua tính kế thừa. Có

Page 11: LẬP TRÌNH HƯỚNG ĐỐI TƯỢNG

LẬP TRÌNH HƯỚNG ĐỐI TƯỢNG

Trang 11

nghĩa là làm cho các đối tượng có thể kế thừa các đặc tính (phương thức và dữ liệu) từ

đối tượng khác.

Thông thường, OOP cho phép lập trình viên sửa đổi các thuộc tính của đối

tượng được kế thừa. Trong lập trình cấu trúc, môđun được gọi thực hiện một chức

năng và bạn phải chấp nhận chức năng này khi nó được viết, hoặc bạn có thể sao chép

mã và sửa lại theo mục đích của bạn.

Tóm lại lập trình hướng đối tượng có những đặc tí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 đóng, che giấ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. Các chương trình được thiết kế theo cách tiếp cận từ dưới lên (bottom-up)

2. Một số khái niệm cơ bản

Câu hỏi đầu tiên mà chúng ta se tìm hiểu là: Lập trình hướng đối tượng là gì?

Câu trả lời rất đơn giản: Đó là lập trình mà trong đó có sự mô tả chính xác các đối

tượng trong thế giới thực, bằng cách sử dụng các đối tượng trong mã nguồn. Lập trình

hướng đối tượng đơn giản là mô hình hoá chính xác thế giới thực trong chương trình.

Nó cho phép sử dụng lại mã nguồn. Cốt yếu của lập trình hướng đối tượng là tạo ra

một đối tượng, chứa các thuộc tính và phương thức. Ví dụ chiếc ô tô là một đối tượng,

nó có các thuộc tính như màu, số cánh cửa, nó cung có các phương thức như đi, dừng

... Bạn có thể sử dụng đối tượng bằng cách thực thi một hoặc các phương thức của nó.

Một đối tượng trong lập trình cung tương tự như vậy. Nó có các thuộc tính được mô tả

và tất nhiên thức bạn có thể sử dụng các phương của nó.

Có bốn khái niệm cơ bản trong ly thuyết lập trình hướng đối tượng. Đó là các

khái niệm: sự trừu tượng hoá, sự đóng gói, kế thừa và đa hình. Nhưng trước khi tìm

hiểu các khái niệm này chúng ta hãy tìm hiểu các khái niệm trong một hệ thống hướng

đối tượng:

2.1. Hệ thống hướng đối tượng

Hệ thống hướng đối tượng là một hệ thống có các đặc điểm sau:

- Gồm tập hợp các đối tượng (objects). Đối tượng là sự bao gói của hai loại thành

phần là dữ liệu và các thao tác trên chính dữ liệu đó.

Page 12: LẬP TRÌNH HƯỚNG ĐỐI TƯỢNG

LẬP TRÌNH HƯỚNG ĐỐI TƯỢNG

Trang 12

- Trong hệ thống các đối tượng có thể được thừa hưởng các thuộc tính từ các đối

tượng khác

- Sự hoạt động của hệ thống là sự tương tác giữa các đối tượng, các đối tượng tương

tác với nhau thông qua cơ chế truyền thông điệp (thông báo và gửi nhận thông báo)

2.2. Đối tượng (Objects)

Đối tượng mô tả sự vật tồn tại hoặc khái niệm trong đời sống thực tế. Ví dụ, một đối

tượng có thể mô tả một khái niệm vật ly như nhân viên, công ty, hoặc ô tô, nó cung mô

tả một cái gì đó trừu tượng như hình tròn, điểm, …Về phương diện lập trình đối tượng

được định nghĩa là một sự thể hiện của một lớp. Nó còn được định nghĩa là phiên bản

trong khi thi hành của lớp. Đối tượng chính là các thực thể cơ bản trong hệ thống

hướng đối tượng. Môi đối tượng được bao hàm bởi tên và trạng thái (dữ liệu của

nó). Các biến trong đối tượng chỉ rõ mọi thông tin về trạng thái của đối tượng (state)

còn các phương thức thì xác định nó có thể sử dụng như thế nào (behavior). Ví dụ, một

cái xe đạp có một trạng thái (số bánh xe và bánh răng), và các thao tác (phanh, tăng

tốc, giảm tốc, thay đổi bánh răng).

2.3. Thuộc tính (Attribute) và Phương thức (method)

Thuộc tính (Attribute)

Thuộc tính của một lớp bao gồm các biến, hằng hay tham số nội tại của nó.

Biến có vai trò quan trọng nhất bởi chúng có thể bị thay đổi trong suốt quá trình tồn

tại, hoạt động của đối tượng. Các thuộc tính có thể được xác định kiểu và kiểu của

chúng có thể là các kiểu dữ liệu cổ điển hay đó là một lớp đã định nghĩa từ trước. Các

thuộc tính còn được gọi là dữ liệu thành viên.

Phương thức (method)

Là các hàm nội tại của đối tượng (hay lớp).Tuỳ theo đặc tính mà người lập trình

gán cho, một phương thức có thể chỉ được gọi bên trong các hàm khác của lớp đó, có

thể cho phép các câu lệnh bên ngoài lớp gọi tới nó, hay chỉ cho phép các lớp có quan

hệ đặc biệt như là quan hệ lớp con, và quan hệ bạn bè (friend) được phép gọi tới nó.

Môi phương thức đều có thể có kiểu trả về, chúng có thể trả các kiểu dữ liệu cổ điển

hay trả về một kiểu là một lớp đã được định nghĩa từ trước. Một tên gọi khác của

phương thức của một lớp là hàm thành viên

2.4. Lớp (Class) và lớp con (SubClass)

Lớp (Class)

Một lớp là một bản thiết kế hoặc bản mẫu dùng để định nghĩa các biến (dữ liệu)

và phương thức (lệnh) cho tất cả các đối tượng cùng loại Lớp sử dụng như là một kiểu

do người dùng định nghĩa. Do đó một lớp định nghĩa kiểu dữ liệu giống như kiểu dữ

Page 13: LẬP TRÌNH HƯỚNG ĐỐI TƯỢNG

LẬP TRÌNH HƯỚNG ĐỐI TƯỢNG

Trang 13

liệu sẵn có của một ngôn ngữ lập trình. Một đối tượng là một sự thể hiện cụ thể của

một lớp.

Việc truy xuất các biến và các phương thức được khai báo trong lớp phụ thuộc

vào việc khai báo chúng là riêng (private) hay chung (public). Một biến được khai báo

là public se được nhìn thấy ở mọi nơi. Còn biến khai báo là private se không xuất hiện

bên ngoài của lớp.

Tóm lại, lớp (class) là nhóm các đối tượng có cùng thuộc tính, cùng hành vi và

cùng mối liên hệ. Mối đối tượng được coi là một sự thể hiện (instant) của một lớp

Lớp con (SubClass)

Lớp con là một lớp thông thường nhưng có thêm tính chất kế thừa một phần

hoặc toàn bộ các đặc tính của một lớp khác. Lớp chia sẻ các đặc tính được gọi là lớp

cơ sở hoặc lớp cha (Base Class)

2.5. Lớp trừu tượng

Lớp trừu tượng là lớp mà nó không thể chuyển thành một lớp thực tế nào. Nó

được thiết kế nhằm tạo ra một lớp có các đặc tính tổng quát nhưng bản thân lớp đó

chưa có y nghĩa nên chưa thể tiến hành viết mã cho đối tượng. Ví dụ: Lớp

"hinh_phang" được định nghĩa không có dữ liệu nội tại và chỉ có các phương pháp

(hàm nội tại) "tinh_chu_vi", "tinh_dien_tich". Nhưng vì lớp hình_phẳng này chưa xác

định được đầy đủ các đặc tính của nó (cụ thể các biến nội tại là toạ độ các đỉnh nếu là

đa giác, là đường bán kính và toạ độ tâm nếu là hình tròn, ...) nên nó chỉ có thể được

viết thành một lớp trừu tượng. Sau đó, người lập trình có thể tạo ra các lớp con chẳng

hạn như là lớp "tam_giac", lớp "hinh_tron", lớp "tu_giac",.... Và trong các lớp con này

người viết mã se cung cấp các dữ liệu nội tại (như là biến nội tại r làm bán kính và

hằng số nội tại Pi cho lớp "hinh_tron" và sau đó viết mã cụ thể cho các phương pháp

"tinh_chu_vi" và "tinh_dien_tich")

2.6. Truyền thông điệp

Thông điệp: là phương tiện để đối tượng này chuyển tới đối tượng khác, yêu

cầu đối tượng thực hiện một trong số các phương thức. Một thông điệp bao gồm 3

phần:

- Handle của đối tượng đích (đối tượng chủ)

- Tên phương thức cần thực hiện

- Các thông tin cần thiết khác (các đối số)

Khi hệ thống yêu cầu một đối tượng nào đó thực hiện một phương thức thì hệ

thống se gửi thông báo và các tham số cần thiết cho đối tượng đó. Sau khi kiểm tra

thông báo đó là hợp lệ hàm tương ứng với phương thức đó se được gọi thực hiện.

Page 14: LẬP TRÌNH HƯỚNG ĐỐI TƯỢNG

LẬP TRÌNH HƯỚNG ĐỐI TƯỢNG

Trang 14

2.7. Sự trừu tượng hoá (abstraction)

Sự trừu tượng hoá đã có trong lập trình cấu trúc khi chuyển sang hướng đối

tượng nó trở thành một khái niệm rất quan trọng.

Đây là khả năng của chương trình bỏ qua hay không chú y đến một số khía

cạnh của thông tin mà nó đang trực tiếp làm việc lên, nghĩa là nó có khả năng tập trung

vào những cốt lõi cần thiết. Nói cách khác sự trừu tượng hoá được xem như việc quan

sát một sự vật hiện tượng mà không cần biết các chi tiết không quan trọng bên trong

nó (trừu tượng hoá dữ liệu) và không quan tâm tới nó làm thế nào để thực hiện công

việc (sự trừu tượng hoá chức năng – Functional Abstraction). Người ta thường phân

nhóm sự vật, hiện tượng theo thuộc tính và hành vi, chỉ quan tâm tới những đặc điểm

quan trọng và bỏ qua các chi tiết không liên quan.

2.8. Sự đóng gói (encapsulation)

Sự đóng gói (Encapsulation) bao gồm hai khái niệm là sự bao đóng và che giấu

thông tin. Nó là cơ chế ràng buộc dữ liệu và các thao tác trên dữ liệu thành một thể

thống nhất gọi là lớp. Tính chất này không cho phép người sử dụng các đối tượng thay

đổi trạng thái nội tại của một đối tượng. Chỉ có các thao tác nội tại của đối tượng cho

phép thay đổi trạng thái của nó. Việc cho phép môi trường bên ngoài tác động lên các

dữ liệu nội tại của một đối tượng theo cách nào là hoàn toàn tuỳ thuộc vào người lập

trình. Đây là tính chất đảm bảo sự toàn vẹn của đối tượng.

Sự đóng gói cho phép người lập trình quyết định cái gì se được che giấu, cái gì

se được xuất hiện. Các ưu điểm của sự đóng kín là:

Quản ly sự phức tạp

Quản ly sự thay đổi

Bảo vệ dữ liệu

Ví dụ: với số tài khoản trong ngân hàng lưu trữ thì người gửi tiền không quan

tâm tới việc lưu trữ như nào (bằng máy tính hay bằng sổ sách), anh ta chỉ quan tâm tới

việc có rút được tiền khi cần hay không?

2.9. Tính kế thừa

Lớp bao gồm các khái niệm có quan hệ chặt che với nhau. Lớp có thể được sử

dụng để tạo ra các lớp khác thông qua việc kế thừa.

Kế thừa là một kỹ thuật cho phép người lập trình có được các lớp từ các lớp sẵn

có. Lớp nhận được hay còn gọi là lớp dẫn xuất (derived class) se kế thừa các phương

thức và dữ liệu của lớp cha (lớp cơ sở - base class), sau đó lớp này có thể bố sung các

phương thức hoặc định nghĩa lại các phương thức mà nó kế thừa. Các phương thức cha

se không bị ảnh hưởng bởi những sự sửa đổi này.

Page 15: LẬP TRÌNH HƯỚNG ĐỐI TƯỢNG

LẬP TRÌNH HƯỚNG ĐỐI TƯỢNG

Trang 15

Ví dụ, sự khác nhau của các loại xe đạp như xe đạp leo núi, xe đua, …là chúng

kế thừa các thuộc tính cơ bản của một chiếc xe đạp nhưng có những sự thay đổi về cấu

tạo để thích ứng với mục đích sử dụng. Ví dụ, xe đạp leo núi se có thêm các bánh răng.

Hình sau thể hiện sự kế thừa

Xe đạp

Xe leo núi Xe đua Xe 2 người đạp

Hình 1.5 Lập trình hướng đối tượng

1.10. Tính đa hình

Khái niệm đa hình thường được chỉ ra thông qua từ khoá “Một giao diện nhiều

phương thức” “one interface, multiple methods”. Nó đơn giản là khả năng đưa một

phương thức có cùng tên trong các lớp con. Các phương thức riêng lẻ có thể được thực

thi các nhiệm vụ giống nhau nhưng cung có thể khác khi kiểu của các đối số hoặc số

lượng đối số đưa vào khác nhau. Hay nói cách khác đó là khả năng một thông điệp có

thể thay đổi cách thực hiện của nó theo lớp cụ thể của đối tượng nhận thông điệp

Ví dụ như việc in tất cả các thuộc tính của một chiếc xe đạp. Thì với những loại

xe khác nhau việc in các thuộc tính cung se khác. Bằng việc sử dụng tính đa hình, các

phương thức khác nhau có thể đặt cùng tên nhưng chỉ cần khác đối số.

Bản chất của tính đa hình là dựa trên sự kết nối (binding), đó chính là quá trình

găn một phương thức với một hàm cụ thể. Khi sử dụng tính đa hình cho các phương

thức thì trình biên dịch chưa thể xác định hàm nào se tương ứng với phương thức nào

se được gọi. Khi một đối tượng nhận một thông điệp cần xác định thông điệp đó tương

ứng với hàm thành phần nào se thực thi hàm thành phần đó. Có hai loại kết nối là kết

nối sớm và kết nối muộn. Kết nối muộn hay còn gọi là kết nối lúc chạy (runtime

binding) là việc kết nối khi chạy chương trình, tức là lúc chạy chương trình hàm cụ thể

mới được xác định và gọi thực hiện.

Kết gán sớm là việc kết gán một thông báo với một hàm và thời điểm kết gán là

lúc biên dịch.

Page 16: LẬP TRÌNH HƯỚNG ĐỐI TƯỢNG

LẬP TRÌNH HƯỚNG ĐỐI TƯỢNG

Trang 16

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 toán

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 sẻ.

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,

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 lớp cơ sở và lớp dẫn xuất.

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 lập trình hướng đối tượng là:

1. Thông qua nguyên ly kế thừa, có thể loại bỏ những đoạn chương trình lặp lại

trong quá trình mô tả 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ể trao đổi với nhau nên việc thiết

kế và lập trình se được thực hiện theo qui 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 ly che giấu thông tin giúp người lập trình tạo ra được những đoạn

chương trình an toàn không bị thay đổ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 đặt hơn.

6. Những hệ thống hướng đối tượng dễ mở rộng, nâng cấp thành những hệ thống

lớn hơn

7. Kỹ thuật truyền thông báo trong việc trao đổi thông tin giữa các đối tượng giúp

cho việc mô tả giao diện với các hệ thống bên ngoài trở nên đơn giản hơn.

8. Có thể quản ly được độ phức tạp của những sản phẩm phần mềm.

Không phải trong hệ thống hướng đối tượng nào cung có tất cả các tính chất

trên. Khả năng có các tính chất đó còn phụ thuộc vào lĩnh vực ứng dụng của dự án tin

học và vào phương pháp thực hiện của người phát triển phần mềm.

5. Một số ngôn ngữ lập trình hướng đối tượng

Ngôn ngữ OOP đầu tiên là ngôn ngữ Smalltalk, sau đó các ngôn ngữ như

Object Pascal, Oject C, C++, Delphi, Java được ra đời.

Page 17: LẬP TRÌNH HƯỚNG ĐỐI TƯỢNG

LẬP TRÌNH HƯỚNG ĐỐI TƯỢNG

Trang 17

Lập trình hướng đối tượng không là đặc quyền của một ngôn ngữ nào đặc biệt.

Cung giống như lập trình có cấu trúc, những khái niệm trong lập trình hướng đối

tượng có thể cài đặt trong những ngôn ngữ lập trình như C hoặc Pascal,... Tuy nhiên,

đối với những chương trình lớn thì vấn đề lập trình se trở nên phức tạp. Những ngôn

ngữ được thiết kế đặc biệt, hô trợ cho việc mô tả, cài đặt các khái niệm của phương

pháp hướng đối tượng được gọi chung là ngôn ngữ đối tượng. Dựa vào khả năng đáp

ứng các khái niệm về hướng đối tượng, ta có chia ra làm hai loại:

1. Ngôn ngữ lập trình dựa trên hướng đối tượng

2. Ngôn ngữ lập trình hướng đối tượng

Lập trình dựa trên đối tượng là kiểu lập trình hô trợ chính cho việc bao gói, che

giấu thông tin và định danh các đối tượng. Lập trình dựa trên đối tượng có các đặc tính

sau:

Bao gói dữ liệu

Cơ chế che giấu và truy nhập dữ liệu

Tự động tạo lập và xoá bỏ đối tượng

Phép toán tải bội

Ngôn ngữ hô trợ cho kiểu lập trình trên được gọi là ngôn ngữ lập trình trên đối

tượng. Ngôn ngữ lớp này không hô trợ cho việc thực hiện kế thừa và liên kết động

chẳng hạn như Ada là ngôn ngữ dựa trên đối tượng.

Lập trình hướng đối tượng là kiểu lập trình dựa trên đối tượng và bổ sung thêm

nhiều cấu trúc để cài đặt những quan hệ về kế thừa và liên kết động. Vì vậy đặc tính

của lập trình hướng đối tượng có thể viết ngăn gọn như sau:

Các đặc tính dựa trên đối tượng + kế thừa + liên kết động.

Ngôn ngữ hô trợ cho những đặc tính trên được gọi là ngôn ngữ lập trình hướng

đối tượng, ví dụ như C++, Smalltalk, Object Pascal ....

Việc chọn ngôn ngữ để cài đặt phần mềm phụ thuộc nhiều vào các đặc tính và

yêu cầu của bài toán ứng dụng, vào khả năng sử dụng lại của những chương trình đã

có và vào tổ chức của nhóm tham gia xây dựng phần mềm.

Tóm lại, khi lựa chọn một ngôn ngữ lập trình hướng đối tượng bạn cần xem

xét các yếu tố sau:

- Dễ học

- Các lớp được định nghĩa sẵn

- Các công cụ và môi trường lập trình tích hợp

- Dễ debug

- Khả năng kiểm tra code

Page 18: LẬP TRÌNH HƯỚNG ĐỐI TƯỢNG

LẬP TRÌNH HƯỚNG ĐỐI TƯỢNG

Trang 18

- Tốc độ thực thi

- Quản ly bộ nhớ

- Truy cập tới các thư viện có sẵn

- Khả năng tạo ra những ứng dụng độc lập, có khả năng phân phối.

6. Một số ứng dụng của lập trình hướng đối tượng

Lập trình hướng đối tượng đang được ứng dụng để phát triển phần mềm

trong nhiều lĩnh vực khác nhau. Trong số đó, có ứng dụng quan trọng và nổi tiếng

nhất hiện nay là hệ điều hành Windows của hãng Microsoft được phát triển dựa

trên kỹ thuật lập trình hướng đối tượng. Một số lĩnh vực ứng dụng chính của kỹ

thuật lập trình hướng đối tượng bao gồm:

- Những hệ thống làm việc theo thời gian thực

- Trong lĩnh vực mô hình hoá hoặc mô phỏng các quá trình

- Các cơ sở dữ liệu hướng đối tượng

- Những hệ siêu văn bản, multimedia

- Lĩnh vực trí tuệ nhân tạo và các hệ chuyên gia

- Lập trình song song và mạng nơron

- Những hệ tự động hoá văn phòng và trợ giúp quyết định.

Page 19: LẬP TRÌNH HƯỚNG ĐỐI TƯỢNG

LẬP TRÌNH HƯỚNG ĐỐI TƯỢNG

Trang 19

Chương 2. GIỚI THIỆU VỀ C++

1. Lịch sử của C++

C++ về bản chất được xây dựng trên nền của ngôn ngữ lập trình C. Do đó,

chúng ta se băt đầu lướt qua lịch sử của C. Ngôn ngữ lập trình C được ra phát minh bởi

Dennis Ritchie vào năm 1972 tại phòng thí nghiệm Bell Telephone. Nó được thiết kế

nhằm mục đích tạo ngôn ngữ để viết hệ điều hành UNIX. Lịch sử của C và Unix găn

liền với nhau. Vì ly do này mà phần lớn hệ điều hành Unix được hoàn thành với C, chỉ

có một số thành phần là được viết bằng ngôn ngữ BCPL.

Ngôn ngữ lập trình C++ được phát minh bởi Bjarne Stroustroup. C++ băt đầu

năm 1979 với phiên bản đầu tiên gọi là "C with classes" - C có lớp, cái tên này có vẻ

không hợp ly nên đã đổi thành C++. Phiên bản đầu tiên của C++ được sử dụng vào

tháng 8 năm 1983 tại AT&T. Phiên bản thương mại đầu tiên ra măt năm 1985. Ngôn

ngữ C++ chuẩn được năm giữ bởi ANSI và ISO. Đó là ly do tại sao bạn hay nghe thấy

C++ chuẩn là ANSI C++ hoặc ISO C++

C++ là gì?

Bạn có thể thấy rằng ngôn ngữ lập trình C được phát triển đầu tiên sau đó C++

mới được phát triển. Bạn có thể tự hỏi, C++ nó là cái gì? Nó có mối quan hệ thế nào

với C. Câu trả lời là: C++ cơ bản là C ở một mức độ mới. Sự khác biệt quan trọng duy

nhất là C++ hô trợ hướng đối tượng. Các đoạn mã viết bằng C được dịch và chạy tốt

với hầu hết các chương trình dịch của C++ nhưng điều ngược lại không đúng. C++ hô

trợ tất cả các lệnh của C và có mở rộng

2. Các mở rộng của C++

Như chúng ta đã biết C++ là sự mở rộng của C tức là mọi khả năng, khái niệm

trong C đều dùng được trong C++. Các chương trình viết bằng C có phần mở rộng là

*.C để chạy vẫn chương trình này với trình biên dịch của C++ ta chỉ cần đổi phần mở

rộng thành *.CPP bởi phần mở rộng mặc định của các chương trình viết bằng C++ có

đuôi là .CPP

2.1. Lời chú thích

Để chú thích trong C ta sử dụng /*…*/ khi đó trình biên dịch se bỏ qua mọi thứ

có trong đó. Trong nhiều trường hợp ta chỉ chú thích trên một dòng.

Ví dụ:

/* Day la dong chu thich */

Ta thấy việc chú thích như vậy có vẻ mất thời gian, trong C++ đã bổ sung thêm

một kiểu chú thích mới bằng cặp //

Page 20: LẬP TRÌNH HƯỚNG ĐỐI TƯỢNG

LẬP TRÌNH HƯỚNG ĐỐI TƯỢNG

Trang 20

Ví dụ:

// Day la 1 dong chu thich.

Kiểu chú thích này thích hợp cho việc chú thích một dòng ngăn. Còn để chú

thích nhiều dòng ta nên dùng kiểu chú thích cu /*…*/

2.2. Từ khoá mới

Một số từ khoá đã được bổ đưa vào C++ nhằm bổ sung thêm một số tính năng

mới. Các từ khoá mới đó là:

Bảng 2.1 Từ khóa mới

asm catch class

delete friend inline

new operator private

protected public template

this throw try

virtual

Trong đó từ khoá new và delete là hai toán tử dùng để quản ly bộ nhớ động thay

cho các hàm cấp phát động malloc, free của C. Hàm inline cho phép tăng tốc độ thực

hiện chương trình. Các từ khoá khác như class, public, virtual … bạn se được tìm hiểu

ở các phần sau.

Nếu trong chương trình bạn viết bằng C có sử dụng các tên trùng với các từ

khoá trên thì bạn phải thay đổi trước khi dịch lại bằng C++ nếu không chương trình

dịch se báo lôi.

2.3. Kiểu dữ liệu char và int

Trong C một hằng kí tự được xem là nguyên nên nó có kích thước là 2 byte còn

trong C++ hằng kí tự được xem là kiểu char và có kích thước là 1 byte

Ví dụ:

sizeof(‘A’)=sizeof(int)=2

sizeof(‘A’)=sizeof(char)=1

2.4. Khai báo biến

Khi viết chương trình trong C, trước khi sử dụng biến ta phải khai báo và phải

đặt ở đầu khối hay đầu chương trình. Vị trí biến khác nhau, vị trí sử dụng khác nhau

Page 21: LẬP TRÌNH HƯỚNG ĐỐI TƯỢNG

LẬP TRÌNH HƯỚNG ĐỐI TƯỢNG

Trang 21

dẫn tới việc kiểm soát chương trình khó khăn. Để khăc phục khó khăn này C++ cho

phép người lập trình khai báo biến ở bất kỳ nơi nào trước khi sử dụng. Phạm vi hoạt

động của các biến kiểu này là khối trong đó biến được khai báo.

Ví dụ:

Chương trình sau nhập một dãy số thực rồi săp xếp theo thứ tự tăng dần:

#include <stdio.h>

#include <conio.h>

#include <alloc.h>

void main()

{

int n;

printf(“\nSo phan tu cua day n=”);

scanf(“%d”,&n);

float *x=(float*)malloc((n+1)*sizeof(float));

for (int i=0;i<n;i++)

{

printf(“\nX[%d]=”,i);

scanf(“%f”,x+i);

}

for(i=0;i<n;++i)

for(int j=i+1;j<n;++j)

if(x[i]>x[j])

{

Float tg=x[i];

x[i]=x[j];

x[j]=tg;

}

printf(“\n Day sau khi sap xep\n”);

for(i=0;i<n;++i)

printf(“%0.2f”,x[i]);

getch();

}

2.5. Chuyển đổi và ép kiểu

C++ cho phép người dùng đổi kiểu một cách rộng rãi, trình biên dịch tự động

thực hiện việc chuyển kiểu này:

- Khi gán một giá trị kiểu số vào một biến kiểu khác

- Các kiểu số khác nhau có thể có mặt trong cùng một biểu thức, và đặc biệt là

khi truyền đối số cho hàm.

Page 22: LẬP TRÌNH HƯỚNG ĐỐI TƯỢNG

LẬP TRÌNH HƯỚNG ĐỐI TƯỢNG

Trang 22

Khi C++ tự động đổi từ các đối tượng nhỏ thành các đối tượng lớn thì không có

vấn đề gì

Ví dụ chuyển từ kiểu short(16 bits) long(32 bits)

Nhưng khi chuyển ngược lại thì có thể có vấn đề:

Ví dụ: long (32bít) short(16bit): có thể mất dữ liệu

Trong trường hợp này trình biên dịch của C++ se phát sinh thông báo đối với

các chuyển đổi tự động gây mất dữ liệu

Nếu trong trường hợp không thể tự động chuyển kiểu người lập trình có thể ép

kiểu một cách tường minh như sau:

Ép kiểu kiểu cu (C): myInt = (int) myFloat

Ép kiểu kiểu mới (C++): myInt = int(myFloat)

Phép chuyển kiểu này có dạng như một hàm số chuyển kiểu đang được gọi.

Cách chuyển kiểu này thường được sử dụng trong thực tế.

Ví dụ:

Chương trình sau tính tổng S

Với n là một số nguyên dương nhập từ bàn phím.

#include <stdio.h>

#include <conio.h>

void main() {

int n;

printf("\n So phan tu cua day N=");

scanf("%d",&n);

float s=0.0;

for (int i=1;i<=n;++i)

s+= float(1)/float(i); //chuyen kieu

theo C++

printf("S=%0.2f",s); getch();

}

2.6. Vào ra trong C++

Để xuất dữ liệu ra màn hình và nhập dữ liệu từ bàn phím, trong C++ vẫn có thể

dùng hàm printf() và scanf(), ngoài ra trong C++ ta có thể dùng dòng nhập xuất chuẩn

để nhập/xuất dữ liệu thông qua hai biến đối tượng của dòng (stream object) là cout và

cin.

- Xuất dữ liệu

Page 23: LẬP TRÌNH HƯỚNG ĐỐI TƯỢNG

LẬP TRÌNH HƯỚNG ĐỐI TƯỢNG

Trang 23

Cú pháp:

cout<<biểu thức 1<<..<<biểu thức n;

Trong đó cout được định nghĩa trước như một đối tượng biểu diễn cho thiết bị

xuất chuẩn cho C++ là màn hình, cout được sử dụng kết hợp với toán tử chèn << để

hiển thị giá trị các biểu thức 1,2,.., N ra màn hình.

- Nhập dữ liệu

Cú pháp:

cin>>biến1>>…>>biến n;

Toán tử cin được định nghĩa trước như một đối tượng biểu diễn cho thiết bị vào

chuẩn cảu C++ là bàn phím, cin được sử dụng kết hợp với toán tử trích>> để nhập dữ

liệu từ bàn phím cho các biến 1,2,…,n

Chú ý:

Để nhập một chuôi không quá n ky tự và lưu vào mảng một chiều a (kiểu char)

có thể dùng hàm cin.get như sau: cin.get(a,n);

Toán tử nhập cin>> se để lại ky tự chuyển dòng’\n’ trong bộ đệm. Ky tự này có

thể làm trôi phương thước cin.get. Để khăc phục tình trạng trên cần dùng phương thức

cin.ignore(1) để bỏ qua một kí tự chuyển dòng.

Để sử dụng các loại toán tử và phương thức nói trên ta cần khai báo tập tin dẫn

hướng iostream.h

- Đinh dạng khi in ra màn hình

Để qui định số thực được hiển thị ra màn hình với p chữ số sau dấu chấm thập

phân, ta sử dụng đồng thời các hàm:

setiosflags(ios::showpoint);//bật cờ hiệu

setprecision(p)

Các hàm này cần đặt trong toán tử xuất như sau:

cout<<setiosflags(io::showpoint)<<setprecision(p);

Câu lệnh trên se có hiệu lực với tất cả các toán tử xuất tiếp theo cho tới khi gặp

một câu lệnh đinh dạng mới.

Để qui định độ rộng tối thiểu để hiện thị là k vị trí cho giá trị ( nguyên, thực,

chuôi) ta dùng hàm setw(k)

Hàm này cần đặt trong toán tử xuất và nó chỉ có hiệu lực cho một giá trị được in

gấn nhất. Các giá trị in tiếp theo se có độ rộng tối thiểu mặc định là 0, như vậy câu

lệnh:

Page 24: LẬP TRÌNH HƯỚNG ĐỐI TƯỢNG

LẬP TRÌNH HƯỚNG ĐỐI TƯỢNG

Trang 24

cout<<setw(6)<<”Khoa”<<”HTTT”

se in ra chuôi “ KhoaHTTT”

Ví dụ:

Chương trình sau cho phép nhập một danh sách không quá 100 thí sinh. Dữ liệu

môi thí sinh gồm họ tên, các điểm thi môn 1, môn 2, môn 3. Sau đó in danh sách thí

sinh theo thứ tự giảm dần của tổng điểm.

#include <iostream.h>

#include <conio.h>

#include <iomanip.h>

void main()

{

struct {

char ht[25];

float d1,d2,d3,td;

}ts[100],tg;

int n,i,j;

clrscr();

cout << "So thi sinh:";

cin >> n;

for (i=0;i<n;++i)

{

cout << "\n Thi sinh:"<<i;

cout << "\n Ho ten:";

cin.ignore(1);

cin.get(ts[i].ht,25);

cout << "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;

}

for (i=0;i<n-1;++i)

for(j=i+1;j<n;++j)

if(ts[i].td<ts[j].td)

{

tg=ts[i];

ts[i]=ts[j];

ts[j]=tg;

}

cout<< "\ Danh sach thi sinh sau khi sap xep :";

for (i=0;i<n;++i)

Page 25: LẬP TRÌNH HƯỚNG ĐỐI TƯỢNG

LẬP TRÌNH HƯỚNG ĐỐI TƯỢNG

Trang 25

{

cout<< "\n" <<

setw(25)<<ts[i].ht<<setw(5)<<ts[i].td;

}

getch();

}

Ví dụ Chương trình sau đây cho phép nhập một mảng thực cấp 50x50. Sau đó

in ma trận dưới dạng bảng và tìm một phần tử lớn nhất.

#include <iostream.h>

#include <iomanip.h>

#include <conio.h>

void main()

{

float a[50][50],smax; int m,n,i,j,imax,jmax;

clrscr();

cout<< "Nhap so hang va cot:";

cin>>m>>n;

for (i=0;i<m;++i)

for (j=0;j<n;++j)

{

cout<< "a["<<i<<","<<j<<"]=";

cin>> a[i][j];

}

smax= a[0][0];

imax=0;

jmax=0;

for (i=0;i<m;++i)

for (j=0;j<n;++j)

if (smax<a[i][j])

{

smax=a[i][j];

imax=i;

jmax=j;

}

cout << "\n\n Mang da nhap";

cout << setiosflags(ios::showpoint)<<setprecision(1);

for (i=0;i<m;++i)

for (j=0;j<n;++j)

{

if (j==0) cout<<"\n";

Page 26: LẬP TRÌNH HƯỚNG ĐỐI TƯỢNG

LẬP TRÌNH HƯỚNG ĐỐI TƯỢNG

Trang 26

cout << setw(6)<<a[i][j];

}

cout << "\n\n"<< "Phan tu max:"<< "\n";

cout << "co gia tri ="<<setw(6)<<smax;

cout<<"\nTai hang"<<imax<< " cot "<<jmax;

getch();

}

2.7. Cấp phát và giải phóng bộ nhớ

Trong C có thể sử dụng các hàm cấp phát bộ nhớ như malloc(), calloc() và hàm

free() để giải phóng bộ nhớ được cấp phát. C++ đưa thêm một cách thức mới để thực

hiện việc cấp phát bộ nhớ và giải phóng bộ nhớ bằng cách dùng hai toán tử new và

delete.

- Toán tử new để cấp phát bộ nhớ

Toán tử new thay cho hàm malloc() và calloc() của C có cú pháp như sau:

new Tên kiểu ;

hoặc new (Tên kiểu);

Trong đó Tên kiểu là kiểu dữ liệu của biến con trỏ, nó có thể là: các kiểu dữ

liệu chuẩn như int, float, double, char,... hoặc các kiểu do người lập trình định nghĩa

như mảng, cấu trúc, lớp,... Chú y: Để cấp phát bộ nhớ cho mảng một chiều, dùng cú

pháp như sau:

Biến con trỏ = new kiểu[n];

Trong đó n là số nguyên dương xác định số phần tử của mảng.

Ví dụ: float *p = new float; //cấp phát bộ nhớ cho biến con trỏ p có kiểu

float

int *a = new int[100]; //cấp phát bộ nhớ để lưu trữ mảng một chiều a

// gồm 100 phần tử

Khi sử dụng toán tử new để cấp phát bộ nhớ, nếu không đủ bộ nhớ để cấp phát,

new se trả lại giá trị NULL cho con trỏ. Đoạn chương trình sau minh họa cách kiểm tra

lôi cấp phát bộ nhớ:

double *p;

int n;

cout<< “\n So phan tu : ”;

cin>>n;

p = new double[n]

if (p == NULL)

Page 27: LẬP TRÌNH HƯỚNG ĐỐI TƯỢNG

LẬP TRÌNH HƯỚNG ĐỐI TƯỢNG

Trang 27

{

cout << “Loi cap phat bo nho”;

exit(0);

}

Toán tử delete

Toán tử delete thay cho hàm free() của C, nó có cú pháp như sau:

delete con trỏ ;

Ví dụ 2.6 Chương trình sau minh hoạ cách dùng new để cấp phát bộ nhớ chứa n

thí sinh. Môi thí sinh là một cấu trúc gồm các trường ht(họ tên), sobd(số báo danh), và

td(tổng điểm). Chương trình se nhập n, cấp phát bộ nhớ chứa n thí sinh, kiểm tra lôi

cấp phát bộ nhớ, nhập n thí sinh, săp xếp thí sinh theo thứ tự giảm của tổng điểm, in

danh sách thí sinh sau khi săp xếp, giải phóng bộ nhớ đã cấp phát.

#include <iomanip.h>

#include <iostream.h>

#include <stdio.h>

#include <conio.h>

#include <stdlib.h>

struct TS {

char ht[20];

long sobd;

float td;

};

void main(void)

{

TS *ts;

int n;

cout<<"\nSo thi sinh n = ";

cin>>n;

ts = new TS[n+1];

if (ts == NULL)

{

cout << "\n Loi cap phat vung

nho";

getch();

exit(0);

}

for (int i=0;i<n;++i)

{

cout << "\n Thi sinh thu "<<i;

Page 28: LẬP TRÌNH HƯỚNG ĐỐI TƯỢNG

LẬP TRÌNH HƯỚNG ĐỐI TƯỢNG

Trang 28

cout<< "\n Ho ten";

cin.ignore(1);

cin.get (ts[i].ht,20);

cout << "so bao danh";

cin>> ts[i].sobd;

cout<< "tong diem:";

cin>>ts[i].td;

}

for (i=0;i<n-1;++i)

for (int j=i+1;j<n;++j)

if (ts[i].td<ts[j].td)

{

TS tg=ts[i];

ts[i]=ts[j];

ts[j]=tg;

}

cout <<

setiosflags(ios::showpoint)<<setprecision(1);

for (i=0;i<n;++i)

cout << "\n" <<

setw(20)<<ts[i].ht<<setw(6)<<ts[i].td;

delete ts;

getch();

}

2.8. Biến tham chiếu

Trong C có 2 loại biến là: Biến giá trị dùng để chứa dữ liệu (nguyên, thực, ky

tự,...) và biến con trỏ dùng để chứa địa chỉ. Các biến này đều được cung cấp bộ nhớ và

có địa chỉ. C++ cho phép sử dụng loại biến thứ ba là biến tham chiếu. C++ cho phép

khai báo một biến tham chiếu dùng tham chiếu tới một biến tồn tại trong bộ nhớ. Khi

biến tham chiếu này được thay đổi thì lập tức biến được tham chiếu cung thay đổi.

Đây là một mở rộng rất tiện bởi nó cho phép hàm thao tác trực tiếp trên biến được

truyền. Biến tham chiếu là một tên khác (bí danh) cho biến đã định nghĩa trước đó. Cú

pháp khai báo biến tham chiếu như sau:

Kiểu &Biến tham chiếu = Biến;

Biến tham chiếu có đặc điểm là nó được dùng làm bí danh cho một biến (kiểu

giá trị) nào đó và sử dụng vùng nhớ của biến này.

Page 29: LẬP TRÌNH HƯỚNG ĐỐI TƯỢNG

LẬP TRÌNH HƯỚNG ĐỐI TƯỢNG

Trang 29

Ví dụ: Với câu lệnh: int a, &tong=a; thì tong là bí danh của biến a và biến

tong dùng chung vùng nhớ của biến a. Lúc này, trong mọi câu lệnh, viết a hay viết

tong đều có y nghĩa như nhau, vì đều truy nhập đến cùng một vùng nhớ. Mọi sự thay

đổi đối với biến tong đều ảnh 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ếu tham chiếu có thể tham gia 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.9. 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à thay đổi giá trị của vung nhớ mà nó tham chiếu

Ví dụ:

Int u=12,z

const int &p=y; //hằng tham chiếu p tham chiếu tới biến y

Page 30: LẬP TRÌNH HƯỚNG ĐỐI TƯỢNG

LẬP TRÌNH HƯỚNG ĐỐI TƯỢNG

Trang 30

p=p+1; //sai biên dịch se 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à 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.10. 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 se 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à se

thao tác trên các bản sao này chứ không xử ly 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 se 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ụ:

Nhập dãy số thực và săp xếp dãy theo thứ tự tăng dần.

#include <iostream.h>

#include <conio.h>

#include <stdio.h>

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])

Page 31: LẬP TRÌNH HƯỚNG ĐỐI TƯỢNG

LẬP TRÌNH HƯỚNG ĐỐI TƯỢNG

Trang 31

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ụ nhập 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 <iostream.h>

#include <conio.h>

#include <stdio.h>

#include <iomanip.h>

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 ";

Page 32: LẬP TRÌNH HƯỚNG ĐỐI TƯỢNG

LẬP TRÌNH HƯỚNG ĐỐI TƯỢNG

Trang 32

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();

}

Hàm trả về giá trị tham chiếu

Page 33: LẬP TRÌNH HƯỚNG ĐỐI TƯỢNG

LẬP TRÌNH HƯỚNG ĐỐI TƯỢNG

Trang 33

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ênhàm()

{

//thân hàm

return <biến phạm vi toàn cục>;

}

Trong truờng hợp này biểu thức được trả lại trong câu lệng 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 này se

bị mất đi khi kết thúc hàm. Do vậy tham chiếu của hàm se không còn y nghĩa gì 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 ly, 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 của một hàm có giá trị trả về là

một tham chiếu

Ví dụ:

#include <iostream.h>

#include <conio.h>

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ụ

#include <iostreams.h>

Page 34: LẬP TRÌNH HƯỚNG ĐỐI TƯỢNG

LẬP TRÌNH HƯỚNG ĐỐI TƯỢNG

Trang 34

#include <stdio.h>

#include <string.h>

#include <conio.h>

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 se 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.11. 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. Qui 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 khái báo nguyên mẫu hàm thì các hàm 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 lại cho các đối mặc định

trong dòng đầu của địng 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

}

Page 35: LẬP TRÌNH HƯỚNG ĐỐI TƯỢNG

LẬP TRÌNH HƯỚNG ĐỐI TƯỢNG

Trang 35

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ảu đị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);

2.12. Các hàm nội tuyến

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ương

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 cung 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ú y: 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ử ly hàm theo kiểu inline. Ví dụ hàm f() trong chương

trình sau se không phải là hàm nội tuyến vì inline viết sau lời gọi hàm.

Ví dụ:

Page 36: LẬP TRÌNH HƯỚNG ĐỐI TƯỢNG

LẬP TRÌNH HƯỚNG ĐỐI TƯỢNG

Trang 36

#include <iostream.h>

#include <conio.h>

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ó se 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 se

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ử ly

hàm theo kiểu nội tuyến. Từ khoá inline chỉ là một từ khoá gợi y cho chương

trình dịch chứ không phải là một mệnh lệnh băt buộc.

2.13. 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 se 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ụ:

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 <iostream.h>

#include <conio.h>

#include <iomanip.h>

void nhapds(int *x,int n);

void nhapds(double *x,int n);

Page 37: LẬP TRÌNH HƯỚNG ĐỐI TƯỢNG

LẬP TRÌNH HƯỚNG ĐỐI TƯỢNG

Trang 37

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];

}

}

}

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);

Page 38: LẬP TRÌNH HƯỚNG ĐỐI TƯỢNG

LẬP TRÌNH HƯỚNG ĐỐI TƯỢNG

Trang 38

maxd=max(x,nd);

cout<<"\n Max day so nguyen ="<<maxi;

cout<<"\n Max day so thuc="<<maxd;

getch();

}

Chú y: 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 se bị thông báo lôi khi biên dịch.

Ví dụ:

#include <iostream.h>

#include <conio.h>

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();

}

Page 39: LẬP TRÌNH HƯỚNG ĐỐI TƯỢNG

LẬP TRÌNH HƯỚNG ĐỐI TƯỢNG

Trang 39

Chương 3. LỚP VÀ ĐỐI TƯỢNG

1. Xây dựng lớp và đối tượng

Như chúng ta đã biết lớp là một mô tả trừu tượng của nhóm các đối tượng cùng

bản chất, cùng loại, ngược lại môi đối tượng lại là một thể hiện cụ thể của một lớp. Xét

về phương diện cấu trúc dữ liệu ta có thể xem lớp như là một kiểu dữ liệu mới do

người dùng định nghĩa, kiểu dữ liệu này tiên tiến hơn kiểu dữ liệu cấu trúc ở chô nó

không chỉ có dữ liệu mà còn bao gói cả các thao tác trên dữ liệu đó. Lúc này đối tượng

chính là các biến thể hiện cho kiểu dữ liệu.

Trong C++ ta khai báo lớp với cú pháp như sau:

1.1. Khai báo lớp

class <tên lớp>

{

[quyền truy xuất:]

<khai báo các thành phần riêng của lớp>

[quyền truy xuất:]

<khai báo các thành phần dùng chung>

};

Trong đó:

Tên lớp: do người lập trình đặt và phải tuân theo các qui tăc về tên

Các thành phần của lớp có thể là các dữ liệu, thuộc tính và phương thức

Quyền truy xuất: là khả năng truy xuất thành phần dữ liệu của các hàm thành phần:

o private: chỉ quyền truy xuất trong phạm vi lớp đó (bởi các hàm thành phần

trong lớp)

o public: được truy xuất ở mọi nơi nếu đối tượng đó tồn tại

o protected: chỉ được truy xuất trong phạm vi lớp đó và các lớp con kế thừa từ

nó.

Phạm vi tác động của 1 từ khóa quyền truy xuất kà từ lúc nó xuất hiện cho đến

hết khai báo lớp hoặc gặp từ khóa xác định quyền truy xuất khác.

Nếu không xác định quyền truy xuất thì ngầm định là private. Ở kiểu dữ liệu

cấu trúc, các thành phần dữ liệu đều có quyền truy xuất là public, nghĩa là các dữ liệu

này có quyền truy nhập ở mọi nơi. Đó là yếu điểm của kiểu dữ liệu cấu trúc, lớp đã

Page 40: LẬP TRÌNH HƯỚNG ĐỐI TƯỢNG

LẬP TRÌNH HƯỚNG ĐỐI TƯỢNG

Trang 40

khăc phục nhược điểm này bằng cách cho quyền truy xuất mặc định các thành phần dữ

liệu là private, chỉ có các hàm trong lớp mới có quyền truy xuất các thành phần dữ liệu

này.

Khai báo các thành phần:

- Dữ liệu: tương tự khai báo biến

<kiểu dữ liệu> <tên thành phần>

Chú y: khi khai báo thành phần dữ liệu không gán giá trị ban đầu, không được

khai báo từ khóa extern, register

- Các hàm thành phần:

Có hai cách để khai báo hàm thành phần:

+ Chỉ khai báo nguyên mẫu trong lớp và định nghĩa ngoài lớp

+ Định nghĩa hàm thành phần ngay trong khai báo lớp

Khai báo:

<kiểu hàm> <tên hàm>([khai báo tham số])

Khi khai báo hàm thành phần ngoài khai báo lớp ta dùng cú pháp sau:

<kiểu hàm> <tên lớp>::<tên hàm>([khai báo tham số])

{

<thân hàm>

}

Nhận xét: Ta thấy rằng khai báo lớp cung gần giống như khai báo cấu trúc, chỉ khác là

trong khai báo lớp có thêm các hàm thành viên và các nhãn phạm vi truy xuất private,

public. Sự khác nhau giữa lớp và cấu trúc là kiểu truy xuất mặc định của struct là

public còn kiểu truy xuất mặc định của lớp là private.

Ví dụ:

Xây dựng cấu trúc dữ liệu mô tả các sinh viên

- Thuộc tính: họ tên, ngày sinh, giới tính, lớp, dtb

- Các hàm: nhập, in danh sách

Lớp sinh viên

class SV

{

private:

char ht[30];

char ns[8];

Page 41: LẬP TRÌNH HƯỚNG ĐỐI TƯỢNG

LẬP TRÌNH HƯỚNG ĐỐI TƯỢNG

Trang 41

char lop[10];

float dtb;

public:

void nhap(){

cout<<”\n Nhap ho ten: “;

gets(ht);

cout<<”\n Nhap ngay sinh: “;

gets(ns);

cout<”\n Nhap diem trung binh:”;

cin>>dtb;

}

void in();

};

void SV::in(){

cout<<”\n Ho ten: “<<ht<<endl;

cout<<”\n Ngay sinh: “<<ns<<endl;

cout<”\n Diem trung binh:”<<dtb<<endl;

Ở ví dụ trên ta thấy các thành phần dữ liệu như họ tênm ngày sinh, lớp, điểm trung

bình có quyền truy xuất là private, nghĩa là chỉ có các hàm thành viên của lớp mới có

quyền truy xuất các thành phần dữ liệu này. Ngược lại, hai hàm thành viên nhập và in

một sinh viên có quyền truy xuất là public. Các lệnh hay người sử dụng bên ngoài

muốn truy nhập tới các thành phần dữ liệu thì phải thông qua việc sử dụng các hàm

thành viên này.

Ví dụ:

Xây dựng cấu trúc dữ liệu mô tả các hóa đơn mua bán vật tư:

- Thuộc tính: mã vật tư, tên vật tư, loại phiếu, khối lượng, đơn giá, thành tiền,….

- Hàm: nhập danh sách, in danh sách, xác định loại phiếu, tính tổng …

Lớp các hóa đơn

class hoadon

{

char mavt[5]

char tenvt[20]

byte lp;

int kl,dg,tt;

public:

void nhap();

void in()

Page 42: LẬP TRÌNH HƯỚNG ĐỐI TƯỢNG

LẬP TRÌNH HƯỚNG ĐỐI TƯỢNG

Trang 42

int loai();

int tinhtong();

};

Ví dụ này cho ta thấy rằng khi không khai báo quyền truy xuất thì quyền ngầm định

quyền truy xuất là private, tức là các dữ liệu thành viên như mã vật tư, tên vật tư, loại

phiếu, khối lượng, đơn giá, thành tiền có quyền truy xuất là private.

Ví dụ: xây dựng cấu trúc dữ liệu mô tả các phân số

- Dữ liệu: tử số, mẫu số

- Phương thức: nhập, in, tối giản

Lớp phân số

class PS

{

protected:

int tso, mso;

public:

void nhap();

void in();

protected:

void toigian();

};

void PS::nhap()

{

cout<<”Nhap tu so:<<”\n”;

cin>>tso;

do

cout<<”nhap mau so:”<<”\n”;

cin>>ms;

while (ms==0);

}

Ở ví dụ này, hai dữ liệu thành phần là tử số và mẫu số có quyền truy xuất là

protected. Hai hàm thành viên nhập và in có quyền truy xuất là public nhưng sau đó

hàm thành viên tối giản lại có quyền truy xuất là protected. Có nghĩa là dữ liệu và hàm

tối chỉ chỉ có thể được sử dụng bởi các thành phần trong lớp phân số và lớp con kế

thừa lớp phân số.

1.2. Khai báo đối tượng

Khi một lớp được định nghĩa, tên của nó có thể được sử dụng để khai báo đối

tượng theo cú pháp sau:

Page 43: LẬP TRÌNH HƯỚNG ĐỐI TƯỢNG

LẬP TRÌNH HƯỚNG ĐỐI TƯỢNG

Trang 43

<tên lớp> <tên đối tượng>;

Ví dụ:

Khai báo đối tượng sinh viên:

SV sv1,vs2;

sv1,sv2 là các đối tượng sinh viên

Khai báo đối tượng hóa đơn:

hoadon hd;

hd là một đối tượng hóa đơn

Chú y: một lớp có thể phát sinh ra nhiều đối tượng, môi đối tượng gọi là các thể

hiện (instant) của lớp đó, môi đối tượng có bản sao riêng về thành phần dữ liệu gọi là

các biến thể hiện. Thành phần dữ liệu của các đối tượng là độc lập nhau.

Các đối tượng dùng chung các phương thức của lớp

Kích thước một lớp bằng tổng kích thước các thành phần dữ liệu ( không tính

các hàm)

1.3. Truy xuất các thành phần của đối tượng

- Thành phần dữ liệu:

<tên đối tượng>.<tên tp dữ liệu>

Cần chú y rằng dữ liệu thành phần riêng chỉ có thể truy nhập bởi các hàm thành

phần của cùng một lớp, đối tượng của lớp cung không thể truy nhập.

Ví dụ:

sv1.ht

sv2.ns

cout<<”Chao ban “<<sv1.ht; sai do sv1.ht có quyền là private

sv2.nhap(); đúng

hoặc

<tên con trỏ><tên tp dữ liệu>;

- Thành phần hàm

<tên đối tượng>.<tên hàm>([ds đối số])

Ví dụ:

hoadon hd1;

hd1.nhap();

Page 44: LẬP TRÌNH HƯỚNG ĐỐI TƯỢNG

LẬP TRÌNH HƯỚNG ĐỐI TƯỢNG

Trang 44

hd1.in();

hoặc

<tên con trỏ><tên hàm>([ds đối số]);

Chú ý:

- Khi thực hiện một phương thức đối với một đối tượng nào đó thì hệ thống se ngầm

truyền vào trong thân phương thức đó một con trỏ có tên là this. Con trỏ này trỏ tới

chính đối tượng thực hiện phương thức.

Ví dụ:

a.nhap(); thì a là đối tượng ẩn

- Trong thân một phương thức khi truy xuất một thành phần mà không viết rõ

tên đối tượng thì se được hiểu thành phần đó của đối tượng ẩn. Viết tường minh se như

sau

kq.pt = pt + a.pt;

kq.pt = thispt + a.pt;

- Một truy xuất được xem là hợp lệ khi chỉ khi đúng cú pháp và đảm bảo đúng

quyền truy xuất

Ví dụ:

Xây dựng lớp phân số { tử số, mẫu số, nhập, in phân số, cộng hai phân số, tích

hai phân số}

Viết hàm main nhập 2 phân số a,b tính và in ra a+b, a*b

#include "iostream.h"

#include "math.h"

#include "conio.h"

class PS{

private:

int ts,ms;

public:

void nhap();

void in();

void toigian();

PS tong(PS);

PS tich(PS);

};

void PS::nhap(){

cout<<"Nhap tu so:";cin>>ts;

cout<<"\n Nhap mau so:";

Page 45: LẬP TRÌNH HƯỚNG ĐỐI TƯỢNG

LẬP TRÌNH HƯỚNG ĐỐI TƯỢNG

Trang 45

do

{

cin>>ms;

if(ms==0)cout<<"\nMau so phai khac 0\n";

}

while(ms==0);

toigian();

}

void PS::in()

{

if(ts==0)cout<<0;

else if(ms==-1) cout<<-ts;

else if(ms==1) cout<<ts;

else if(ts*ms<0) cout<<-

abs(ts)<<"/"<<abs(ms);

else cout<<abs(ts)<<"/"<<abs(ms)<<endl;

}

void PS::toigian()

{

int a=abs(ts);

int b=abs(ms);

if(a!=0)

{

while(a!=b)

{

if(a>b) a=a-b;

else b-=a;

}

ts/=a;

ms/=a;

}

}

PS PS::tong(PS a){

PS kq;

kq.ts=ts*a.ms+ms*a.ts;

kq.ms=ms*a.ms;

kq.toigian();

return kq;

}

Page 46: LẬP TRÌNH HƯỚNG ĐỐI TƯỢNG

LẬP TRÌNH HƯỚNG ĐỐI TƯỢNG

Trang 46

PS PS::tich(PS a){

PS kq;

kq.ts=ts*a.ts;

kq.ms=ms*a.ms;

kq.toigian();

return kq;

}

void main(){

PS a,b;

a.nhap();b.nhap();

a.cong(b).in();

a.tich(b).in();

getch();

}

2. Các phương thức

Một đối tượng thường có 4 kiểu hành vi cơ bản sau: tạo, truy vấn, cập nhập và

hủy. Để cài đặt cho các hành vi trên ta có thể sử dụng các phương thức sau:

- Các phương thức tạo – Constructor

Dùng để khởi tạo một thể hiện của lớp

- Các phương thức truy vấn – Queries

Dùng để hỏi về dữ liệu của lớp

- Các phương thức cập nhập – Updates

Dùng để cập nhập, thay đổi trạng thái của đối tượng

- Các phương thức hủy – Destructor

Dùng để dọn dẹp, thu hồi bộ nhớ khi hủy một đối tượng

2.1. Hàm khởi tạo - Constructor

Khi người lập trình khai báo biến thì các giá trị chứa trong biến đó thường là

chưa xác định, trong rất nhiều trường hợp ta phải đảm bảo là các biến phải được khởi

tạo giá trị nào đó trước khi dùng. Tương tự với các lớp trong C++ các thành viên dữ

liệu cần được khởi tạo trước khi sử dụng các phương thức tác động lên, nhưng như ta

đã biết trong C++ không cho phép khởi tạo thành phần dữ liệu ngay khi khai báo trong

lớp. Để khởi tạo các thành phần dữ liệu ta sử dụng hàm khởi tạo Constructor

Page 47: LẬP TRÌNH HƯỚNG ĐỐI TƯỢNG

LẬP TRÌNH HƯỚNG ĐỐI TƯỢNG

Trang 47

Hàm khởi tạo là một hàm thành phần đặc biệt của lớp cho phép khởi tạo đối

tượng; nó được gọi thực hiện một cách tự động khi đối tượng phát sinh.

Khai báo hàm khởi tạo

Cú pháp:

Khai báo trong lớp:

<tên lóp>([ds tham số])

Định nghĩa:

<tên lớp>::<tên lớp>([ds tham số])

{

//thân hàm

}

Ví dụ:

class PS

{

int tso, mso;

public:

PS()

{

tso=0;

mso=1;

}

PS(int ts, int ms)

{

tso=ts;

mso=ms;

}

void nhap();

void in();

void toigian();

};

PS a,b; //Gọi hàm khởi tạo thứ nhất cho a,b

PS c(1,5); //Gọi hàm khởi tạo thứ 2 cho c

PS *d; //khai báo con trỏ chưa phát sinh đối tượng

Như vậy:

Page 48: LẬP TRÌNH HƯỚNG ĐỐI TƯỢNG

LẬP TRÌNH HƯỚNG ĐỐI TƯỢNG

Trang 48

- Hàm khởi tạo có với mọi lớp

- Tên hàm giống như tên lớp

- Hàm tạo không có kiểu nên không cần khai báo

- Hàm tạo không có giá trị trả về

- Nếu lập trình viên không xây dựng hàm khởi tạo thì chương trình se tự động

sinh ra hàm khởi tạo mặc định

- Không được phép gọi các hàm khởi tạo một cách tường minh bởi chúng được

gọi tự động khi khai báo các thể hiện của lớp.

Một số hàm khởi tạo:

- Hàm khởi tạo mặc định (default constructor): là hàm khởi tạo được gọi khi đối

tượng được khai báo mà không có đối số

- Hàm khởi tạo sao chép (copy destructor)là hàm khởi tạo đặc biệt khi ta gọi đối

tượng mới là bản sao của đối tượng đã có sẵn

Khai báo:

<tên lớp>([const] <tên lớp> & <tên tham số>);

ví dụ:

class PS

{

int tso, mso;

public:

PS(){int ts=0;int ms=1;};

PS(const PS &);

void nhap();

void in();

void toigian();

}

PS::PS(const PS & a)

{

tso=a.tso;

mso=a.mso;

}

void main()

{

PS p;

PS q(p); //Tạo q theo p

}

Page 49: LẬP TRÌNH HƯỚNG ĐỐI TƯỢNG

LẬP TRÌNH HƯỚNG ĐỐI TƯỢNG

Trang 49

2.2. Hàm hủy – Destructor

Hàm hủy là một hàm thành phần của lớp với chức năng hủy bỏ, giải phóng các

đối tượng khi nó hết phạm vi tồn tại.

Khai báo:

tên: ~<tên lớp>

Hàm hủy không có đối, không có giá trị trả về và không thể định nghĩa lại

Hàm hủy trùng tên với lớp và có dấu ~ ở trước.

Tác động: hàm destructor được gọi khi đối tượng bị hủy bỏ tức là khi sự thực

hiện chương trình rời khỏi phạm vi mà trong đó đối tượng của lớp được khởi tạo. Thực

chất hàm destructor không hủy bỏ đối tượng mà nó chỉ thực hiện một số công việc

trước khi hệ thống giải phóng bộ nhớ.

Mọi lớp đều có hàm hủy do đó nếu người lập trình không định nghĩa tường

minh hàm hủy thì chương trình dịch se tự phát sinh một hàm hủy mặc định.

Ví dụ:

class VT

{

int spt;

int *a;

public:

VT2(int n);

VT2(const VT2 &);

~VT2()

{

delete []a;

}

};

Nếu

p:con trỏ p = new int delete p;

q: mảng q= new int[n] delete []q;

Tóm lại, hai hàm constructor và destructor là hai hàm thành phần đặc biệt cho

phép khởi tạo và hủy bỏ đối tượng. Hai hàm này được gọi một cách tự động. Thứ tự

các hàm này được gọi phụ thuộc vào thứ tự trong đó sự thực hiện vào và rời khỏi

phạm vi mà các đối tượng được khởi tạo. Một cách tổng quát, các destructor được gọi

theo thứ tự ngược với thứ tự của các constructor được gọi. Các constructor được gọi

Page 50: LẬP TRÌNH HƯỚNG ĐỐI TƯỢNG

LẬP TRÌNH HƯỚNG ĐỐI TƯỢNG

Trang 50

của các đối tượng khai báo trong phạm vi toàn cục trước bất kỳ hàm nào (bao gồm

hàm main()) trong file mà băt đầu thực hiện. Các destructor tương ứng được gọi khi

hàm main() kết thúc hoặc hàm exit() được gọi. Hoặc khi ta dùng con trỏ kiểu đối

tượng, khi ta khai báo và xin cấp phát bộ nhớ cho con trỏ, hàm constructor se được

gọi, còn khi ta giải phóng vùng nhớ mà con trỏ đang chiếm giữ bằng lệnh delete , thì

hàm destructor se được gọi.

Ví dụ:

class A{

public:

A()

{

cout<<” \nHam khoi tao!”;

}

A(A &a)

{

cout<<”\nHam khoi tao sao chep!”;

}

~A()

{

cout<<”\nHam huy!”;

}

};

void main()

{

A a;

{

A b;

A *c;

}

cout<<”\nKet thuc ham main”;

}

Kết quả thực hiện se như sau:

Ham khoi tao! //a

Ham khoi tao! //b

Ham huy! //b

Ket thuc ham main

Ham huy! //a

Page 51: LẬP TRÌNH HƯỚNG ĐỐI TƯỢNG

LẬP TRÌNH HƯỚNG ĐỐI TƯỢNG

Trang 51

Ta xét trường hợp cấp phát động như sau:

void main()

{

A a;

{

A b;

A *c;

c= new A;

}

}

Khi đó kết quả thực hiện se như sau:

Ham khoi tao! //a

Ham khoi tao! //b

Ham khoi tao! //c

Ham huy! //b

Ket thuc ham main

Ham huy! //a

3. Đa năng hóa tóan tử

Với các kiểu dữ liệu chuẩn thì C++ cung cấp cho chúng ta các toán tử +, -, *, /

… để thực hiện các phép toán. Ví dụ

int a, b, c;

a=b + c;

Mã lệnh như trên là hiển nhiên đúng trong C++, kể từ khi các kiểu dữ liệu mới

do người dùng định nghĩa được thêm vào thì việc chùng ta thực hiện các phép tóan

trên kiểu dữ liệu đó là không còn hiển nhiên đúng. Ví dụ:

struct {

string san_pham;

float gia;

} a, b, c;

a=b+c;

Trong thực tế, khi dịch chương trình dịch se báo lôi. Để giải quyết vấn đề này

trong khi khai báo lớp chúng ta thêm vào đó các phương thức thỏa mãn.

Page 52: LẬP TRÌNH HƯỚNG ĐỐI TƯỢNG

LẬP TRÌNH HƯỚNG ĐỐI TƯỢNG

Trang 52

Ví dụ

class Vat_tu{

string san_pham;

float gia;

public:

void nhap();

void in();

san_pham cong();

};

san_pham a,b,c;

a = b.cong(c);

Tuy nhiên cách viết như vậy không chuẩn cho lăm, chúng ta luôn muốn viết

theo kiểu

a=b+c;

Trong C++ đã đáp ứng yêu cầu này bằng cách cho phép ta nạp chồng toán tử.

Nghĩa là các toán tử cơ bản chỉ dùng được cho các kiểu cơ bản nay đã áp dụng được

cho các kiểu do người dùng định nghĩa.

Sau đây là danh sách các toán tử có thể nạp chồng.

+ - * / = < > += -= *= /= << >>

<<= >>= == != <= >= ++ -- % & ^ ! |

~ &= ^= |= && || %= [] () , ->* -> new

delete new[] delete[]

Để nạp chồng toán tử ta sử dùng từ khóa operator theo cú pháp sau:

<kiểu trả vê> operator <tên tóan tử>([danh sách tham số])

{

/*…*/

}

Ví dụ: về việc nạp chồng tóan tử operator(+) để cộng hai vec tơ

// vectors: overloading operators example

#include <iostream>

#include <conio.h>;

class Vector {

public:

int x,y;

Vector () {};

Page 53: LẬP TRÌNH HƯỚNG ĐỐI TƯỢNG

LẬP TRÌNH HƯỚNG ĐỐI TƯỢNG

Trang 53

Vector (int,int);

Vector operator + (Vector);

};

Vector::Vector (int a, int b) {

x = a;

y = b;

}

Vector Vector::operator+ (Vector p) {

Vector temp;

temp.x = x + p.x;

temp.y = y + p.y;

return (temp);

}

int main () {

Vector a (3,1);

Vector b (1,2);

Vector c;

c = a + b;

cout << c.x << "," << c.y;

return 0;

}

Chú ý:

- Chỉ định nghĩa lại được các toán tử được chấp nhận bởi C++ ( các toán tử ở bảng

trên)

- Không thể làm thay đổi độ ưu tiên của các toán tử

- Đối với các toán tử 2 ngôi thì tóan hạng bên trái là đối tượng ẩn còn đối tượng bên

phải là đối số. Do đó số tham số của hàm toán tử bằng số toán hạng - 1

- Đối với các toán tử một ngôi, không có đối số thì toán hạng (!) chính là đối tượng

ẩn

Cách gọi hàm toán tử

Có hai cách dùng các hàm toán tử:

- Dùng như cú pháp thông thường của phép toán

Ví dụ

PS a, b,c;

Page 54: LẬP TRÌNH HƯỚNG ĐỐI TƯỢNG

LẬP TRÌNH HƯỚNG ĐỐI TƯỢNG

Trang 54

c = a+b;

- Dùng như hàm thành phần của đối tượng

Ví dụ:

PS a, b, c;

c = a.operator+(b);

Xây dựng lớp phân số có hai toán tử + và *

class PS

{

public:

int tso, mso;

public:

void nhap();

void in();

void toigian();

PS operator + (PS);

PS operator *(PS);

};

PS PS::operator + (PS a)

{

PS kq;

kq.tso = tso*a.mso + mso*a.tso;

kq.mso = mso*a.mso;

kq.toigian();

return kq;

}

PS PS::operator * (PS a)

{

PS kq;

kq.tso = tso*a.tso;

kq.mso = mso*a.mso;

kq.toigian();

return kq;

}

void main()

{

PS a,b,c;

//nhap a,b

c= a+b;

Page 55: LẬP TRÌNH HƯỚNG ĐỐI TƯỢNG

LẬP TRÌNH HƯỚNG ĐỐI TƯỢNG

Trang 55

cout<<”a+b=”; c.in();

c= a.operator*(b);

cout<<”a*b=”; c.in();

}

Đa năng hóa các toán tử chèn dòng (<<) và trích dòng (>>)

Chúng ta có thể đa năng hóa các toán tử chèn dòng << và trích dòng >> . Hàm

toán tử của toán tử << được đa năng hóa được khai báo như sau:

ostream & operator << (ostream & stream, ClassName Object);

Hàm toán tử << trả về tham chiếu chỉ đến dòng xuất ostream. Tham số thứ nhất

của hàm toán tử << là một tham chiếu chỉ đến dòng xuất ostream, tham số thứ hai là

đối tượng được chèn vào dòng. Khi sử dụng, dòng trao cho toán tử << (tham số thứ

nhất) là toán hạng bên trái và đối tượng được đưa vào dòng (tham số thứ hai) là toán

hạng bên phải. Để bảo đảm cách dùng toán tử << luôn nhất quán, chúng ta không thể

định nghĩa hàm toán tử << như là hàm thành viên của lớp đang xét, thông thường nó

chính là hàm friend.

- Hàm toán tử của toán tử >> được đa năng hóa được khai báo như sau:

istream & operator >> (istream & stream, ClassName Object);

Hàm toán tử >> trả về tham chiếu chỉ đến dòng nhập istream. Tham số thứ nhất

của hàm toán tử này là một tham chiếu chỉ đến dòng nhập istream, tham số thứ hai là

đối tượng của lớp đang xét mà chúng ta muốn tạo dựng nhờ vào dữ liệu lấy từ dòng

nhập. Khi sử dụng, dòng nhập đóng vai toán hạng bên trái, đối tượng nhận dữ liệu

đóng vai toán hạng bên phải. Cung như trường hợp toán tử <<, hàm toán tử >> không

là hàm thành viên của lớp, thông thường nó chính là hàm friend.

VD:Xây dựng lớp phân số và các toán tử >>,<<,+,-

#include<iostream.h>

class PS

{

privave:

int tso, int mso;

public:

PS ( );

PS (int,int );

PS operator + (PS);

PS operator( );

friend istream & operator>>(istream & is,PS & ps);

friend ostream & operator>>(ostream & os,PS & ps);

};

Page 56: LẬP TRÌNH HƯỚNG ĐỐI TƯỢNG

LẬP TRÌNH HƯỚNG ĐỐI TƯỢNG

Trang 56

istream & operator>>(istream & is,PS & ps)

{

cout<<”Nhap tu so:”;

is>>ps.tso;

cout<<”Nhap mau so:”;

is>>ps.mso;

return is;

};

ostream & operator>>(ostream & os,PS & ps)

{

os<<ps.tso<<”/”<<ps.mso;

return os;

};

PS::PS()

{

tso=0;

mso=1;

};

PS::PS(int TS,int MS)

{

tso=TS;

mso=MS;

};

PS PS::operator + (PS ps)

{

PS Tong;

Tong.tso=tso*ps.mso+mso*ps.tso;

Tong.mso=mso*ps.mso;

return Tong;

}

PS PS::operator-()

{

tso = - tso;

return *this;

};

int main()

{

PS a(3,4),b,c;

cout<<”Nhap phan so b:”<<endl;

cin>>;

cout<<”a=”<<a<<endl;

Page 57: LẬP TRÌNH HƯỚNG ĐỐI TƯỢNG

LẬP TRÌNH HƯỚNG ĐỐI TƯỢNG

Trang 57

cout<<”b=”<<b<<endl;

c=a+b;

cout<<”c=”<<c<<endl;

-c;

cout<<”-c=”<<c<<endl;

return 0;

}

4. Mảng và con trỏ của lớp

Một điều quan trọng mà bạn đã nhận ra, khi bạn tạo ra một lớp, nó thực sự là

một kiểu dữ liệu mới, cung giống như các kiểu dữ liệu đã có (int, float, char, …). Rõ

ràng lớp phức tạp hơn kiểu int và float, nhưng nó vẫn chỉ là một kiểu dữ liệu và bất kỳ

những gì bạn có thể làm với kiểu dữ liệu chuẩn thì cung có thể làm với một lớp. Có

nghĩa là bạn có thể tạo mảng các lớp và con trỏ tới các lớp. Ví dụ sau đây mô tả một

mảng của lớp. Ví dụ này cung sử dụng một con trỏ trỏ tới mảng của lớp, để nhập dữ

liệu vào một lớp.

#include <iostream>

class sinhvien

{

public:

char hodem[30];

char ten[7];

float dtb;

void hienthi();

};

void sinhvien::hienthi()

{

cout << "*****Thong tin Sinhvien*****\n";

cout << "Ho Dem: " << hodem << endl;

cout << "Ten:" << ten << endl;

cout << "Diem trung binh: " << dtb << endl;

}

int main()

{

sinhvien sv[5];

sinhvien *ptr = sv;

int i;

for (i = 0;i<5;i++)

{

Page 58: LẬP TRÌNH HƯỚNG ĐỐI TƯỢNG

LẬP TRÌNH HƯỚNG ĐỐI TƯỢNG

Trang 58

cout << "Nhap ho dem cua sinh vien: \n";

cin>>ptr->hodem;

cout << "Nhap ten sinh vien: \n";

cin >> ptr->ten;

cout << "Nhap diem trung binh: \n";

cin >> ptr->dtb;

ptr++;

}// het vong lap.

for(i=0;i<5;i++)

{

sv[i].hienthi();

}

return 0;

}

Một mảng của lớp không khác gì mảng các số nguyên integer. Tất cả các quy

tăc áp dụng cho mảng vẫn được áp dụng. Điều tương tự cung đúng cho con trỏ. Khi

tạo một lớp, nó se chiếm một phần bộ nhớ. Khi bạn tạo con trỏ trỏ tới một lớp, nó trỏ

tới byte đầu tiên của vùng nhớ mà lớp chiếm.

Chú y cuối cùng là từ khóa: this. Trong hầu hết các ngôn ngữ lập trình hướng

đối tượng, this thay thế cho thể hiện hiện tại của một lớp bạn đang dùng. Khi một hàm

thành viên tham chiếu thành viên khác của lớp cho đối tượng cụ thể của lớp đó, làm

thế nào C++ bảo đảm rằng đối tượng thích hợp được tham chiếu? Câu trả lời là môi

đối tượng duy trì một con trỏ trỏ tới chính nó – gọi là con trỏ this – Đó là một tham số

ẩn trong tất cả các tham chiếu tới các thành viên bên trong đối tượng đó. Con trỏ this

cung có thể được sử dụng tường minh. Môi đối tượng có thể xác định địa chỉ của chính

mình bằng cách sử dụng từ khóa this. Con trỏ this được sử dụng để tham chiếu cả các

thành viên dữ liệu và hàm thành viên của một đối tượng. Kiểu của con trỏ this phụ

thuộc vào kiểu của đối tượng.

VD: chương trình sau minh họa sử dụng tường minh của con trỏ this để cho

phép một hàm thành viên của lớp Test in dữ liệu X của một đối tượng Test.

#include <iostream.h>

class Test

{

int x;

public:

Test(int i= 0);

void Print();

Page 59: LẬP TRÌNH HƯỚNG ĐỐI TƯỢNG

LẬP TRÌNH HƯỚNG ĐỐI TƯỢNG

Trang 59

};

Test::Test(int i) {

x = i;

}

void Test::Print() {

cout << " x = " << x << endl;

cout<< " this->x = " << this->x << endl;

cout<< "(*this).x = " << (*this).x << endl;

void main()

{

Test t(12);

t.Print();

};

Kết quả chương trình:

x = 12;

this->x = 12;

(*this).x = 12;

5. Các hàm bạn và lớp bạn

Như chúng ta đã biết, các thành viên được khai báo là private hay protected

không thể được truy xuất từ bên ngoài mà chỉ các hàm thành viên của lớp đó mới được

truy xuất. Tuy nhiên, luật này se không có hiệu lực nếu ta sử dụng thêm từ khóa friend.

Friends là các hàm hoặc các lớp được khai báo với từ khóa friend ở trước.

5.1. Hàm bạn (friend function)

Khái niệm

Một hàm friend của một lớp được khai báo bên trong lớp và được định nghĩa

bên ngoài phạm vi của lớp đó, có quyền truy cập đến các thành viên private hoặc

protected của một lớp. Tuy nhiên nó không phải là hàm thành viên của lớp.

Khai báo

Một hàm se trở thành hàm bạn của một lớp nếu trong lớp đó có khai báo

friend <kiểu trả về> <tên hàm>([danh sách tham số]);

Ví dụ:

class SP

{

float pt,pa;

Page 60: LẬP TRÌNH HƯỚNG ĐỐI TƯỢNG

LẬP TRÌNH HƯỚNG ĐỐI TƯỢNG

Trang 60

public:

void nhap();

void in();

friend float getpa(SP);

};

float getpa(SP x)

{

return x.pa;

}

void main()

{

SP x;

x.nhap();

cout<<”Phan ao cua x la:”<<getpa(x);

}

Ta thấy hàm getpa là hàm bạn của lớp SP, nó trả về phần ảo của một đối tượng

bất kỳ.

Ví dụ 2 :

Khai báo một hàm bạn tongdientich là bạn của 2 lớp hinhchunhat va hinhtron,

có chức năng tính tổng diện tích của hình chữ nhật và hình tròn.

class hinhtron;

class hinhchunhat {

int cd,cr;

public:

hinhchunhat(int cd1, int cr1);

int tinhdientich( );

friend float tongdientich(hinhchunhat hcn, hinhtron

ht);

};

class hinhtron {

int r;

public:

hinhtron(int r1);

float tinhdientich( );

friend float tongdientich(hinhchunhat hcn, hinhtron ht);

};

Page 61: LẬP TRÌNH HƯỚNG ĐỐI TƯỢNG

LẬP TRÌNH HƯỚNG ĐỐI TƯỢNG

Trang 61

hinhchunhat :: hinhchunhat(int cd1, int cr1)

{

cd = cd1;

cr = cr1;

};

int hinhchunhat :: tinhdientich( )

{

return cd*cr;

};

hinhtron :: hinhtron (int r1)

{

r = r1;

};

float hinhtron :: tinhdientich( )

{

return 3.14*r*r;

};

float tongdientich(hinhchunhat hcn, hinhtron ht)

{

float t;

t = hcn.tinhdientich() + ht.tinhdientich() ;

return t ;

};

Chú ý :

- Không hạn chế số hàm bạn

- Hàm bạn không phải hàm thành viên nên không bị ảnh hưởng bởi từ khóa truy xuất

5.2. Lớp bạn

Lớp A là bạn của lớp B nếu trong B chứa khai báo :

friend class A ;

Nếu A là bạn của B thì mọi hàm thành phần trong A đều trở thành bạn của B

Page 62: LẬP TRÌNH HƯỚNG ĐỐI TƯỢNG

LẬP TRÌNH HƯỚNG ĐỐI TƯỢNG

Trang 62

Như vậy nếu vế trái là đối tượng thuộc lớp đang xét (A) thì một hàm f nào đó

có thể là thành phần có thể là bạn

Nếu vế trái không là đối tượng thuộc lớp đang xét (A) thì hàm f phải là hàm

bạn.

Ví dụ :

Xây dựng lớp số phức với các tóan tử >> , <<

class SP

{

float pt,pa ;

public :

friend istream& operator>>(istream &, SP &)

friend ostream& operator<<(ostream & , const

SP);

};

istream &operator>>(istream &is, SP &a)

{

cout<<”Nhap phan thuc:”;

is>>a.pt;

cout<<”\n Nhap phan ao:”;

is>>a.pa;

return is;

}

ostream &operator<<(ostream &os, SP a)

{

os<<a.pt<<”+”<<a.pa<<”i”;

return os;

}

void main()

{

SP a;

cout<<”Nhap a:\n”;

cin>>a;

cout<<”So phuc vua nhap:”<<a;

}

Page 63: LẬP TRÌNH HƯỚNG ĐỐI TƯỢNG

LẬP TRÌNH HƯỚNG ĐỐI TƯỢNG

Trang 63

6. Thành phần tĩnh

6.1. Các thành phần dữ liệu tĩnh

Khái niệm:

Thành phần dữ liệu tĩnh là một thành phần dữ liệu của lớp nhưng lại không

“găn” cụ thể với một đối tượng nào mà được dùng chung cho toàn bộ lớp. Nói cách

khác thành phần dữ liệu tĩnh chỉ có một biến thể hiện trên toàn bộ lớp (các đối tượng

của lớp)

Khai báo:

Để khai báo một thành phần dữ liệu là thành phần dữ liệu tĩnh ta sử dụng từ

khóa static như sau:

static <kiểu dữ liệu> <tên thành phần>;

Ví dụ:

Xây dựng lớp thí sinh ngoài các thuộc tính cá nhân như SBD, họ tên, điểm ..

cần thêm một thành phần để ghi số lượng đối tượng thí sinh trong danh sách.

Nhận xét: Số lượng là một thành phần nhưng chỉ có một thể hiện chung cho tất

cả các đối tượng trong lớp thí sinh. Vậy số lượng là thành phần dữ liệu tĩnh.

class TS

{

int sbd;

char ht[30];

float d1,d2,d3;

static int sl;

public:

friend istream& operator>>(istream &is, TS &a);

friend ostream& operator<<(ostream &os, TS &a);

};

Để truy xuất các thành phần dữ liệu tĩnh:

<tên đối tượng>.<tên thành phần >

<tên lớp>::<tên thành phần >

Chú ý:

- Vì thành phần dữ liệu tĩnh không găn với một đối tượng cụ thể nên nó tồn tại

ngay khi chưa có đối tượng nào.

- Thành phần dữ liệu tĩnh phải được gán giá trị ban đầu (khởi tạo) trước khi đối

tượng của lớp được phát sinh và phải khởi tạo ngoài mọi hàm theo cú pháp sau:

Page 64: LẬP TRÌNH HƯỚNG ĐỐI TƯỢNG

LẬP TRÌNH HƯỚNG ĐỐI TƯỢNG

Trang 64

<kiểu dữ liệu><tên lớp>::<tên thành phần dữ liệu tĩnh>=<giá trị>;

int TS::sl=0;

Xây dựng lớp Hóa đơn có một thành phần dữ liệu tĩnh để kiểm soát số đối

tượng HĐ được cấp phát.

class hoadon

{

char mavt[5]

char tenvt[20]

byte lp;

int kl,dg,tt;

public:

static int sl;

hoadon(int k=0, int d=0, int t=0)

{

kl=k ;

d=dg ;

t=tt ;

sl++ ;

}

void nhap();

void in()

int loai();

int tinhtong();

~hoadon()

{

sl--;

}

};

int hoadon::sl=0;

void main()

{

hoadon a,b,c;

cout<<”So hoa don hien co la:”<<a.sl;

}

6.2. Các hàm thành viên tĩnh

Xét trường hợp sau:

Xây dựng lớp SV có các phương thức:

- nhập 1 sinh viên

Page 65: LẬP TRÌNH HƯỚNG ĐỐI TƯỢNG

LẬP TRÌNH HƯỚNG ĐỐI TƯỢNG

Trang 65

- in một sinh viên

- Nhập một danh sách sinh viên

- In một danh sách sinh viên.

Ta thấy rằng hai phương thức đầu được dùng cho một đối tượng cụ thể, còn hai

phương thức sau là thao tác chung trên lớp không găn với một đối tượng cụ thể nào.

Để làm được như vậy C++ cung cấp cho chúng ta khái niệm phương thức tĩnh.

Khái niệm:

Phương thức tĩnh là hàm thành phần của lớp nhưng không găn với một đối

tượng cụ thể nào, nó dùng để thao tác chung cho lớp, trong thân hàm không có đối

tượng ẩn.

Khai báo

static <kiểu hàm> <tên hàm>([danh sách tham số]);

Ví dụ:

Quản ly danh sách sinh viên bằng danh sách liên kết với yêu cầu:

SV( ht, lop, dtb)

- con trỏ next, head (tĩnh)

- số lượng các SV (tĩnh)

Phương thức:

- Nhập, in 1 sinh viên

- Nhập, in danh sách sinh viên

- In số lượng sinh viên

class SV

{

char ht[30], lop[10];

float dtb;

SV *next;

static SV *head;

static int sl;

public:

void nhap();

void in();

static void nhapds();

static void inds();

static int getsl()

Page 66: LẬP TRÌNH HƯỚNG ĐỐI TƯỢNG

LẬP TRÌNH HƯỚNG ĐỐI TƯỢNG

Trang 66

{

return sl;)

}

};

int SV::sl=0;

SV *SV::head=NULL;

void SV::nhapds()

{

int i,tl;

SV *p;

i=1;

do

{

cout <<”Nhap sinh vien thu <<i<<”:”;

p=new SV;

pnhap();

pnext=head;

head=p;

sl++;

cout<<”Nhap nua khong?”;

cin>>tl;

}

while (tl=1);

}

void SV::inds()

{

cout<<”\n Danh sacg sinh vien:\n”;

SV *p=head;

while(p)

{

pin();

p=pnext;

cout<<endl;

}

}

void main()

{

cout<<”Danh sach co:”<<SV::getsl();

cout<<”\n Nhap danh sach:\n”;

SV::nhapds();

Page 67: LẬP TRÌNH HƯỚNG ĐỐI TƯỢNG

LẬP TRÌNH HƯỚNG ĐỐI TƯỢNG

Trang 67

cout<<”\n Danh sach vua nhap la:\n”;

SV::inds();

cout<<”Danh sach co: “<<SV::getsl();

}

7. Thành phần hằng

7.1. Dữ liệu hằng

Khái niệm:

Là một thành phần dữ liệu của một lớp mà không thể thay đổi giá trị trong thời

gian tồn tại của đối tượng

Ví dụ:

Với lớp SV thì mã sinh viên là thành phần dữ liệu hằng

Với lớp nhân sự thì số CMT là thành phần dữ liệu hằng

Khai báo:

const <kiểu dữ liệu > <tên thành phần >

Ví dụ:

class SV

{

char ht[30];

const char masv[5];

float dtb;

char lop[10];

public:

};

-Khởi tạo giá trị hằng:

Thành phần hằng của một đối tượng chỉ được gán giá trị tại thời điểm hàm khởi

tạo của nó được gọi . Hay nói cách khác nó phải được khởi tạo tại dòng khởi tạo đối

tượng (sau tên hàm khởi tạo, trước khi vào thân của hàm khởi tạo).

Cú pháp:

<tên lớp>::<tên lớp>([danh sách tham số]):<tên thành phần hằng>([đối

số]),…

{

<thân hàm khởi tạo>

Page 68: LẬP TRÌNH HƯỚNG ĐỐI TƯỢNG

LẬP TRÌNH HƯỚNG ĐỐI TƯỢNG

Trang 68

}

Ví dụ với lớp SV

SV::SV(char *msv):masv(msv){…}

Ví dụ:

class SV

{

const int masv;

char ht[30];

char lop[10]

float dtb;

public:

SV(int m=0);

friend istream& operator>>(istream &is, SV &a);

friend ostream & operator<<(ostream &os, SV &a);

};

SV::SV(int m):masv(m)

{

ht[0]=’\0’;

lop[0]=’\0’;

dtb=0;

}

istream & operatorr>>(istream & is, SV &a)

{

cout<<”\Nhap thong tin cho sinh vien co

ma:”<<a.masv;

cout<<”\n Ho ten:”; gets(a.ht);

cout<<”\n Lop:”;gets(a.lop);

cout<<”\nDiem tb:”;is>>a.dbt;

return is;

}

ostream& operator<<(ostream& os, SV &a)

{

...

}

void main()

{

int m;

cout<<”Nhap ma sinh vien”; cin>>m;

SV a(m) ;

Page 69: LẬP TRÌNH HƯỚNG ĐỐI TƯỢNG

LẬP TRÌNH HƯỚNG ĐỐI TƯỢNG

Trang 69

cin>>a ;

cout<<" Sinh vien vua nhap :" ; cout>>a ;

}

7.2. hàm thành phần (phương thức hằng)

Phương thức hằng là các hàm thành phần không có khả năng thay đổi thành

phần dữ liệu trong đối tượng.

Ví dụ :

Lớp PS hàm in() là phương thức hằng

class PS

{

int tso, mso ;

public:

void nhap();

void in() const;

void toigian();

};

void PS::in()const

{

cout<<tso<<”/”<<mso;

}

8. Thành phần là đối tượng

Đối tượng thành phần là các thành phần dữ liệu có kiểu là một lớp khác. Khai

báo các thành phần như sau:

<tên lớp> <tên thành phần>;

Ví dụ:

- thành phần ngày sinh của lớp SV là đối tượng của lớp date

- Thành phần cha mẹ của đối tượng SV là thành phần của lớp nhân sự

class date

{

int ngay;

int thang;

int nam;

};

class nguoi

{

Page 70: LẬP TRÌNH HƯỚNG ĐỐI TƯỢNG

LẬP TRÌNH HƯỚNG ĐỐI TƯỢNG

Trang 70

char ht[30];

char qq[50];

date ns;

public:

nguoi(char *hten=” “, char *q=” “);

..

} ;

class SV

{

const int masv ;

char ht[30] ;

char lop[10] ;

float dtb ;

nguoi cha, me ;

public :

SV(char *h, int m, char *tencha, char *tenme) ;

} ;

Chú ý :

- Việc nhập các thành phần như nhập các đối tượng thông thường

- Khi phát sinh một đối tượng của lớp bao phải đảm bảo mọi thành phần của nó đều

được phát sinh

- Nếu lớp thành phần có hàm khởi tạo không đối số hoặc có đối nhưng đối số có giá

trị ngầm định thì hàm khởi tạo của lớp bao có thể gọi tự động đến hàm khởi tạo của

lớp thành phần để phát sinh đối tượng thành phần đó.

- Ngược lại, hàm khởi tạo của lớp thành phần có tham số thì hàm khởi tạo của lớp

bao phải gọi tường minh tới hàm khởi tạo của lớp thành phần.

Ví dụ :

class A

{

int x ;

public:

A(int k)

{

x=k;

}

};

Page 71: LẬP TRÌNH HƯỚNG ĐỐI TƯỢNG

LẬP TRÌNH HƯỚNG ĐỐI TƯỢNG

Trang 71

class B

{

int y;

A t;

public:

B(int t1, int t2)

{

}

}

B b1; // Lôi do không có đối số

B b2(4,5); //Lôi do không khởi tạo được t

Nếu viết

B(int t1, int t2):t(t2)

{

...

}

thì khai báo B b2(4,5) mới thỏa mãn

Như vậy thành phần đối tượng được khởi tạo tại dòng khởi tạo tương tự như các

thành phần là hằng. Thứ tự thực hiện khởi tạo theo thứ tự khai báo thành phần trong

lớp.

Page 72: LẬP TRÌNH HƯỚNG ĐỐI TƯỢNG

LẬP TRÌNH HƯỚNG ĐỐI TƯỢNG

Trang 72

Chương 4. TÍNH KẾ THỪA

Chương trước giới thiệu về hướng đối tượng và tìm hiểu về các khái niệm sự

trừu tượng và đóng gói. Tuy nhiên, bạn vẫn chưa thấy được sức mạnh của lập trình

hướng đối tượng, sức mạnh đó thể hiện qua sự kế thừa. Sự kế thừa là một quá trình

cho phép một lớp kế thừa hoặc nhận các thành phần public và protected từ một lớp

khác. Thông quá kế thừa bạn có thể tạo ra các lớp tương đương mà không phải viết lại

code mà những code này được sử dụng thường xuyên. Bạn se thấy, trong chương này,

C++ mang lại sức mạnh tuyệt đối trong kế thừa. Điều đó rất quan trọng bởi chỉ một số

ngôn ngữ lập trình hô trợ kế thừa một cách hoàn chỉnh.

Như chúng ta đã biết việc sử dụng lại phần mềm cho phép người lập trình tiết

kiệm thời gian trong quá trình phát triển phần mềm. Khả năng kế thừa giúp cho người

lập trình thực hiện việc này. Có nhiều loại kế thừa sau đây ta se đi vào tìm hiểu hại loại

kế thừa chính là đơn kế thừa và đa kế thừa

1. Khái niệm

Kế thừa là khả năng cho phép xây dựng một lớp mới (lớp con, lớp dẫn xuất)

được thừa hưởng các thành phần từ một hay nhiều lớp đã có (lớp cha, lớp cơ sở).

Trong lớp dẫn xuất ta có thể bổ sung thêm các thành phần mới hoặc định nghĩa lại các

thành phần từ lớp cơ sở.

Ví dụ :

-Xây dựng lớp Phân số PS1:{ tử số, mẫu số, nhập, in, tối giản phân số}

-Xây dựng lớp phân số PS2: {tử số, mẫu số, nhập, in, tối giản, cộng, trừ, nhân,

chia phân số}

Xây dựng lớp người

Xây dựng lớp SV: lớp SV kế thừa từ lớp người

Xây dựng lớp GIÁO VIÊN: lớp GV kế thừa từ lớp người

Kế thừa tạo ra một mô hình phân cấp :

Page 73: LẬP TRÌNH HƯỚNG ĐỐI TƯỢNG

LẬP TRÌNH HƯỚNG ĐỐI TƯỢNG

Trang 73

Đơn kế thừa: là sự kế thừa trong đó một lớp dẫn xuất có một lớp cơ sở

Đa kế thừa: là sự kế thừa trong đó một lớp dẫn xuất có nhiều lớp cơ sở

2. Khai báo lớp dẫn xuất

Cú pháp:

class <lớp dẫn xuất>:[kiểu dẫn xuất] <lớp cơ sở 1 >,

[kiểu dẫn xuất] <lớp cơ sở 2>,…

{

//Khai báo lớp dẫn xuất

};

Trong đó:

- Kiểu dẫn xuất (kiểu kế thừa): gồm có:

-public

-private

C

A B

PS1

PS2

NGƯỜI

SV GV

Page 74: LẬP TRÌNH HƯỚNG ĐỐI TƯỢNG

LẬP TRÌNH HƯỚNG ĐỐI TƯỢNG

Trang 74

-protected

Ví dụ:

class NGUOI

{

char ht[30];

char noisinh[40];

unsigned namsinh;

public:

void nhap();

void in()

{

cout<<ht<<’\t’<<namsinh<<’\t’<<noisinh;

}

};

void NGUOI::nhap()

{

cout<<”Nhap ho ten”;

cin.getline(ht,30);

cout<<”Nhap noi sinh:”;

cin.getline(noisinh,40);

cout<<”Nhap nam sinh:”;

cin>>namsinh;

}

class SV:public NGUOI

{

char lop[10];

float dtb;

public:

void nhap();

void in()

{

NGUOI::in();

cout<<lop<<’\t’<<dtb<<endl;

}

};

void SV::nhap()

{

NGUOI::nhap();

cout<<”Nhap lop:”;

cin.getline(lop,10);

Page 75: LẬP TRÌNH HƯỚNG ĐỐI TƯỢNG

LẬP TRÌNH HƯỚNG ĐỐI TƯỢNG

Trang 75

cout<<”Nhap diem trung binh:”;

cin>>dtb;

}

void main()

{

SV a;

cout<<”Nhap sinh vien\n”;

a.nhap();

cout<<”Thong tin ve sinh vien:\n”;

a.in();

}

Như ta thấy, kích thước của lớp dẫn xuất se bằng tổng kích thước của lớp cở sở

+ thành phần dữ liệu mới.

3. Các kiểu kế thừa

Ở ví dụ trên lớp SV kế thừa lớp người bằng từ khóa public. Đó là hai kiểu kế

thừa chính: private và public ngoài ra còn kiểu kế thừa là protected. Mặc định các lớp

dẫn xuất se kế thừa các lớp cơ sở theo kiểu private, muốn kế thừa theo kiểu public ta

phải khai báo tường minh như ở trên để trình biên dịch biết. Kiểu kế thừa chi phối

quyền truy xuất tới các phần tử của các lớp cơ sở khác nhau. Khi sử dụng kế thừa

public, mọi thành phần được khai báo ở lớp cơ sở là private se trở thành private ở lớp

con. Tương tự, các thành phần khai báo là public ở lớp cha thì ở lớp con cung se là

public.

Khi sử dụng kế thừa kiểu private thì lại khác

Mọi thành phần trong lớp cha khi sang lớp con se trở thành private. Bạn cung

có thể xem thêm kiểu kế thừa protected thông qua hình trên.

Ví dụ:

Nhập đoạn code sau và lưu dưới tên bankacc.h

class bankaccount

{

protected:

float balance;

public:

Page 76: LẬP TRÌNH HƯỚNG ĐỐI TƯỢNG

LẬP TRÌNH HƯỚNG ĐỐI TƯỢNG

Trang 76

float withdraw(float);

float deposit(float);

void displaybalance();

bankaccount();

};

bankaccount::bankaccount()

{

balance = 1100;

}

float bankaccount::withdraw(float amount)

{

balance -= amount;

return balance;

}

float bankaccount::deposit(float amount)

{

balance += amount;

return balance;

}

void bankaccount::displaybalance()

{

cout << "Your balance is " << balance << endl;

}

class checking:public bankaccount

{

};

class savings:public bankaccount

{

};

Sau đó nhập đoạn code sau và lưu dưới tên bankacc-1.cpp

#include "bankacc.h"

#include <iostream>

// function prototypes

void menu();

// declare an instance of the checking class

checking mychecking;

int main()

{

menu();

return 0;

Page 77: LẬP TRÌNH HƯỚNG ĐỐI TƯỢNG

LẬP TRÌNH HƯỚNG ĐỐI TƯỢNG

Trang 77

}

void menu()

{

// declare other variables

int menuchoice;

float amount, balance;

// display choices

cout << "****** Bank Checking Account*******"<<

endl;

cout << endl;

cout << "1. Check Balance "<< endl;

cout << "2. Make Deposit "<< endl;

cout << "3. Make withdrawal " << endl;

cout << "4. Exit " << endl;

cout << "Please enter your choice " << endl;

// input selection

cin >> menuchoice;

// do a switch based on selection

switch(menuchoice)

{

case 1:

mychecking.displaybalance();

cout << endl;

menu();

break;

case 2:

cout << "Please enter the amount to deposit \n";

cin>> amount;

balance = mychecking.deposit(amount);

cout << "New balance is " << balance << endl;

cout << endl;

menu();

break;

case 3:

cout << "Please enter the amount to withdraw

\n";

cin >> amount;

balance = mychecking.withdraw(amount);

cout << "New balance is " << balance << endl;

cout << endl;

menu();

Page 78: LẬP TRÌNH HƯỚNG ĐỐI TƯỢNG

LẬP TRÌNH HƯỚNG ĐỐI TƯỢNG

Trang 78

break;

case 4:

return;

break;

default:

cout << "Your choice was invalid \n";

menu(); // re display the menu

}// end of switch

}

Bây giờ bạn có thể bạn có thế kế thừa phương thức và thuộc tính từ lớp cha như

thế nào. Ví dụ này cung có giá trị thực tế vì nó cung cấp cơ sở cho việc tạo một

chương trình ngân hàng thực tế. Vấn đề chú y trong ví dụ này là thực tế bạn có thể

thấy phương thức public được kế thừa bởi lớp con tồn tại như thế nào trong lớp con,

nó như là một phần của lớp. Đó là toàn bộ mục đính cho việc sử dụng kế thừa. Định

nghĩa của kế thừa đề cập tới việc một lóp nhập bản sao của một lớp khác. Bạn có thể

băn khoăn đó có nghĩa gì. Public và protected đều là quyền truy xuất, Chúng xác định,

thành phần bên ngoài lớp có thể được truy xuất như thế nào các phương thức hoặc

thuộc tính trong lớp.

4. Định nghĩa lại quyền truy xuất

Như chúng ta đã biết quyền truy xuất một thành phần của lớp con phụ thuộc

vào quyền truy xuất đó ở lớp cha và kiểu dẫn xuất. Trong nhiều trường hợp ta muốn

thay đổi quyền truy xuất của một thành phần lớp cha với lớp con. Xét ví dụ sau:

class A{

private: f1,f2;

protect: f3,f4;

public: f5,f6;

};

class B:A

{

public:

}

Kết quả f1f6: private

Ta muốn f6 có quyền truy xuất là public thì cần phải làm thế nào.

Page 79: LẬP TRÌNH HƯỚNG ĐỐI TƯỢNG

LẬP TRÌNH HƯỚNG ĐỐI TƯỢNG

Trang 79

Để xác định lại quyền truy xuất một thành phần của lớp cha trong lớp con thì

chỉ cần liệt kê thành phần đó sau từ khoá quyền truy xuất tương ứng.

<quyền truy xuất> :

<tên lớp cơ sở>::<tên thành phần>;

Ví dụ:

Class B:A

{

public:

A::f6;

}

Khi đó f6 là public

Ta lại xét trường hợp sau:

class A

{

int x;

protected:

int y,z;

public:

void f1();

void f1(int);

void f2();

};

class B:A

{

protected:

int t;

A::y;

public:

A::f1();

void g();

Page 80: LẬP TRÌNH HƯỚNG ĐỐI TƯỢNG

LẬP TRÌNH HƯỚNG ĐỐI TƯỢNG

Trang 80

};

Ở đây ta có có một số hàm cùng tên vậy khi định nghĩa lại quyền truy xuất 1

thành phần thì các thành phần cùng tên khác có ảnh hưởng không? Câu trả lời là có, vì

khi định nghĩa lại quyền truy xuất của một thành phần thì tất các thành phần có cùng

tên đều bị tác động. Nhưng nếu các thành phần cùng tên mà khác quyền truy xuất thì

ta không thể định nghĩa lại quyền truy xuất. Chúng ta cung chỉ có thể định lại quyền

truy xuất xuất theo đúng quyền của thành phần đó trong lớp cơ sở. Trong trường hợp

lớp con có một thành phần trùng tên với lớp cha thì thành phần của lớp con se che phủ

lớp cha, tức là khi truy xuất thành phần này thì mặc định se là thành phần của lớp con.

Do đó muốn truy xuất thành phần của lớp cha thì phải truy xuất tường minh.

<tên đối tượng>.<tên lớp cơ sở>::<tên thành phần>

Ví dụ: xây dựng lớp TS gồm: sbd, họ tên, ngày sinh, khu vực, phương thức

nhập, in

Xây dựng lớp TSA kế thừa từ lớp TS: bổ sung thêm điểm toán, ly, hoá

Xây dựng lớp TSC kế thừa lớp TS: bổ sung điểm văn, sử địa

Hàm main:

- Nhập một sanh sách các TS

- In danh sách theo từng khối

- In danh sách trúng tuyển theo từng khối

class TS

{

int sbd, kv;

char hoten[30];

date ns;

public:

void nhap();

void in();

};

class TSA: public TS

{

float dt, dl, dh;

public:

Page 81: LẬP TRÌNH HƯỚNG ĐỐI TƯỢNG

LẬP TRÌNH HƯỚNG ĐỐI TƯỢNG

Trang 81

void nhap();

void in();

};

class TSC:public TS

{

float dvan,dsu,ddia;

public:

void nhap();

void in();

};

5. Hàm khởi tạo và hàm hủy của lớp cơ sở

a. Hàm khởi tạo

Các thành phần của lớp cơ sở đều được lớp dẫn xuất kế thừa, tuy nhiên hàm

khởi tạo của lớp cơ sở se không được kế thừa. Việc gọi hàm khởi tạo của lớp dẫn xuất

se kéo theo gọi hàm khởi tạo của lớp cơ sở, bởi có thế xem một đối tượng của lớp dẫn

xuất là một đối tượng của lớp cơ sở. Thứ tự thực hiện se là: hàm khởi tạo của lớp cơ sở

trước, sau đó tới hàm khởi tạo của lớp dẫn xuất. Ví dụ:

class A

{

public:

A(){cout<<”Ham khoi tao cua A\n”;}

};

class B: public A

{

public:

B(){ cout<<”Ham khoi tao cua B\n”;}

};

void main()

{ B b;

}

Khi đó kết quả thực hiện se là:

Page 82: LẬP TRÌNH HƯỚNG ĐỐI TƯỢNG

LẬP TRÌNH HƯỚNG ĐỐI TƯỢNG

Trang 82

Ham khoi tao cua A

Ham khoi tao cua B

Tuy nhiên, đó là trong trường hợp hàm khởi tạo mặc định. Nếu ta xây dựng lại

hàm khởi tạo của lớp dẫn xuất thì trong định nghĩa của nó ta phải gọi hàm khởi tạo của

lớp cơ sở một cách tường minh như sau:

Cú pháp:

<tên lớp dẫn xuất>([ds tham số]):<tên lớp cơ sở>([ds đối số])

{

//thân hàm khởi tạo

}

b hàm hủy

Không viết lệnh gọi hàm hủy của lớp cơ sở một cách tường minh mà hàm hủy

của lớp cơ sở được gọi tự động khi hàm hủy của lớp dẫn xuất thực hiện xong.

Trình tự thực hiện:

class B:A1, A2 { }

B b; A1() hủy ~B()

A2() ~A2()

B() ~A1()

Theo nguyên tăc: hàm khởi tạo của lớp cơ sở thực hiện trước rồi mới đến lớp

dẫn xuất

thứ tự của các hàm khởi tạo của lớp cơ sở theo thứ tự kế thừa

Thứ tự của hàm hủy ngược với thứ tự hàm khởi tạo.

Ví dụ:

Xây dựng lớp NGUOI, SV, GV theo sơ đồ kế thừa

Hàm main: nhập 1 danh sách SV,GIÁO VIÊN vào một mảng

NGƯỜI

SV GV

Page 83: LẬP TRÌNH HƯỚNG ĐỐI TƯỢNG

LẬP TRÌNH HƯỚNG ĐỐI TƯỢNG

Trang 83

class NGUOI

{

char ht[30];

char noisinh[40];

unsigned namsinh;

public:

void nhap();

void in();

};

void NGUOI::nhap()

{

cout<<”Nhap ho ten”;

cin.getline(ht,30);

cout<<”Nhap noi sinh:”;

cin.getline(noisinh,40);

cout<<”Nhap nam sinh:”;

cin>>namsinh;

}

class SV:public NGUOI

{

char lop[10];

float dtb;

public:

void nhap();

void in()

{

NGUOI::in();

cout<<lop<<’\t’<<dtb<<endl;

}

};

void SV::nhap()

{

NGUOI::nhap();

cout<<”Nhap lop:”;

cin.getline(lop,10);

cout<<”Nhap diem trung binh:”;

cin>>dtb;

}

class GV:public NGUOI

{

char donvi[10];

Page 84: LẬP TRÌNH HƯỚNG ĐỐI TƯỢNG

LẬP TRÌNH HƯỚNG ĐỐI TƯỢNG

Trang 84

float hsl;

public:

void nhap();

void in()

{

NGUOI::in();

cout<<donvi<<’\t’<<hsl<<endl;

}

};

void GV::nhap()

{

NGUOI::nhap();

cout<<”Nhap don vi:”;

cin.getline(donvi,10);

cout<<”Nhap he so luong:”;

cin>>hsl;

}

void main()

{

NGUOI *ds[10];

int n,i,c;

cout<<”Nhap so doi tuong trong danh sach\n”;

cin>>n;

cout<<”Thong tin ve sinh vien:\n”;

a.in();

}

6. Đa kế thừa

C++ hô trợ đa kế thừa một cách đầy đủ nhất so với các ngôn ngữ lập trình

hướng đối tượng cùng loại. Đa kế thừa (Multiple Inheritance) là khả năng cho phép

một lớp kế thừa từ nhiều hơn một lớp cơ sở. Bạn có thể dễ dàng dẫn xuất từ nhiều hơn

một lớp thông qua việc viết các lớp cơ sở trong danh sách ngăn cách nhau bởi dấu

phẩy.

class <lớp dẫn xuất>:public lớp cs1,public lớp cs2, public lớp cs3

{

};

Page 85: LẬP TRÌNH HƯỚNG ĐỐI TƯỢNG

LẬP TRÌNH HƯỚNG ĐỐI TƯỢNG

Trang 85

Ví dụ:

Ví dụ sau mô tả đa kế thừa, nhập đoạn code sau và lưu vớ tên tax.h

class baseclassl

{

public:

float computetax(float);

} ;

float baseclass1::computetax(float amount)

{

return amount * .075f;

}

class baseclass2

{

public:

float computededuction(float);

};

float baseclass2::computededuction(float amount)

{

return amount - (amount *.10f);

}

class derivedclass:public baseclassl,public

baseclass2

{

} ;

Nhập đoạn code sau và lưu với tên tax-1.cpp

#include "tax.h"

#include <iostream>

int main()

{

derivedclass myclass;

float answer, amount;

cout <<"Enter the amount to tax \n";

Page 86: LẬP TRÌNH HƯỚNG ĐỐI TƯỢNG

LẬP TRÌNH HƯỚNG ĐỐI TƯỢNG

Trang 86

cin>> amount;

answer = myclass.computetax(amount);

cout << "The tax is " << answer << endl;

cout << "Enter the amount to reduce \n";

cin >> amount;

answer = myclass.computededuction(amount);

cout << "The amount after deduction is "<< answer <<

endl;

return 0;

}

Như bạn thấy trong ví dụ trên, bạn có thể sử dụng đa thừa kế để nhận các hàm

mà bạn cần kế thừa từ nhiều hơn một lớp cơ sở. Mục đích của lập trình hướng đối

tượng là có thể sử dụng lại các đoạn mã. Điều này chỉ có thể thực hiện thông qua việc

sử dụng đơn kế thừa và đa kế thừa.

Đa kế thừa có thể là tính năng rất mạnh tuy nhiên đôi khi nó lại gây ra một số

vấn đề. Cho rằng bạn đang kế thừa từ một lớp cớ sở A và lớp cơ sở B, hai lớp này đều

có một hàm có tên là funca(). Khi lớp dẫn xuất gọi hàm funca(), thì hàm nào se được

gọi? Câu trả lời cho vấn đề này đơn giản là nạp chồng bất cứ hàm nào mà cả các lớp

cơ sở phổ biến. Về cơ bản bạn sử dụng kỹ thuật đa năng hóa hàm ở chương trước.

7. Lớp cơ sở ảo và nhập nhằng trong đa kế thừa

Xét trường hợp sau:

Giả sử trong lớp A có thành phần x, lớp B cung có thành phần x

Xây dựng lớp D kế thừa từ A và B

Theo nguyên ly kế thừa ta thấy trong lớp D se có hai thành phần x. Khi truy

xuất thành phần x trong D trình biên dịch se không biết thành phần x đó là x của lớp A

hay lớp B. Tức là có sự nhập nhằng trong kế thừa.

Để giải quyết sự nhập nhằng này ta có thể xác định phạm vi tường minh. Ví dụ

D d;

d.A::x

hoặc d.B::x

Trường hợp 2:

Page 87: LẬP TRÌNH HƯỚNG ĐỐI TƯỢNG

LẬP TRÌNH HƯỚNG ĐỐI TƯỢNG

Trang 87

Trong lớp A có thành phần x

Xây dựng lớp B,C kế thừa từ A và lớp D kế thừa từ B và C

Như ta thấy trong D se có hai thể hiện của x, và tất nhiên khi truy xuất x trong

D se dẫn tới sự nhập nhằng. Để khăc phục ta lại xác định tường minh việc truy xuất:

d.B::x

d.C::x

Một cách khác để giải quyết vấn đề nhập nhằng này là việc khai báo lớp cơ sở

là lớp cơ sở ảo như sau:

class B: virtual public A {….}

class C: virtual public A{…..}

class D: public B, public C {….}

Bằng cách này trong D chỉ có một sự thể hiện của A

Cú pháp

class <tên lớp>: virtual [<kiểu kế thừa>] <tên lớp cơ sở>

{

//

A

x

B

x

C

x

D

x

Page 88: LẬP TRÌNH HƯỚNG ĐỐI TƯỢNG

LẬP TRÌNH HƯỚNG ĐỐI TƯỢNG

Trang 88

};

Ví dụ:

Giả sử có lớp động vật như sau:

class Animal{

virtual void eat();

};

//Lớp động vật có vú kế thừa từ lớp động vật

class Mammal{

public:

virtual Color getHairColor();

};

//Lớp động vật có cánh kế thừa lớp động vật

class WingedAnimal{

public:

virtual void Flap();

};

//Loài dơi là động vật có vú biết bay nên nó kế thừa cả 2 lớp động vật có vú và

động vật có cánh

class Bat: public Mammal, public: WingedAnimal {};

Vấn đề đặt ra là loài dơi ăn như thế nào tức là sử dụng phương thức eat() nào?

Nếu theo như khai báo ở trên thì việc gọi Bat.eat() là nhập nhằng không rõ ràng. Khi

đó có thể gọi Bat.WingedAnimal::eat(); hoặc Bat.Mammal::eat();

Sử dụng kế thừa ảo se giải quyết được vấn đề này. Chúng ta se khai báo lại lớp

như sau:

class Mammal : puclic virtual Animal{

public:

virtual Color GetHairColor();

};

class WingedAnimal : public virtual Animal{

public:

virtual void Flap();

};

Page 89: LẬP TRÌNH HƯỚNG ĐỐI TƯỢNG

LẬP TRÌNH HƯỚNG ĐỐI TƯỢNG

Trang 89

//Loài dơi vẫn là động vật có vú

class Bat: public Mammal, public WingedAnimal

{

};

Bây giờ phần Animal trong Bat::WingedAnimal cung tương tự như trong

Bat::Mammal, có thể nói rằng loài dơi chỉ là một loại vật và ta có thể gọi phương thức

Bat::eat() không nhập nhằng.

Page 90: LẬP TRÌNH HƯỚNG ĐỐI TƯỢNG

LẬP TRÌNH HƯỚNG ĐỐI TƯỢNG

Trang 90

Chương 5. TÍNH ĐA HÌNH

Chương trước đã giới thiệu cho bạn về sự thừa kế, đó là quá trình mà một lớp

tiếp nhận bản sao các phương thức public và private của một lớp khác. Trong 2

chương trước, bạn đã thấy ba trong bốn nguyên ly cơ bản của ly thuyết hướng đối

tượng. Bạn đã thấy sự trừu tượng, đóng gói và thừa kế. Chương này se cho bạn thấy

điều nguyên ly cơ bản thứ tư được sử dụng như thế nào, đó là đa hình. Đa hình theo

nghĩa đen là “nhiều dạng-many forms”. Nó đơn giản là khi bạn đã kế thừa một hàm,

bạn có thể ghi đè lên hàm đó và thay đổi nó.

Trong OOP, khi một lớp dẫn xuất kế thừa một lớp cơ sở, đối tượng của lớp dẫn

xuất có thể được tham chiếu tới cả lớp cơ sở và lớp dẫn xuất. Nếu trong lớp dẫn xuất

có hàm chồng lên hàm của lớp cơ sở se nảy sinh vấn đề là đối tượng được dẫn xuất

tham chiếu như là đối tượng của lớp cơ sở. Khi một đối tượng dẫn xuất được tham

chiếu như là đối tượng của lớp cơ sở thì các hàm dẫn xuất rơi vào trình trạng nhập

nhằng.

Trước khi đi vào phương thức ảo và tính đa hình ta nhăc lại hai khái niệm kết

gán sớm (tĩnh) và kết gán muộn (động). Khi một đối tượng nhận thông báo để thực

hiện một phương thức, hệ thống se thực hiện các công việc sau:

Kiểm tra cú pháp của thông báo

Găn thông báo đó với 1 định nghĩa hàm cụ thể ( kết gán)

Việc kết gán này có thể thực hiện ở hai thời điểm:

Lúc biên dịch chương trình Kết gán sớm

Lúc chạy chương trình Kết gán muộn

Kết gán sớm làm cho chương trình chạy nhanh hơn, còn kết gán muộn se làm

chương trình chạy chậm do lúc đó chương trình mới biết kiểu của đối tượng.

Để kết gán muộn C++ cho phép dùng phương thức ảo thực hiện

1. Phương thức ảo

Khai báo:

virtual <kiểu giá trị trả về> <tên phương thức>([danh sách tham số])

Ví dụ:

Page 91: LẬP TRÌNH HƯỚNG ĐỐI TƯỢNG

LẬP TRÌNH HƯỚNG ĐỐI TƯỢNG

Trang 91

class NGUOI

{

char ht[30];

char noisinh[40];

unsigned namsinh;

public:

virtual void nhap();

virtual void in();

};

class GV: public NGUOI

{

float hsl;

char donvi[20]

public:

void nhap();

void in();

void f();

};

void main()

{

NGUOI *p;

p = new NGUOI;

pnhap();

pin();

p=new GV;

pnhap();

pin();

pf();// sai vì kiểm tra không thấy p là con trỏ lớp

NGUOI

}

Cơ chế kết gán muộn:

Khi một lớp có phương thức ảo hoặc kế thừa từ lớp có phương thức ảo thì

chương trình dịch se phát sinh thêm một con trỏ gọi là con trỏ ảo(virtual pointer). Con

trỏ này trỏ tới bảng ảo (virtual table), trong bảng ảo ghi địa chỉ của các phương thức ảo

Page 92: LẬP TRÌNH HƯỚNG ĐỐI TƯỢNG

LẬP TRÌNH HƯỚNG ĐỐI TƯỢNG

Trang 92

Khi kết gán chương trình se kiểm tra xem bảng ảo của lớp GV có phương thức

nhập và in chưa nếu đã có thì se ghi đè lên NGUOI::nhap và NGUOI:in thành

GV::nhap và Gv::in

Ví dụ:

Xây dựng lớp GV, SV kế thừa từ lớp NGUOI, in danh sách GV, SV

NGUOI

{

- ht,namsinh

- nhap, in

- loai(){returrn 0};

}

SV:NGUOI

{

-lop,dtb

-nhap, in

-loai(){ return 1;}

}

GV:NGUOI

{

NGUOI

ht

noisinh

nguoi::in

nguoi::nhap

GV

VP

ht

noisinh

hsl

donvi

VP

GV::in

nguoi::nhap

GV::nhap

NGUOI::in

Page 93: LẬP TRÌNH HƯỚNG ĐỐI TƯỢNG

LẬP TRÌNH HƯỚNG ĐỐI TƯỢNG

Trang 93

-hsl, dv

-nhap,in

-loai(){return 2;}

}

class NGUOI

{

char ht[30];

unsigned namsinh;

public:

virtual void nhap();

virtual void in()

{

cout<<”\n”<<ht<<’\t’<<namsinh<<’\t’;

}

virtual void loai()

{

return 0;

}

};

void NGUOI::nhap()

{

cout<<”Ho va ten:”; gets(ht);

cout<<"\nNam sinh :" ; cin>>namsinh ;

}

class GV:public NGUOI

{

float hsl;

char donvi[20];

public:

void nhap();

void in();

int loai()

{

return 1;

}

Page 94: LẬP TRÌNH HƯỚNG ĐỐI TƯỢNG

LẬP TRÌNH HƯỚNG ĐỐI TƯỢNG

Trang 94

};

void GV::nhap()

{

cout<<”\nHe so luong:”; cin>>hsl;

cout<<”\nDon vi:”;gets(donvi);

}

void GV ::in::()

{

NGUOI ::in() ;

cout<<hsl<<’\t’<<donvi<<’\t’ ;

}

class SV : public NGUOI

{

char lop[10] ;

float dtb;

public :

void nhap() ;

void in() ;

int loai()

{

return 2;

}

};

void SV::nhap()

{

NGUOI::nhap();

cout<<”Lop:”; gets(lop);

cout<<”\nDiem TB:”; cin>>dtb;

}

void SV ::in()

{

NGUOI ::in() ;

cout<<lop<<’\t’<<dtb ;

}

void main()

{

Page 95: LẬP TRÌNH HƯỚNG ĐỐI TƯỢNG

LẬP TRÌNH HƯỚNG ĐỐI TƯỢNG

Trang 95

NGUOI *ds[20] ;

int n,c,i;

do

{

cout<<”\n1. Nhap danh sach”;

cout<<”\n2. In danh sach sinh vien”;

cout<<”\n3. In danh sach giao vien”;

cout<<”\n0. Ket thuc!”;

c=getchar();

switch(c)

{

case ‘1’: cout<<”So doi tuong trong danh sach?”;

cin>>n;

for(i=0;i<n;i++)

{

cout<<”\n1. Nhap sinh vien”;

cout<<”\n2. Nhap giao vien”;

int k ;

k=getchar() ;

if(k==’1’) ds[i]=new SV ;

else ds[i]=new GV;

ds[i]nhap();

}

break;

case ‘2’: cout<<”\n Danh sach sinh vien:”;

for(i=0;i<n;i++)

if(ds[i]loai()==1) ds[i]in();

getchar();

break;

case ‘3’: cout<<”\n Danh sach giao vien:”;

for(i=0;i<n;i++)

if(ds[i]loai()==2) ds[i]in();

getchar();

break;

}

}

Page 96: LẬP TRÌNH HƯỚNG ĐỐI TƯỢNG

LẬP TRÌNH HƯỚNG ĐỐI TƯỢNG

Trang 96

while(c!=’0’);

}

Nhận xét: Ở class NGUOI nếu như hai phương thức nhap() và in() không khai

báo virtual thì khi gọi phương thức nhập và in của GV và SV đều se gọi phương thức

nhập và in của lớp cơ sở là lớp NGUOI. Như vậy là không theo y muốn của chúng ta,

để gọi đúng phương thức GV::nhap() hay SV::nhap() thì ở lớp NGUOI ta phải khai

báo virtual ở hai phương thức nhập và in.

Chú ý:

Cơ chế kết gán phương thức ảo chỉ có thể thực hiện được qua phép gán con trỏ

hoặc tham chiếu.

Ví dụ:

NGUOI a,*p;

GV b;

a=b;

a.nhap(); //kết gán sớm

p=&b;

pnhap(); //kết gán muộn

Ví dụ 2:

NGUOI &q=b;

qnhap(); //kết gán muộn

void f(NGUOI x)

{

x.nhap(); //kết gán sớm

}

void f(NGUOI &x)

{

x.nhap(); //kết gán muộn

}

Page 97: LẬP TRÌNH HƯỚNG ĐỐI TƯỢNG

LẬP TRÌNH HƯỚNG ĐỐI TƯỢNG

Trang 97

2. Phương thức ảo thuần túy và lớp trừu tượng

Trong quá trình kế thừa se phát sinh ra các lớp con, các lớp con này lại có các

lớp con riêng. Chúng ta phải đoán trước xem các lớp con này có những thành phần nào

để xây dựng lớp cơ sở trên cùng. Tuy nhiên nếu xây dựng toàn bộ các thành phần cho

các đối tượng thì se dẫn tới tình trạng lãng phí bộ nhớ. Để giải quyết vấn đề này C++

cung cấp cho chúng ta khái niệm phương thức ảo thuần túy.

Đối với lớp cơ sở cần cung cấp một phương thức thống nhất làm giao diện cho

các lớp con của nó mà các phương thức này không được định nghĩa trong lớp hoặc

không làm gì thì phương thức đó được gọi là phương thức ảo thuần túy.

Cú pháp:

virtual <kiểu giá trị trả về> <tên phương thức>([danh sách tham số])=0;

Một số đặc điểm của phương thức ảo thuần túy:

- Phương thức ảo thuần túy không băt buộc định nghĩa trong lớp cơ sở

- Phương thức ảo thuần túy không thể phát sinh các đối tượng tức là không thể khai

báo đối tượng thuộc lớp có phương thức ảo thuần túy

- Lớp có phương thức ảo thuần túy chỉ làm lớp cơ sở cho các lớp khác và được gọi là

lớp cơ sở trừu tượng.

- Trong lớp dẫn xuất kế thừa lớp cơ sở trừu tượng nếu không định nghĩa lại phương

thức ảo thuần túy thì lớp dẫn xuất đó cung trở thành lớp cơ sở trừu tượng.

Ví dụ:

Xây dựng lớp hình, hình vuông, hình tam giác kế thừa từ lớp hình với các thành

phần cần thiết để thực hiện yêu cầu:

- Nhập danh sách các hình

- Săp xếp các hình theo thứ tự giảm của diện tích

- In các hình

class hinh

{

public:

virtual void nhap()=0;

virtual void in()=0;

virtual float dt()=0;

Page 98: LẬP TRÌNH HƯỚNG ĐỐI TƯỢNG

LẬP TRÌNH HƯỚNG ĐỐI TƯỢNG

Trang 98

int operator>(hinh x)

{

return dt()>x.dt();

}

};

class HV:public hinh

{

float a;

public:

void nhap();

void in()

{

cout<<”Kich thuoc:”<<a;

cout<<”Dien tich:”<<đối tượng();

}

float dt()

{

return a*a;

}

};

class HTG: public hinh

{

float a,b,c

public:

void nhap();

void in():

int ktra()

{

if((a+b>c)&&(b+c>a)&&(c+a>b)&&(a>0)&&(b>0)&&(c>0

))

return 1;

else return 0;

}

float dt()

{

float s,p;

Page 99: LẬP TRÌNH HƯỚNG ĐỐI TƯỢNG

LẬP TRÌNH HƯỚNG ĐỐI TƯỢNG

Trang 99

p=(a+b+c)/2

s=sqrt(p*(p-a)*(p-b)*(p-c));

return s;

}

};

void HV::nhap()

{

cout<<”Nhap do dai canh :”;cin>>a;

}

void HTG::in()

{

cout<<”Cac canh tam giac:”<<a<<”,”<<b<<”,”<<c;

cout<<”Dien tich:”<<dt;

}

void HTG::nhap()

{

do

{

cout<<”Nhap cac canh:”;

cin>>a>>b>>c;

}while ktra()=0;

}

void main()

{

hinh *d[10];

int n,i,c;

cout<<”Nhap so hinh:”;cin>>n;

for(i=0;i<n;i++)

{

cout<<”Hinh thu “<<i<<”(1. HV, 2. TG)”;

cin>>c;

if(c==1) d[i]=new HV;

else d[i]= new HTG;

d[i]nhap();

}

int k,j

Page 100: LẬP TRÌNH HƯỚNG ĐỐI TƯỢNG

LẬP TRÌNH HƯỚNG ĐỐI TƯỢNG

Trang 100

for(i=0;i<n-1;i++)

for(k=i,j=i+1;j<n;j++)

if d[j]>d[k] k=j;

if(k!=i)

{

hinh *tmp;

tmp=d[i];

d[i]=d[k];

d[k]=tmp;

}

cout<<”Danh sach cac hinh sau khi sap:”;

for(i=0;i<n;i++)

{

d[i]in();

cout<<endl;

}

}

Page 101: LẬP TRÌNH HƯỚNG ĐỐI TƯỢNG

LẬP TRÌNH HƯỚNG ĐỐI TƯỢNG

Trang 101

Chương 6. KHUÔN HÌNH MẪU

1. Template

a. Khuôn hình hàm

Trước khi đi vào khái niệm khuôn hình hàm ta xét một số ví dụ sau:

Xây dựng hàm để tìm giá trị nhỏ nhất của 2, 3, hay 4 số thực.

Bài toán này se được giải quyết đơn giản bằng cách nạp chồng hàm

float min(float, float);

float min(float, float, float);

float min(float,float,float,float);

Ta lại xét trường hợp sau:

Xây dựng hàm để tìm giá trị lớn nhất của 2 số bất kỳ. Nếu nạp chồng hàm ta se

có hai hàm như sau:

int max(int a, int b)

{

if(a>b) return a;

else return b;

}

float max(float a, float b)

{

if(a>b) return a;

else return b;

}

Ta thấy rằng hàm max thứ nhất se trả về kiểu int cùng kiểu với kiểu của tham

số, hàm max thứ hai cung tương tự. Vậy tại sao chúng ta không xây dựng một mẫu

hàm sao cho đối số truyền vào là kiểu gì thì hàm trả về kiểu đó. Trong thực tế ta gặp

rất nhiều trường hợp như vậy.

Khái niệm

Page 102: LẬP TRÌNH HƯỚNG ĐỐI TƯỢNG

LẬP TRÌNH HƯỚNG ĐỐI TƯỢNG

Trang 102

Khuôn hình hàm là một mẫu của hàm có các tham số là kiểu của các đối số; với

môi giá trị hợp lệ của tham số đó (tham số kiểu) se phát sinh cho chúng ta một hàm cụ

thể gọi là hàm thể hiện.

Một khuôn hình hàm là đặc trưng cho một lớp các hàm có cùng tham số và

cùng thuật giải

Khai báo

template <class T1, class T2....> <kiểu giá trị trả về> <tên khuôn hình

hàm>([ds tham số])

{

//thân khuôn hình hàm

}

ví dụ:

template <class T> T max(T a, T b)

{

if(a>b) return a ;

else return b;

}

Ví dụ 2:

Xây dựng khuôn hình tổng của 3 số bất kỳ

template <class T1, class T2, class T3> T1 tong(T1 x,

T2 y, T3 z)

{

return x+y+z ;

}

Gọi hàm từ khuôn hình hàm

Cú pháp

<tên hàm>(đối số)

Tên hàm trùng tên khuôn hình hàm

Ví dụ:

Với khuôn hình hàm max

int a,b ;

Page 103: LẬP TRÌNH HƯỚNG ĐỐI TƯỢNG

LẬP TRÌNH HƯỚNG ĐỐI TƯỢNG

Trang 103

float x,y ;

max(a,b) ;

Khi gọi hàm max(a,b) như trên thì chương trình dịch se xác định :

- Kiểu của a,b là int nên kiểu của T se là int

- Phát sinh một hàm cụ thể từ khuôn hình hàm max với T là int

Hàm max lúc này se như sau

int max(int a, int b)

{

if(a>b) return a ;

else return b;

}

Tương tự với max(x,y)

Nhận xét: khi gặp một lời gọi hàm, chương trình dịch căn cứ vào kiểu của đối

số để xác định giá trị của tham số kiểu và se phát sinh một hàm thể hiện tương ứng với

giá trị của tham số kiểu đó. Sau đó biên dịch và chạy hàm

Chú ý:

- Mọi tên tham số kiểu phải xuất hiện trong khai báo tham số ít nhất một lần

- Tham số kiểu được dùng trong khuôn hình như tên 1 kiểu thông thường

Ví dụ: max(x, a) se báo lôi

- Giá trị của tham số kiểu phải so sánh chính xác (không ép kiểu)

Cụ thể hóa một khuôn hình hàm

Ta xét trường hợp sau:

Có một khuôn hình hàm f có thể sử dụng với hầu hết các kiểu dữ liệu trừ :

- có kiểu T không đáp ứng các thao tác bên trong của f

- có kiểu U có các phép tóan đáp ứng được các phép tóan bên trong của f nhưng lại

không phù hợp với yêu cầu đặt ra

Để giải quyết, ta làm như sau :

Page 104: LẬP TRÌNH HƯỚNG ĐỐI TƯỢNG

LẬP TRÌNH HƯỚNG ĐỐI TƯỢNG

Trang 104

Th1 : bổ sung vào T các thao tác cần thiết ( nếu T là kiểu của người lập trình tạo

ra lớp)

Th2 : định nghĩa một hàm cụ thể đối với kiểu U ( cụ thể hoad 1 khuôn hình

hàm)

Ví dụ :

char *max(char *s1, char *s2)

{

if(strcmp(s1,s2)>0)

return s1;

else return s2;

}

float a,b;

char *s1, *s2;

max(a,b) sử dụng khuôn hình hàm rồi phát sinh hàm tương ứng với T là float

max(s1,s2) sử dụng hàm cụ thể

Qui tăc chọn hàm

Khi gặp một lời gọi hàm, chương trình dịch chọn hàm theo các bước sau:

B1: Chọn trong các hàm cụ thể một hàm phù hợp (không ép kiểu)

- Nếu có đúng 1 hàm phù hợp thì thể hiện hàm đó

- Nếu có nhiều hơn 1 hàm phù hợp thì báo lôi nhập nhằng

- Nếu không có hàm phù hợp thì chuyển sang bước 2

B2: Chọn trong khuôn hình hàm

- Nếu có đúng 1 khuôn hình hàm phù hợp thì phát sinh hàm thể hiện và thể hiện hàm

đó

- Nếu có hơn 1 khuôn hình phù hợp thì báo lôi

- Nếu không có khuôn hình phù hợp se sang bước 3

B3: Chọn lại trong các hàm cụ thể 1 hàm phù hợp (có ép kiểu)

- Nếu có đúng 1 hàm phù hợp thì thực hiện

Page 105: LẬP TRÌNH HƯỚNG ĐỐI TƯỢNG

LẬP TRÌNH HƯỚNG ĐỐI TƯỢNG

Trang 105

- Ngược lại báo lôi

Ví dụ

Viết khuôn hình hàm min để tìm giá trị nhỏ nhất trong 2 số, 3 số, 1 mảng có n

phần tử

template <class T> T min(T a, T b)

{

if(a>b) return b ;

else return a;

}

template <class T> T min(T a, T b, T c)

{

return min(min(a,b),c) ;

}

template<class T> T min (T *d, int n)

{

T x ;

x=d[0] ;

for(i=0 ;i<n ;i++)

if(x>d[i]) x=d[i] ;

return x ;

}

b. Khuôn hình lớp

Khái niệm

Khuôn hình lớp là một mẫu của lớp có các tham số là các kiểu dữ liệu (tham số

kiểu) và với môi giá trị của tham số kiểu se phát sinh ra một thể hiện là một lớp cụ thể

(lớp khuôn hình)

Khai báo khuôn hình lớp

Cú pháp :

template <class T1, class T2...> class <tên khuôn hình lớp>

{

<khai báo các thành phần>

};

Page 106: LẬP TRÌNH HƯỚNG ĐỐI TƯỢNG

LẬP TRÌNH HƯỚNG ĐỐI TƯỢNG

Trang 106

Định nghĩa các phương thức

- Có thể khai báo trong lớp

- Khai báo ngoài lớp

Ví dụ:

Xây dựng khuôn hình lớp vectơ có kiểu chưa xác định

template <class T> class VT{

int spt;

T *d;

public:

void nhap() ;

void in() ;

VT operator + (VT&) ;

T operator *(VT &);

};

Khai báo phương thức:

template <class T> void VT<T>::nhap(){

cout<<"So phan tu :" ; cin>>spt ;

d= new T[spt] ;

for(int i=0 ; i<spt ;i++)

{

cout<<”Phan tu thu “<<i<<”:”;

cin>>d[i];

}

}

Cú pháp:

template <class T1, class T2 …> <kiểu trả về> <tên lớp><T1, T2>::<tên

tphần>([tham số])

{

<thân phương thức>

}

Phát sinh các lớp và các đối tượng

Page 107: LẬP TRÌNH HƯỚNG ĐỐI TƯỢNG

LẬP TRÌNH HƯỚNG ĐỐI TƯỢNG

Trang 107

Khi phát sinh lớp từ khuôn hình lớp ta phải truyền các giá trị cụ thể của tham số

kiểu

Ví dụ với khuôn hình lớp VT

VT <int>: lớp vec tơ với các phần tử kiểu int: T= int

VT <float>: lớp vec tơ với các phần tử kiểu float: T= float

VT <PS>: lớp vec tơ với các phần tử kiểu PS: T= PS

VT <SP>: lớp vec tơ với các phần tử kiểu SP: T=SP

Khai báo đối tượng

VT<int> a,b;

VT<float> x;

Cú pháp

<tên khuôn hình lớp><kiểu 1, kiểu 2…>

Các thành phần đặc biệt

- Phương thức bạn:

Giả sử có khuôn hình lớp A, nếu trong A có hàm bạn f thì f là bạn của mọi lớp

phát sinh từ A

- Thành phần tĩnh:

Giả sử có khuôn hình lớp A, nếu trong A có thành phần tĩnh k thì môi lớp phát

sinh từ A đều có một thành phần tĩnh k

Ví dụ:

xây dựng khuôn hình lớp MT2 có các thành phần :

- dữ liệu: số dòng, số cột, mảng các phần tử có kiểu xác định

- Phương thức: hàm tạo, hàm hủy, phép gán, +, *, nhập, in

- Hàm bạn:

Hàm bạn của một khuôn hình lớp là bạn của mọi lớp thể hiện

-Lớp bạn:

Lớp bạn của một khuôn hình lớp là lớp bạn của mọi lớp thể hiện

Page 108: LẬP TRÌNH HƯỚNG ĐỐI TƯỢNG

LẬP TRÌNH HƯỚNG ĐỐI TƯỢNG

Trang 108

-Khuôn hình hàm: là một bạn của khuôn hình lớp thì mọi hàm thể hiện của

khuôn hình hàm là bạn của mọi lớp thể hiện của khuôn hình lớp

Tham số giá trị của khuôn hình lớp

Xét ví dụ:

template <class T> class VT

{

int spt;

T v[10];

VT(int n);

};

Lớp thể hiện VT<int>, VT<float>

x(10), y(3)

Ta thấy rằng cần có một tham số là số phần tử của VT, tham số đó được gọi là

giá trị của khuôn hình lớp.

Tham số giá trị là tham số của khuôn hình lớp, ứng với môi giá trị của tham số

cho ta một lớp.

Ví dụ :

template <class T, int n> class VT

{

T v[n];

public:

void nhap();

void in();

};

Để phát sinh một lớp từ khuôn hình lớp cần:

- Cấp giá trị của tham số kiểu (tên một kiểu)

- Cấp giá trị của tham số giá trị (giá trị cụ thể)

Ví dụ :

Page 109: LẬP TRÌNH HƯỚNG ĐỐI TƯỢNG

LẬP TRÌNH HƯỚNG ĐỐI TƯỢNG

Trang 109

Xây dựng khuôn hình lớp mảng 2 chiều với số dòng, số cột và kiểu các phần tử

là các tham số

template <class T, int sd, int sc> class MT2

{

T d[sd][sc];

public:

void nhap() ;

void in() ;

MT2 operator+(MT2) ;

} ;

template <class T, int sd, int sc>

void MT2<T, sd,sc>::nhap()

{

int I,j;

for(i=0;i<sd;i++)

for(j=;j<sc;j++)

{

cout<<” Pha tu hang “<<i<<” cot “<<j;

cin>>d[i][j];

}

}

template <class T, int sd, int sc>

void MT2<T, sd,sc>::in()

{

for(int i=0;i<sd;i++)

for(int j=0;j<sc;j++)

cout<<d[i][j];

}

template <class T, int sd, int sc) MT2<T,sd,sc>

MT2<t,sd,sc>::operator+(MT2 a)

{

MT2<T,sd,sc> kq;

for(int i=0;i<sd;i++)

for(int j=0;j<sc;j++)

kq.d[i][j]=d[i][j]+a.d[i][j];

return kq;

Page 110: LẬP TRÌNH HƯỚNG ĐỐI TƯỢNG

LẬP TRÌNH HƯỚNG ĐỐI TƯỢNG

Trang 110

}

void main()

{

MT2<int,2,2> a,b,c;

cout<<”\nNhap a:”;a.nhap();

cout<<”\nNhap b:”;b.nhap();

c=a+b;

cout<<”\n a+b=:”;c.in();

}

Cụ thể hóa một lớp thể hiện

1. Xử ly lôi phát sinh

Phụ lục: Các dòng nhập/xuất

Phụ lục: Thiết kế hướng đối tượng

Page 111: LẬP TRÌNH HƯỚNG ĐỐI TƯỢNG

LẬP TRÌNH HƯỚNG ĐỐI TƯỢNG

Trang 111

MỞ ĐẦU .............................................................................................................. 5

Chương 1. GIỚI THIỆU VỀ LẬP TRÌNH HƯỚNG ĐỐI TƯỢNG ................. 6

1. Tổng quan về các kỹ thuật lập trình ............................................................. 6

1.1. Lập trình tuyến tính ............................................................................... 6

1.2. Lập trình cấu trúc .................................................................................. 6

1.3. Lập trình mô đun hoá ............................................................................ 7

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

1.5. Lập trình hướng đối tượng .................................................................... 9

2. Một số khái niệm cơ bản ............................................................................ 11

2.1. Hệ thống hướng đối tượng .................................................................. 11

2.2. Đối tượng (Objects) ............................................................................. 12

2.3. Thuộc tính (Attribute) và Phương thức (method) ............................... 12

2.4. Lớp (Class) và lớp con (SubClass) ...................................................... 12

2.5. Lớp trừu tượng .................................................................................... 13

2.6. Truyền thông điệp ............................................................................... 13

2.7. Sự trừu tượng hoá (abstraction) ........................................................... 14

2.8. Sự đóng gói (encapsulation) ................................................................ 14

2.9. Tính kế thừa ......................................................................................... 14

1.10. Tính đa hình ....................................................................................... 15

3. Các bước cần thiết để thiết kế chương trình theo hướng đối tượng ........... 16

4. Các ưu điểm của lập trình hướng đối tượng ............................................... 16

5. Một số ngôn ngữ lập trình hướng đối tượng .............................................. 16

6. Một số ứng dụng của lập trình hướng đối tượng ........................................ 18

Chương 2. GIỚI THIỆU VỀ C++ .................................................................... 19

1. Lịch sử của C++ ......................................................................................... 19

2. Các mở rộng của C++................................................................................. 19

Page 112: LẬP TRÌNH HƯỚNG ĐỐI TƯỢNG

LẬP TRÌNH HƯỚNG ĐỐI TƯỢNG

Trang 112

2.1. Lời chú thích ........................................................................................ 19

2.2. Từ khoá mới ........................................................................................ 20

2.3. Kiểu dữ liệu char và int ....................................................................... 20

2.4. Khai báo biến ....................................................................................... 20

2.5. Chuyển đổi và ép kiểu ......................................................................... 21

2.6. Vào ra trong C++ ................................................................................. 22

2.7. Cấp phát và giải phóng bộ nhớ ............................................................ 26

2.8. Biến tham chiếu ................................................................................... 28

2.9. Hằng tham chiếu .................................................................................. 29

2.10. Truyền tham số cho hàm theo tham chiếu ......................................... 30

2.11. Hàm với tham số có giá trị mặc định ................................................ 34

2.12. Các hàm nội tuyến ............................................................................. 35

2.13. Hàm tải bội ........................................................................................ 36

Chương 3. LỚP VÀ ĐỐI TƯỢNG .................................................................. 39

1. Xây dựng lớp và đối tượng ......................................................................... 39

1.1. Khai báo lớp ........................................................................................ 39

1.2. Khai báo đối tượng .............................................................................. 42

1.3. Truy xuất các thành phần của đối tượng ............................................. 43

2. Các phương thức ........................................................................................ 46

2.1. Hàm khởi tạo - Constructor ................................................................. 46

2.2. Hàm hủy – Destructor ......................................................................... 49

3. Đa năng hóa tóan tử .................................................................................... 51

4. Mảng và con trỏ của lớp ............................................................................. 57

5. Các hàm bạn và lớp bạn ............................................................................. 59

5.1. Hàm bạn (friend function) ................................................................... 59

5.2. Lớp bạn ................................................................................................ 61

6. Thành phần tĩnh .......................................................................................... 63

Page 113: LẬP TRÌNH HƯỚNG ĐỐI TƯỢNG

LẬP TRÌNH HƯỚNG ĐỐI TƯỢNG

Trang 113

6.1. Các thành phần dữ liệu tĩnh ................................................................. 63

6.2. Các hàm thành viên tĩnh ...................................................................... 64

7. Thành phần hằng ........................................................................................ 67

7.1. Dữ liệu hằng ........................................................................................ 67

7.2. hàm thành phần (phương thức hằng) ................................................... 69

8. Thành phần là đối tượng ............................................................................. 69

Chương 4. TÍNH KẾ THỪA ........................................................................... 72

1. Khái niệm ................................................................................................... 72

2. Khai báo lớp dẫn xuất ................................................................................. 73

3. Các kiểu kế thừa ......................................................................................... 75

4. Định nghĩa lại quyền truy xuất ................................................................... 78

5. Hàm khởi tạo và hàm hủy của lớp cơ sở .................................................... 81

6. Đa kế thừa ................................................................................................... 84

7. Lớp cơ sở ảo và nhập nhằng trong đa kế thừa ............................................ 86

Chương 5. TÍNH ĐA HÌNH ............................................................................ 90

1. Phương thức ảo ........................................................................................... 90

2. Phương thức ảo thuần túy và lớp trừu tượng .............................................. 97

Chương 6. KHUÔN HÌNH MẪU .................................................................. 101

1. Template ................................................................................................... 101

a. Khuôn hình hàm ................................................................................... 101

b. Khuôn hình lớp ..................................................................................... 105