Bao gồm các nội dung sau: - Giới thiệu về cách tiếp cận của lập trình truyền thống - Giới thiệu về cách tiếp cận của lập trình hướng đối tượng - Các khái niệm cơ bản của lập trình hướng
Trang 1HỌC VIỆN CÔNG NGHỆ BƯU CHÍNH VIỄN THÔNG KHOA THIẾT KẾ VÀ SÁNG TẠO ĐA PHƯƠNG TIỆN
LƯU HÀNH NỘI BỘ
Hà Nội, 11/2014
PTIT
Trang 2LỜI NÓI ĐẦU
Hiện nay, các ngôn ngữ lập trình hướng đối tượng được sử dụng phổ biến để viết các ứng dụng như C++, Java, C# Trong đó C++ là ngôn ngữ được mở rộng từ ngôn ngữ C, do đó C++ kế thừa được các điểm mạnh của C như khả năng tương thích với các thiết bị phần cứng Bài giảng này nhằm giới thiệu cho sinh viên hiểu biết về lập trình hướng đối tượng và ngôn ngữ lập trình hướng đối tượng C++
Nội dung bài giảng bao gồm 6 chương:
- Chương 1: Tổng quan về lập trình hướng đối tượng Giới thiệu về các phương
pháp lập trình, các khái niệm cơ bản trong lập trình hướng đối tượng và một số ngôn ngữ lập trình hướng đối tượng phổ biến
- Chương 2: Giới thiệu ngôn ngữ C++ Giới thiệu lập trình cơ bản với C++
- Chương 3: Lớp và đối tượng Trình bày về cách khai báo, sử dụng lớp, các hàm
thành phần của lớp
- Chương 4: Kế thừa và đa hình Trình bày về đơn kế thừa, đa kế thừa và đa hình
trong C++
- Chương 5: Khuôn hình hàm và khuôn hình lớp Trình bày về cách tạo và sử dụng
khuôn hình hàm, khuôn hình lớp trong C++
- Chương 6: Thư viện chuẩn STL của C++ Trình bày một số lớp thư viện chuẩn
trong C++
Mặc dù tác giả đã có nhiều cố gắng trong quá trình biên soạn bài giảng này, song không thể tránh khỏi những thiếu sót Rất mong nhận được sự đóng góp ý kiến của sinh viên và các bạn đồng nghiệp
PTIT
Trang 3MỤC LỤC
LỜI NÓI ĐẦU 2
MỤC LỤC 3
CHƯƠNG 1: TỔNG QUAN VỀ LẬP TRÌNH HƯỚNG ĐỐI TƯỢNG 7
1.1 Tổng quan về tiếp cận hướng đối tượng 7
1.1.1 Phương pháp tiếp cận của lập trình truyền thống 7
1.1.2 Phương pháp tiếp cận hướng đối tượng 8
1.2 Các khái niệm cơ bản của lập trình hướng đối tượng 11
1.2.1 Đối tượng 11
1.2.2 Lớp đối tượng 11
1.2.3 Phương thức 12
1.2.4 Thuộc tính 12
1.2.5 Thông điệp và truyền thông điệp 12
1.2.6 Trừu tượng hoá đối tượng 12
1.2.7 Tính đóng gói 13
1.2.8 Tính kế thừa 13
1.2.9 Tính đa hình 14
1.3 Các ngôn ngữ lập trình hướng đối tượng 14
1.3.1 C++ 14
1.3.2 Net 14
1.3.3 Java 15
1.4 Case study 15
TỔNG KẾT CHƯƠNG 1 15
CÂU HỎI VÀ BÀI TẬP CHƯƠNG 1 16
CHƯƠNG 2: GIỚI THIỆU NGÔN NGỮ C++ 17
2.1 Cấu trúc chương trình trên C++ 17
2.2 Từ khóa 18
2.3 Kiểu dữ liệu 18
2.4 Khai báo biến, hằng 19
2.4.1 Khai báo biến 19
2.4.2 Khai báo hằng 19
2.5 Các lệnh vào ra và các lệnh điều khiển 20
PTIT
Trang 42.5.1 Các lệnh vào ra 20
2.5.2 Các lệnh điều khiển 23
2.6 Kiểu dữ liệu cấu trúc 30
2.6.1 Khái niệm về kiểu cấu trúc 30
2.6.2 Cách sử dụng kiểu cấu trúc 33
2.6.3 Con trỏ cấu trúc và mảng cấu trúc 36
2.7 Cấp phát và giải phóng bộ nhớ 42
2.7.1 Cấp phát bộ nhớ với toán tử new 42
2.7.2 Giải phóng bộ nhớ với toán tử delete 44
2.8 Làm việc với tệp tin 44
2.8.1 Mở tệp tin 44
2.8.2 Thao tác trên tệp tin 45
2.8.3 Truy nhập tệp với con trỏ 51
2.9 Case study: Xây dựng chương trình đơn giản với C++ 54
TỔNG KẾT CHƯƠNG 2 55
CÂU HỎI VÀ BÀI TẬP CHƯƠNG 2 56
CHƯƠNG 3: LỚP VÀ ĐỐI TƯỢNG 57
3.1 Lớp đối tượng 57
3.1.1 Khai báo lớp 57
3.1.2 Tạo và sử dụng lớp đối tượng 59
3.2 Con trỏ đối tượng 62
3.3 Mảng đối tượng 65
3.4 Hàm bạn, lớp bạn 68
3.4.1 Hàm bạn 68
3.4.2 Lớp bạn 73
3.5 Hàm thiết lập, huỷ bỏ 74
3.5.1 Hàm thiết lập 74
3.5.2 Hàm huỷ bỏ 76
3.6 Toán tử trên lớp 78
3.6.1 Hàm toán tử là hàm thành phần 78
3.6.2 Hàm toán tử là hàm bạn 80
3.6.3 Khả năng và giới hạn của các định nghĩa chồng toán tử 83
3.7 Case study: Xây dựng các ứng dụng đơn giản sử dụng lớp và đối tượng 83
PTIT
Trang 5TỔNG KẾT CHƯƠNG 3 86
CÂU HỎI VÀ BÀI TẬP CHƯƠNG 3 87
CHƯƠNG 4: KẾ THỪA VÀ ĐA HÌNH 88
4.1 Kế thừa 89
4.1.1 Lớp cơ sở và lớp dẫn xuất 89
4.1.2 Các kiểu kế thừa 89
4.1.3 Đơn kế thừa 90
4.1.4 Đa kế thừa 101
4.2 Đa hình 107
4.2.1 Phương thức ảo 107
4.2.2 Lớp trừu tượng, hàm ảo thuần túy 110
4.3 Case study: Xây dựng các ứng dụng đơn giản sử dụng tính kế thừa và đa hình 110
TỔNG KẾT CHƯƠNG 4 113
CÂU HỎI VÀ BÀI TẬP CHƯƠNG 4 114
CHƯƠNG 5: KHUÔN HÌNH HÀM VÀ KHUÔN HÌNH LỚP 116
5.1 Khuôn hình hàm 116
5.1.1 Khái niệm khuôn hình hàm 116
5.1.2 Tạo khuôn hình hàm 116
5.1.3 Sử dụng khuôn hình hàm 117
5.1.4 Tham số trong khuôn hình hàm 119
5.1.5 Chồng khuôn hình hàm 121
5.2 Khuôn hình lớp 123
5.2.1 Khái niệm khuôn hình lớp 123
5.2.2 Tạo khuôn hình lớp 123
5.2.3 Sử dụng khuôn hình lớp 125
5.2.4 Tham số trong khuôn hình lớp 126
TỔNG KẾT CHƯƠNG 5 128
CÂU HỎI VÀ BÀI TẬP CHƯƠNG 5 128
CHƯƠNG 6: THƯ VIỆN CHUẨN STL CỦA C++ 129
6.1 Lớp chứa 129
6.1.1 Lớp chứa tuần tự 130
6.1.2 Lớp chứa liên kết 138
6.2 Lớp Iterator 147
PTIT
Trang 66.2.1 Các loại iterator 147
6.2.2 Các hàm iterator bổ trợ 150
6.2.3 Các iterator chuyển tiếp 153
6.3 Lớp các đối tượng hàm 161
6.3.1 Các đối tượng hàm là tiêu chuẩn sắp xếp trong lớp chứa 161
6.3.2 Các đối tượng hàm sử dụng trong các hàm giải thuật 162
6.3.3 Đối tượng hàm tự định nghĩa 163
6.4 Lớp giải thuật 165
6.4.1 Một số hàm bổ trợ 165
6.4.2 Hàm giải thuật for_each() 166
6.4.3 Các hàm giải thuật không thay đổi phần tử 167
6.4.4 Các giải thuật thay đổi phần tử 175
6.4.5 Các giải thuật xóa phần tử 181
6.4.6 Các giải thuật biến đổi 183
6.4.7 Các giải thuật sắp xếp 187
TỔNG KẾT CHƯƠNG 6 189
TÀI LIỆU THAM KHẢO 190
PTIT
Trang 7CHƯƠNG 1: TỔNG QUAN VỀ LẬP TRÌNH HƯỚNG ĐỐI TƯỢNG
Nội dung chương này sẽ giới thiệu tổng quan về lập trình hướng đối tượng Bao gồm các nội dung sau:
- Giới thiệu về cách tiếp cận của lập trình truyền thống
- Giới thiệu về cách tiếp cận của lập trình hướng đối tượng
- Các khái niệm cơ bản của lập trình hướng đối tượng
- Giới thiệu về các ngôn ngữ lập trình hướng đối tượng phổ biến hiện nay
1.1 Tổng quan về tiếp cận hướng đối tượng
1.1.1 Phương pháp tiếp cận của lập trình truyền thống
- Đơn giản: chương trình được tiến hành đơn giản theo lối tuần tự, không phức tạp
- Đơn luồng: chỉ có một luồng công việc duy nhất, các công viêc được thực hiện tuần tự trong luồng đó
Tính chất
- Ưu điểm: đơn giản, dễ hiểu
- Nhược điểm: Không thể dùng lập trình tuyến tính để xây dựng các ứng dụng đơn giản Ngày nay, lập trình tuyến tính chỉ tồn tại trong các module nhỏ của các phương pháp lập trình khác
1.1.1.2 Lập trình cấu trúc
Với lập trình hướng cấu trúc, chương trình chính được chia nhỏ thành các chương trình con và mỗi chương trình con thực hiện một công việc xác định Chương trình chính sẽ gọi đến chương trình con theo một giải thuật, hoặc một cấu trúc được xác định trong chương trình chính
Một số ngôn ngữ lập trình hướng cấu trúc phổ biến là: Pascal, C, C++ Riêng C++ ngoài việc
có đặc trưng của lập trình cấu trúc do kế thừa từ C, còn có đặc trưng của lập trình hướng đối tượng Vì thế, C++ còn được gọi là ngôn ngữ nửa hướng cấu trúc, nửa hướng đối tượng
Đặc trưng
Đặc trưng cơ bản nhất của lập trình cấu trúc thể hiện ở mối quan hệ:
Chương trình = Cấu trúc dữ liệu + giải thuật
PTIT
Trang 8Trong đó:
- Cấu trúc dữ liệu: là cách tổ chức dữ liệu, cách mô tả bài toán dưới dạng ngôn ngữ lập trình
- Giải thuật: là một quy trình để thực hiện một công việc xác định
Trong chương trình, giải thuật có quan hệ phụ thuộc vào cấu trúc dữ liệu:
- Một cấu trúc dữ liệu chỉ phù hợp với một số hạn chế các giải thuật
- Nếu thay đổi cấu trúc dữ liệu thì phải thay đổi giải thuật cho phù hợp
- Một giải thuật thường phải đi kèm một cấu trúc dữ liệu nhất định
- Các ngôn ngữ lập trình cấu trúc cung cấp một số cấu trúc lệnh điều khiển chương trình
- Ưu điểm: Chương trình sáng sủa, dễ hiểu, dễ theo dõi, tư duy giải thuật rõ ràng
- Nhược điểm: Không hỗ trợ sử dụng lại mã nguồn Giải thuật phụ thuộc chặt chẽ vào cấu trúc dữ liệu, do đó khi thay đổi cấu trúc dữ liệu phải thay đổi giải thuật, viết lại chương trình Lập trình cấu trúc không phù hợp với các phần mềm lớn
1.1.2 Phương pháp tiếp cận hướng đối tượng
- Quan hệ giữa các đối tượng là quan hệ ngang hang hoặc quan hệ kế thừa: nếu lớp B kế thừa từ lớp A thì được gọi là lớp cơ sở và B gọi là lớp dẫn xuất
Một số ngôn ngữ lập trình hướng đối tượng phổ biến: C++, java, C#, objective C…
Đặc trưng
- Đóng gói dữ liệu: dữ liệu được tổ chức thành các thuộc tính của lớp đối tượng Việc truy nhập đến dữ liệu phải thông qua các phương thức của đối tượng lớp
PTIT
Trang 9- Sử dụng lại mã nguồn: việc sử dụng lại mã nguồn được thể hiện thông qua cơ chế kế thừa Cơ chế này cho phép các lớp đối tượng có thể kế thừa từ các lớp đối tượng khác Khi đó trong lớp dẫn xuất có thể sử dụng các phương thức của các lớp cơ sở mà không cần phải định nghĩa lại
Ưu điểm
- Không có nguy cơ dữ liệu bị thay đổi tự do trong chương trình vì dữ liệu được đóng gói vào các đối tượng Nếu muốn truy nhập dữ liệu phải thông qua các phương thức được cho phép của đối tượng
- Khi thay đổi cấu trúc dữ liệu của đối tượng, không cần phải thay đổi mã nguồn của các đối tượng khác mà chỉ cần thay đổi một số thành phần của lớp dẫn xuất
- Có thể sử dụng lại mã nguồn, tiết kiệm tài nguyên, chi phí thời gian Vì nguyên tắc kế thừa cho phép các lớp dẫn xuất sử dụng các phương thức từ lớp cơ sở như những phương thức của chính nó, mà không cần phải định nghĩa lại
- Phù hợp với các dự án phần mềm lớn, phức tạp
1.1.2.2 Phương pháp phân tích và thiết kế hướng đối tượng
Vấn đề cơ bản đặt ra cho phương pháp lập trình hướng đối tượng là từ một bài toán ban đầu, làm sao để thu được một tập các đối tượng, với các chức năng được phối hợp với nhau, đáp ứng được yêu cầu của bài toán đặt ra
Phương pháp phân tích thiết kế hướng đối tượng ra đời nhằm trả lời cho câu hỏi này Mục đích là xây dựng một tập các lớp đối tượng tương ứng với mỗi bài toán
Phương pháp này gồm 2 pha chính:
- Pha phân tích: Chuyển đổi yêu cầu bài toán từ ngôn ngữ tự nhiên sang ngôn ngữ mô hình
- Pha thiết kế: Chuyển đổi bài toán từ ngôn ngữ mô hình sagn một mô hình cụ thể có thể cài đặt được
Hai pha trên gồm các bước sau:
- Mô tả bài toán
- Đặc tả yêu cầu
- Trích chọn đối tượng
- Mô hình hóa lớp đối tượng
- Thiết kế tổng thể
- Thiết kế chi tiết
Bước 1: Mô tả bài toán
Bài toán ban đầu được phát triển dưới dạng ngôn ngữ tự nhiên, bao gồm:
- Mục đích, chức năng chung
PTIT
Trang 10- Các yêu cầu về thông tin dữ liệu
- Các yêu cầu về chức năng thực hiện
Bước 2: Đặc tả yêu cầu
Các yêu cầu được hình thức hóa lên mức cao hơn bằng cách sử dụng ngôn ngữ kịch bản để
mô tả Trong một kịch bản, mỗi chức năng, mỗi hoạt động được mô tả bằng một kịch bản, bao gồm:
- Các tác nhân tham gia vào kịch bản
- Vai trò của mỗi tác nhân trong kịch bản
- Thứ tự các hành động mà mỗi tác nhân thực hiện: khi nào thực hiện, tác động vào tác nhân nào, thông tin nào được trao đổi
Quá trình trên được tiến hành với tất cả các chức năng yêu cầu của hệ thống
Bước 3: Trích chọn đối tượng
Tiến hành đề xuất các đối tượng có mặt trong hệ thống:
- Dựa vào các kịch bản được mô tả trong bước hai, chọn ra các tác nhân có xuất hiện để
đề xuất thành các đối tượng
- Lựa chọn các đối tượng bằng cách loại bỏ các tác nhân bên ngoài hệ thống, các tác nhân trùng lặp
- Cuối cùng, ta thu được các đối tượng của hệ thống
Bước 4: Mô hình hóa lớp đối tượng
Tiến hành trừu tượng hóa đối tượng thành các lớp:
- Thu thập tất cả các thuộc tính của mỗi đối tượng vừa thu thập được, dựa vào yêu cầu
về thông tin trong yêu cầu hệ thống từ bước 1
- Thu thập các hành động mà mỗi đối tượng cần thực hiện, dựa vào các kịch bản mà đối tượng tương ứng có tham gia trong bước 2
- Nhóm các đối tượng tương tự nhau, hoặc có nhiều thuộc tính gần giống nhau
- Loại bỏ một số thuộc tính cá biệt, riêng tư của một số đối tượng trong nhóm
- Mô hình mỗi nhóm đối tượng còn lại thành lớp: Các thuộc tính chung của các đối tượng thành thuộc tính của lớp, các hành động của các đối tượng thành phương thức của lớp
Kết quả thu được của bước này là một tập các lớp đối tượng ban đầu của hệ thống
Bước 5: Thiết kế tổng thể
Tiến hành thiết kế mối quan hệ giữa các lớp đối tượng trong hệ thống
- Xác định sơ đồ thiết kế nếu có giữa các lớp: Nếu hai lớp có một số thuộc tính chung, thì tách các thuộc tính chung làm thành một lớp cơ sở, và hai lớp ban đầu đề dẫn xuất
PTIT
Trang 11từ lớp cơ sở đó Thông thường các lớp trừu tượng sẽ làm lớp cơ sở, lớp cụ thể, chi tiết
sẽ làm lớp dẫn xuất
- Xác định tương tác nếu có giữa các lớp: Dựa vào kịch bản được mô tả trong bước 2, hai tác nhân có tương tác với nhau thì hai lớp tương ứng ở bước này cũng có tương tác với nhau
Kết quả thu được ở bước này là một sơ đồ quan hệ bên ngoài giữa các lớp trong hệ thống
Bước 6: Thiết kế chi tiết
Tiến hành thiết kế kiến trúc bên trong của mỗi lớp đối tượng:
- Tổ chức dữ liệu của lớp theo các thuộc tính Quy định phạm vi truy nhập cho từng thuộc tính
- Thiết kế chi tiết cách cư xử của lớp đối tượng thong qua các phương thức của lớp: Xác định kiểu dữ liệu trả về, kiểu tham số của phương thức, mô tả thuật toán chi tiết cho từng phương thức nếu cần
Kết quả thu được của bước này là một tập các lớp với thiết kế chi tiết kiến trúc bên trong Sau các bước phân tích thiết kế hướng đối tượng từ một yêu cầu của bài toán ban đầu, ta thu được một mô hình hệ thống hướng đối tượng chi tiết:
- Có cái nhìn tổng quan, vĩ mô về hệ thống bằng mô hình thiết kế tổng quan, chỉ rõ số lượng các lớp đối tượng, mối quan hệ kế thừa và quan hệ tương tác giữa các lớp đối tượng trong hệ thống
- Có cái nhìn chi tiết, vi mô về hệ thống bằng mô hình thiết kế chi tiết Mô hình này chỉ
rõ bên trong mỗi lớp đối tượng: các thuộc tính, các phương thức với kiểu trả về và kiểu tham số, thuật toán chi tiết cho mỗi phương thức
Sau pha phân tích và thiết kế hướng đối tượng, ta có đặc tả hệ thống dưới dạng mô hình các lớp: quan hệ giữa các lớp và kiến trúc bên trong của mỗi lớp Đây là đầu vào cho pha lập trình hướng đối tượng
1.2 Các khái niệm cơ bản của lập trình hướng đối tượng
1.2.1 Đối tượng
Trong lập trình hướng đối tượng, đối tượng được coi là đơn vị cơ bản nhỏ nhất Các dữ liệu
và cách xử lý chỉ là thành phần của đối tượng mà không được coi là thực thể Một đối tượng chứa các dữ liệu của riêng nó, đồng thời có các phương thức (hành động) thao tác trên các dữ liệu đó:
Đối tượng = dữ liệu + phương thức
Trang 12- Đối tượng là một thể hiện của lớp, là một thực thể tồn tại trong hệ thống
Lớp dùng để biểu diễn đối tượng, cho nên lớp cũng có thuộc tính và phương thức:
- Thuộc tính của lớp tương ứng với thuộc tính của đối tượng
- Phương thức của lớp tương ứng với hành động của đối tượng
Lưu ý: Một lớp có thể có một trong các khả năng sau
- Hoặc chỉ có thuộc tính, không có phương thức
- Hoặc chỉ có phương thức không có thuộc tính
- Hoặc có cả thuộc tính và phương thức
- Lớp không có thuộc tính và phương thức nào là lớp trừu tượng Các lớp này không có đối tượng tương ứng
1.2.5 Thông điệp và truyền thông điệp
Một chương trình hay ứng dụng lớn thường chứa nhiều đối tượng khác nhau Các đối tượng tương tác và giao tiếp với nhau bằng cách gửi các thông điệp Khi đối tượng A muốn thực hiện các phương thức của đối tượng B thì A gửi một thông điệp tới B
Đôi khi đối tượng nhận cần nhiều thông tin để biết chính xác thực hiện công việc gì Các thông tin này được truyền kèm theo thong điệp và được gọi là tham số
Một thông điệp gồm có:
- Đối tượng nhận thông điệp
- Tên của phương thức thực hiện
- Các tham số mà phương thức cần
1.2.6 Trừu tượng hoá đối tượng
1.2.6.1 Trừu tượng hóa đối tượng theo chức năng
Trừu tượng hóa đối tượng theo chức năng là quá trình mô hình hóa phương thức của lớp dựa
PTIT
Trang 13trên các hành động của các đối tượng Quá trình này tiến hành như sau:
- Tập hợp tất cả các hành động có thể có của đối tượng
- Nhóm các đối tượng có các hoạt động tương tự nhau, loại bỏ bớt các hoạt động cá biệt, tạo thành một nhóm chung
- Mỗi nhóm đối tượng đề xuất một lớp tương ứng
- Các hành động chung của nhóm đối tượng sẽ cấu thành các phương thức của lớp tương ứng
1.2.6.2 Trừu tượng hóa đối tượng theo dữ liệu
Trừu tượng hoá đối tượng theo dữ liệu chính là quá trình mô hình hoá các thuộc tính của lớp dựa trên các thuộc tính của các đối tượng tương ứng Quá trình này được tiến hành như sau:
- Tập hợp tất cả các thuộc tính có thể có của các đối tượng
- Nhóm các đối tượng có các thuộc tính tương tự nhau, loại bỏ bớt các thuộc tính cá biệt, tạo thành một nhóm chung
- Mỗi nhóm đối tượng đề xuất một lớp tương ứng
- Các thuộc tính chung của nhóm đối tượng sẽ cấu thành các thuộc tính tương ứng của lớp được đề xuất
Ưu điểm của trừu tượng hóa đối tượng
- Tập trung vào vấn đề cần quan tâm
- Xác định những đặc tính thiết yếu và những hành động cần thiết
- Giảm thiểu những chi tiết không cần thiết
Việc trừu tượng hóa dữ liệu là cần thiết, bởi vì không thể mô tả tất cả các hành động và các thuộc tính của một thực thể Vấn đề mấu chốt là tập trung đến những hành vi cốt yếu và áp dụng chúng trong ứng dụng
1.2.7 Tính đóng gói
- Các dữ liệu được đóng gói vào trong đối tượng Mỗi dữ liệu có một phạm vi truy nhập riêng
- Không thể truy nhập đến dữ liệu một cách tự do như lập trình cấu trúc
- Muốn truy nhập đến các dữ liệu đã được bảo vệ, phải thông qua các đối tượng, nghĩa
là phải sử dụng các phương thức mà đối tượng cung cấp mới có thể truy nhập đến dữ liệu của đối tượng đó
1.2.8 Tính kế thừa
Tính kế thừa của lập trình hướng đối tượng cho phép một lớp có thể kế thừa từ một số lớp đã tồn tại Khi đó, lớp mới có thể sử dụng dữ liệu và phương thức của các lớp cơ sở như là của mình Ngoài ra, lớp dẫn xuất còn có thể bổ sung thêm một số dữ liệu và phương thức Ưu điểm của kế thừa là khi thay đổi dữ liệu của một lớp, chỉ cần thay đổi các phương thức trong
PTIT
Trang 14phạm vi lớp cơ sở mà không cần thay đổi trong lớp dẫn xuất
1.2.9 Tính đa hình
Đa hình là khái niệm luôn đi kèm kế thừa Do tính kế thừa, một lớp có thể sử dụng lại các phương thức của lớp khác Tuy nhiên, nếu cần thiết lớp dẫn xuất cũng có thể định nghĩa lại một số phương thức của lớp cơ sở Đó là sự nạp chồng phương thức trong kế thừa Nhờ sự nạp chồng phương thức này, ta chỉ cần gọi tên phương thức bị nạp chồng từ đối tượng mà không cần quan tâm đó là đối tượng của lớp nào Chương trình sẽ tự động kiểm tra xem đối tượng là thuộc kiểu lớp cơ sở hay thuộc lớp dẫn xuất, sau đó sẽ gọi phương thức tương ứng với lớp đó
1.3 Các ngôn ngữ lập trình hướng đối tượng
1.3.1 C++
C++, ra đời vào giữa những năm 1980, là một ngôn ngữ lập trình hướng đối tượng được mở rộng từ ngôn ngữ lập trình cấu trúc C Cho nên, C++ là ngôn ngữ lập trình nửa hướng đối tượng, nửa hướng cấu trúc
Những đặc trưng hướng đối tượng của C++
- Cho phép định nghĩa lớp đối tượng
- Cho phép đóng gói dữ liệu vào các lớp đối tượng Cho phép định nghĩa phạm vi truy nhập dữ liệu của lớp bằng các từ khoá phạm vi
- Cho phép kế thừa lớp với các kiểu kế thừa khác nhau tuỳ vào từ khoá dẫn xuất
- Cho phép lớp kế thừa sử dụng các phương thức của lớp bị kế thừa (trong phạm vi quy định)
- Cho phép định nghĩa chồng phương thức trong lớp kế thừa
Những vi phạm hướng đối tượng của C++
Những vi phạm này là do kết quả kế thừa từ ngôn ngữ C, một ngôn ngữ lập trình thuần cấu trúc
- Cho phép định nghĩa và sử dụng các biến dữ liệu tự do
- Cho phép định nghĩa và sử dụng các hàm tự do
- Ngay cả khi dữ liệu được đóng gói vào lớp, dữ liệu vẫn có thể truy nhập trực tiếp như
dữ liệu tự do bởi các hàm bạn, lớp bạn (friend) trong C++
1.3.2 Net
Các ngôn ngữ lập trình NET (còn được gọi là NET Frameworks) của MicroSoft ra đời vào cuối những năm 1990 để cạnh tranh với ngôn ngữ lập trình Java .NET là một ngôn ngữ hoàn toàn hướng đối tượng, hơn nữa, nó còn cung cấp một giao diện lập trình đồ hoạ thân thiện và đẹp mắt với truyền thống lập trình kéo thả của MicroSoft
Một số đặc điểm của ngôn ngữ NET:
PTIT
Trang 15trong chương trình đều được mô hình dưới dạng một lớp nhất định Không có dữ liệu
1.3.3 Java
Java là một ngôn ngữ lập trình được Sun Microsystems giới thiệu vào tháng 6 năm 1995 Java được xây dựng trên nền tảng của C và C++: Java sử dụng cú pháp của C và đặc trưng hướng đối tượng của C++
Một số đặc điểm của Java:
- Java là một ngôn ngữ lập trình hoàn toàn hướng đối tượng: Tất cả các thực thể đều được coi là một đối tượng, là một thể hiện cụ thể của một lớp xác định Không có dữ liệu tự do và hàm tự do trong Java, tất cả đều được đóng gói vào các lớp xác định
- Java là ngôn ngữ vừa biên dịch vừa thông dịch Đầu tiên mã nguồn được biên dịch thành dạng bytecode; sau đó được thực thi trên từng loại máy nhờ trình thông dịch Điều này tạo ra khả năng hoạt động độc lập với nền tảng phần cứng của các ứng dụng Java
- Java cho phép người dùng tự tạo các đối tượng thư viện JavaBeans của mình (tương tự như các thành phần UserControl của NET) Các đối tượng Bean sẽ được sử dụng lại như các thành phần có sẵn trong các ứng dụng khác Điều này mở ra khả năng to lớn
để tiết kiệm công sức viết mã nguồn và khả năng xây dựng các kỹ thuật cho một nền công nghiệp lắp ráp phần mềm
Ngôn ngữ lập trình hướng đối tượng Java sẽ được trình bày chi tiết trong toàn bộ phần 2 của giáo trình này
1.4 Case study
Các nhóm sinh viên lựa chọn đề tài cho bài tập nhóm Sau đó tiến hành các bước phân tích thiết kế theo hướng đối tượng cho bài toán đã lựa chọn
TỔNG KẾT CHƯƠNG 1
Chương 1 đã trình bày các nội dung sau:
- Phương pháp tiếp cận của lập trình truyền thống
- Phương pháp tiếp cận của lập trình hướng đối tượng
- Phương pháp phân tích thiết kế theo hướng đối tượng
PTIT
Trang 16- Các khái niệm về đối tượng, lớp đối tượng, phương thức, thuộc tính trong lập trình hướng đối tượng
- Khái niệm về sự truyền thông điệp giữa các đối tượng, trừu tượng hóa đôi tượng
- Trình bày ba đặc trưng cơ bản của lập trình hướng đối tượng là tính đóng gói, tính kế thừa và tính đa hình
- Giới thiệu một số ngôn ngữ lập trình hướng đối tượng phổ biến: C++, Java, C#
CÂU HỎI VÀ BÀI TẬP CHƯƠNG 1
1 So sánh 2 phương pháp tiếp cận của lập trình truyền thống và lập trình hướng đối tượng
2 Xác định các đối tượng trong bài toán quản lý sinh viên của một trường đại học
3 Xác định các thuộc tính và hành động của các đối tượng trong câu 2
PTIT
Trang 17CHƯƠNG 2: GIỚI THIỆU NGÔN NGỮ C++
Nội dung chương này trình bày các vấn đề chính về ngôn ngữ C++:
- Cấu trúc chương trình C++
- Từ khóa, toán tử, các kiểu dữ liệu trên C++
- Cấu trúc lệnh điều khiển, vào ra trên C++
- Cấp phát bộ nhớ trên C++
- Dữ liệu kiểu cấu trúc trên C++
2.1 Cấu trúc chương trình trên C++
Sau đây là ví dụ về môt chương trình C++ đơn giản
#include <iostream.h>
Các câu bắt đầu bằng dấu (#) được dùng cho các chỉ thị, được dùng để báo hiệu cho trình dịch
về các hàm thư viện, các định nghĩa
Ở đây câu lệnh #include <iostream.h> báo cho trình dịch biết cần phải "include" thư viện
iostream Đây là một thư viện vào ra cơ bản trong C++
int main ()
Dòng này tương ứng với phần bắt đầu khai báo hàm main Hàm main là điểm mà tất cả các chương trình C++ bắt đầu thực hiện Nó không phụ thuộc vào vị trí của hàm này (ở đầu, cuối hay ở giữa của mã nguồn) mà nội dung của nó luôn được thực hiện đầu tiên khi chương trình bắt đầu Mọi chương trình C++ đều phải tồn tại một hàm main Theo sau main là một cặp
PTIT
Trang 18ngoặc đơn bởi vì nó là một hàm Trong C++, tất cả các hàm mà sau đó là một cặp ngoặc đơn () thì có nghĩa là nó có thể có hoặc không có tham số (không bắt buộc) Nội dung của hàm main tiếp ngay sau phần khai báo chính thức được bao trong các ngoặc nhọn ( { } ) như trong
ví dụ
cout << "Hello World";
cout là một dòng output chuẩn trong C++ được định nghĩa trong thư viện iostream và những
gì mà dòng lệnh này làm là gửi chuỗi kí tự "Hello World" ra màn hình Dòng này kết thúc bằng dấu chấm phẩy(( ; ) Kí tự này được dùng để kết thúc một lệnh và bắt buộc phải có sau mỗi lệnh trong chương trình C++
return 0;
Lệnh return kết thúc hàm main và trả về mã đi sau nó, trong trường hợp này là 0
2.2 Từ khóa
Ngoài các từ khóa có trong C, C++ còn bổ sung thêm một số từ khóa mới thông dụng sau:
2.3 Kiểu dữ liệu
Một số kiểu dữ liệu đơn giản trong C++ như sau:
Số nguyên ngắn không dấu unsigned short 2 byte Từ 0 đến 65535
đến 2,147,438,647
PTIT
Trang 192.4 Khai báo biến, hằng
2.4.1 Khai báo biến
- <tên_biến>: Tên biến không được đặt trùng với tên các từ khóa , không được bắt đầu
bằng chữ số, không chứa ký tự trắng, ký tự đánh dấu, cho phép dấu _, phân biệt chữ hoa, chữ thường
Lưu ý: C++ cho phép khai báo biến có thể đặt ở bất kỳ chỗ nào trong chương trình trước khi
Trang 20Trong đó:
- const: Là từ khóa khai báo hằng
- <kiểu_dữ_liệu>: Là một trong các kiểu dữ liệu mô tả ở mục trên hoặc kiểu dữ liệu do
người dùng định nghĩa
- tên_hằng: giống tên biến, thường viết dưới dạng chữ in hoa
- giá_trị_hằng: giá trị cố định và không thay đổi của hằng trong suốt quá trình thực hiện
chương trình
Ví dụ:
const int MAX=20; //Khai báo hằng số nguyên tên là MAX có giá trị 20
2.5 Các lệnh vào ra và các lệnh điều khiển
Giá trị của một biểu thức đưa ra màn hình có các kiểu sau:
- Kiểu dữ liệu cơ sở (char, int, float, double)
- Xâu ký tự
- Con trỏ (trừ con trỏ char*)
PTIT
Trang 21Nếu muốn đưa ra địa chỉ biến xâu ký tự phải thực hiện phép chuyển kiểu tường minh như (char*)(void*)
Trang 22Value of y:1.234567e+17
Value of ch:WelcomeC++
Address of n:0xfff2
Address de ch:0x00b2
2.5.1.2 Định dạng thong tin đưa ra màn hình
Trong C++ cung cấp một số định dạng cơ bản khi in ra màn hình nằm trong file thư viện iomanip.h như sau:
- endl: Tương đương với ký tự xuống dòng “\n”
- setw(n): Chỉ ra độ rộng cần in ra Nếu n< độ dài thực tế của dữ liệu thì độ rộng in ra
bằng độ rộng của dữ liệu Ngược lại, nếu n> độ dài thực tế của dữ liệu thì vị trí dư thừa được chèn bằng khoảng trống trước dữ liệu
Ví dụ:
cout<<setw(5)<<”ABC”;
Lệnh trên in ra 2 dấu cách và chuỗi ký tự ABC
- setionsflags(ios::showpoint), setprecision(n): Chỉ ra số chữ số của phần thập phân in
2.5.1.3 Đọc dữ liệu từ thiết bị vào chuẩn cin
Nếu như cout dùng để chỉ thiết bị ra chuẩn, thì cin dùng để chỉ thiết bị vào chuẩn Toán tử
“>>” được dùng cùng với cin để nhập các giá trị
Xét ví dụ:
int a;
cin >> a;
Ý nghĩa: Yêu cầu đọc các ký tự từ bàn phím, chuyển thành số nguyên và gán cho biến a
Có thể sử dụng toán tử “>>” để nhập dữ liệu cho các biến có kiểu float, int, char, double, char*
Có thể nhập nhiều giá trị cùng kiểu hay khác kiểu bằng cách viết liên tiếp tên các biến cần
nhập giá trị cùng với toán tử “>>” ngay sau cin
PTIT
Trang 23Để nhập một dãy ký tự khong quá n ký tự và chứa vào mảng ký tự h, ta dùng cú pháp sau:
sẽ thoát ra khỏi cấu trúc if và thực hiện các lệnh tiếp theo sau cấu trúc if
Biểu diễn dưới dạng lưu đồ cách hoạt động của cấu trúc if:
PTIT
Trang 25if (a>b) cout << “a lớn hơn b”;
else if (a<b) cout << “b lớn hơn a”;
else cout << “a bằng b”;
}
2.5.2.2 Câu lệnh lựa chọn switch…case
switch (biểu_thức_nguyên)
{
case hằng_nguyên_1: Khối_lệnh_1; break;
case hằng_ nguyên_2: Khối_lệnh_2; break;
…
case hằng_nguyên_n: Khối_lệnh_n; break;
default: Khối_lệnh_n+1; break
PTIT
Trang 26cout<<"\nNhap 1 de tinh tong”;
cout<<”\nNhap 2 de tinh hieu”;
cout<<”\nNhap 3 de tinh tich”;
Trang 28Biểu diễn dưới dạng lưu đồ:
Ví dụ: Chương trình nhập vào số nguyên có hai chữ số
#include <iostream.h>
main()
PTIT
Trang 29while(i>10 && i<100);
cout<<”So vua nhap la so co hai chu so”;
Thực hiện:
- Biểu_thức_1: Được gọi là biểu thức khởi đầu có nhiệm vụ khởi đầu các biến sử dụng trong chu trình, biểu_thức_1 chỉ được thực hiện duy nhất một lần khi bắt đầu bước vào chu trình Nếu trong chu trình phải khởi đầu nhiều biểu thức thì mỗi biểu thức được phân biệt với nhau bởi một kí tự ','
- Biểu_thức_2: Được gọi là biểu thức kiểm tra và được thực hiện ngay sau khi thực hiện xong biểu_thức_1, nếu biểu thức kiểm tra có giá trị đúng (khác 0) thì câu lệnh trong thân của chu trình for sẽ được thực hiện, nếu biểu thức kiểm tra có giá trị sai thì điều khiển của chương trình chuyển về lệnh kế tiếp ngay sau thân của chu trình for
- Biểu_thức_3: Được gọi là biểu thức khởi đầu lại có nhiệm vụ khởi đầu lại các biến trong chu trình và được thực hiện ngay sau khi thực hiện xong câu_lệnh Chu trình sẽ được lặp lại bằng việc thực hiện biểu thức kiểm tra
PTIT
Trang 30Biểu diễn dưới dạng lưu đồ:
2.5.2.6 Lệnh rẽ nhánh continue, break, goto
- Lệnh rẽ nhánh break : kết thúc câu lệnh điều khiển đang sử dụng (Ví dụ sử dụng trong lệnh chuyển mạch switch)
- Lệnh continue : trái với break, lệnh continue cho phép chuyển sang vòng lặp tiếp theo mà không cần thực hiện phần còn lại
- Lệnh nhẩy goto : lệnh nhẩy goto sẽ chuyển điều khiển chương trình tới 1 nới nào đó có gán 1 nhãn label để đánh dấu
2.6 Kiểu dữ liệu cấu trúc
2.6.1 Khái niệm về kiểu cấu trúc
Kiểu cấu trúc (Structure) là kiểu dữ liệu bao gồm nhiều thành phần có kiểu khác nhau, mỗi thành phần được gọi là một trường (field) Sự khác biệt giữa kiểu cấu trúc và kiểu mảng là: các phần tử của mảng là cùng kiểu còn các phần tử của kiểu cấu trúc có thể có kiểu khác nhau
PTIT
Trang 31Định nghĩa kiểu cấu trúc
- struct: là tên từ khóa để khai báo một cấu trúc, bắt buộc phải có khi định nghĩa cấu trúc
- Tên_cấu_trúc: là tên do người dùng tự định nghĩa, tuân theo quy tắc đặt tên của C++ Tên
này sẽ trở thành tên của kiểu dữ liệu có cấu trúc tương ứng
- Thuộc tính: mỗi thuộc tính của cấu trúc được khai báo như khai báo một biến thuộc kiểu
dữ liệu thông thường, bao gồm kiểu dữ liệu và tên biến tương ứng Mỗi khai báo thuộc tính kết thúc bằng dấu “;”
Ví dụ: Khai báo kiểu cấu trúc SinhVien gồm các thông tin mã sinh viên, họ tên, địa chỉ như sau:
unsigned int Ngay;
unsigned int Thang;
};
Dạng 2: dùng từ khóa typedef để định nghĩa kiểu
PTIT
Trang 32Dạng 3: Khai báo cấu trúc lồng cấu trúc
Các cấu trúc có thể định nghĩa lồng nhau khi một thuộc tính của cấu trúc cũng là một kiểu cấu trúc Khi đó việc định nghĩa cấu trúc cha được thực hiện như một cấu trúc thông thường, với khai báo thuộc tính là một cấu trúc con:
Trang 33NgayThang NamSinh;
char DiaChi[32];
};
2.6.2 Cách sử dụng kiểu cấu trúc
2.6.2.1 Khai báo biến cấu trúc
Việc khai báo biến cấu trúc cũng tương tự khia báo biến thuộc kiểu dữ liệu chuẩn:
Cú pháp:
- Đối với cấu trúc được định nghĩa theo dạng 1:
struct <Tên_cấu_trúc> <Biến 1>, <Biến 2> ;
- Đối với cấu trúc định nghĩa theo dạng 2:
<Tên_cấu_trúc> <Biến 1>, <Biến 2> ;
Ví dụ:
- Khai báo biến NgaySinh có kiểu cấu trúc NgayThang:
struct NgayThang NgaySinh;
- Khai báo biến SV có kiểu cấu trúc SinhVien
SinhVien SV;
2.6.2.2 Khởi tạo giá trị ban đầu cho cấu trúc
Khởi tạo cấu trúc có thể thực hiện trong lúc khai báo biến cấu trúc Các thuộc tính của cấu trúc được khởi tạo đặt trong cặp dấu {} và được phân cách nhau bằng dấu “,”
unsigned int Ngay;
PTIT
Trang 34};
Khởi tạo biến cấu trúc NgaySinh:
struct NgayThang NgaySinh = {22,10,1980} ;
Đối với các biến có cấu trúc lồng nhau, việc khởi tạo cũng thực hiện tương tự đối với các cấu trúc con
Ví dụ: Với khai báo cấu trúc sau
typedef struct
{
unsigned int Ngay;
unsigned int Thang;
Trang 35SVien SV ={
1,
“Trần Văn A”, {10 , 2 , 2013},
“Hà Nội”
};
Có thể truy xuất như sau:
cout << SV.HoTen; // hiển thị ra “Trần Văn A”
cout << SV.NamSinh.Nam; // hiển thị ra 2013
SV.MaSV += 2; // Tăng mã sinh viên lên 2
Ví dụ: Chương trình định nghĩa và sử dụng cấu trúc SinhVien
#include<iostream>
using namespace std;
typedef struct{
unsigned int Ngay;
unsigned int Thang;
unsigned int Nam;
cout<<"\nMa Sinh Vien:"<<SV.MaSV;
cout<<"\nTen Sinh Vien:"<<SV.HoTen;
cout<<"\nNgay Sinh:"<<SV.NamSinh.Ngay<<"/"<<SV.NamSinh.Thang<<
"/"<<SV.NamSinh.Nam;
cout<<"\nDia chi"<<SV.DiaChi;
PTIT
Trang 36return 0;
}
int main()
{
// khoi tao gia tri ban dau cho cau truc sinh vien
SinhVien SV={1,"Tran Van A",{1, 4, 2013},"Ha Noi"};
DisplaySV(SV);
// Thay doi gia tri cac thuoc tinh
cout<<"\nThay doi thong tin";
cout<<"\nMa SV:";cin>>SV.MaSV;
cout<<"\nNgay sinh:"; cin>>SV.NamSinh.Ngay;
cout<<"\nThang sinh:"; cin>>SV.NamSinh.Thang;
cout<<"\nNam sinh:"; cin>>SV.NamSinh.Nam;
cout<<"\nThong tin sau khi thay doi";
Con trỏ cấu trúc là một con trỏ trỏ đến địa chỉ của một biến có kiểu cấu trúc Cách khai báo và
sử dụng con trỏ cấu trúc được thực hiện như con trỏ thông thường
Khai báo con trỏ cấu trúc:
unsigned int Ngay;
unsigned int Thang;
unsigned int Nam;
PTIT
Trang 37} NgayThang;
Có thể khai báo con trỏ cấu trúc như sau:
NgayThang *pNgayThang;
Gán giá trị cho con trỏ cấu trúc:
Khi khai báo biến con trỏ cấu trúc, biến con trỏ chưa có địa chỉ cụ thể Mốn thao tác trên con trỏ cấu trúc hợp lệ cần cấp phát cho nó một vùng nhớ cụ thể
Một con trỏ cấu trúc có thể trỏ đến địa chỉ của một biến cấu trúc có cùng kiểu thông qua phép gán:
<Tên_con_trỏ_cấu_trúc> = &<Tên_biến_cấu_trúc>;
Ví dụ:
SinhVien *pSinhVien;
SinhVien SV;
pSinhVien = &SV; // con trỏ pSinhVien trỏ đến địa chỉ của biến SV
Cấp phát bộ nhớ động cho con trỏ cấu trúc:
Để tạo ra một con trỏ cấu trúc mới, không trỏ vào một biến cấu trúc sẵn có, ta có thể cấp phát vùng nhớ cho nó
SinhVien *pSinhVien = new SinhVien;
Sau khi cấp phát vùng nhớ cho con trỏ bằng từ khóa new, khi không dùng con trỏ nữa, hoặc
chuyển sang địa chỉ khác, ta phải giải phóng vùng nhớ được cấp phát cho con trỏ bằng từ
khóa delete:
Cú pháp: delete<Tên_biến_con_trỏ>;
Ví dụ:
// cấp phát vùng nhớ cho con trỏ cấu trúc
SinhVien *pSinhVien = new SinhVien;
// giải phóng vùng nhớ khi không sử dụng
delete pSinhVien;
Chú ý: Thao tác delete chỉ thực hiện đối với con trỏ mà trước đó nó được cấp phát bộ nhớ
PTIT
Trang 38thông qua thao tác new
Truy cập thuộc tính của con trỏ cấu trúc:
Để truy cập các thuộc tính của cấu trúc thông qua con trỏ cấu trúc, có thể dùng một trong hai cách sau:
unsigned int Thang;
} NgayThang;
//Khai báo và cấp phát vùng nhớ cho con trỏ
NgayThang *pNgayThang = new NgayThang;
unsigned int Ngay;
unsigned int Thang;
unsigned int Nam;
Trang 39cout<<"\nMa Sinh Vien:"<<SV->MaSV;
cout<<"\nTen Sinh Vien:"<<SV->Ten;
cout<<"\nNgay sinh:"; cin>>pSV1->NamSinh.Ngay;
cout<<"\nThang sinh:"; cin>>pSV1->NamSinh.Thang;
cout<<"\nNam sinh:"; cin>>pSV1->NamSinh.Nam;
cout<<"\nDia chi:"; cin>>pSV1->DiaChi;
cout<<"\nThong tin sinh vien thu 2";
DisplaySV(pSV1);
return 1;
}
PTIT
Trang 402.6.3.2 Mảng cấu trúc
Để xử lý nhiều đối tượng có cùng một kiểu dữ liệu cấu trúc, ta sử dụng mảng cấu trúc
Khai báo mảng cấu trúc:
Cú pháp khai báo tĩnh:
<Tên_kiểu_cấu_trúc> <Tên_mảng>[Số_phần_tử_mảng];
Cú pháp khai báo động: Vì mảng một chiều là tương đương với con trỏ cùng kiểu do đó cú
pháp khai báo mảng cấu trúc động cũng giống khai báo con trỏ cấu trúc
<Tên_kiểu_cấu_trúc> *<Tên_mảng>;
Tuy nhiên, việc cấp phát bộ nhớ cho mảng khác với con trỏ Cú pháp như sau:
<Tên_mảng> = new <Tên_cấu_trúc>[Số_phần_tử_mảng];
Ví dụ:
SinhVien SV[10]; // khai báo mảng SV tĩnh có 10 phần tử
SinhVien *SV = new SinhVien[20]; // khai báo mảng SV động có 20 phần tử
Truy cập các phần tử trong mảng cấu trúc:
Việc truy cập đến các phần tử trong mảng cấu trúc giống với việc truy cập đến các phần tử của mảng thông thường
Ví dụ:
// Khai báo
SinhVien *SV = new SinhVien[20];
// Truy cập đến tên của sinh viên thứ i
unsigned int Ngay;
unsigned int Thang;
unsigned int Nam;
}NgayThang;
PTIT