1. Trang chủ
  2. » Giáo án - Bài giảng

Lập trình hướng đối tượng với c

191 15 0

Đang tải... (xem toàn văn)

Tài liệu hạn chế xem trước, để xem đầy đủ mời bạn chọn Tải xuống

THÔNG TIN TÀI LIỆU

Thông tin cơ bản

Định dạng
Số trang 191
Dung lượng 1,53 MB

Các công cụ chuyển đổi và chỉnh sửa cho tài liệu này

Nội dung

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 1

HỌ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 2

LỜ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 3

MỤ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 4

2.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 5

TỔ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 6

6.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 7

CHƯƠ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 8

Trong đó:

- 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 11

từ 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 13

trê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 14

phạ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 15

trong 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 17

CHƯƠ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 18

ngoặ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 19

2.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 20

Trong đó:

- 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 21

Nế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 22

Value 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 25

if (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 26

cout<<"\nNhap 1 de tinh tong”;

cout<<”\nNhap 2 de tinh hieu”;

cout<<”\nNhap 3 de tinh tich”;

Trang 28

Biể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 29

while(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 30

Biể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 32

Dạ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 33

NgayThang 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 35

SVien 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 36

return 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 38

thô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 39

cout<<"\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 40

2.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

Ngày đăng: 19/03/2021, 17:02

TÀI LIỆU CÙNG NGƯỜI DÙNG

TÀI LIỆU LIÊN QUAN