Trong ví dụ trên lệnh throw sẽ phát sinh ra một ngoại lệ có mã là 20 nhưng ngoại lệ
này đã bị bắt bởi khối lệnh try catch và câu lệnh trong khối catch đã được thực hiện.
Các khối câu lệnh try catch có thể lồng nhau.
37 trang |
Chia sẻ: nguyenlam99 | Lượt xem: 1101 | Lượt tải: 0
Bạn đang xem trước 20 trang tài liệu Bài giảng môn học kỹ thuật lập trình, để xem tài liệu hoàn chỉnh bạn click vào nút DOWNLOAD ở trên
thuật trong chương trình chính mà không phụ thuộc vào thứ
tự khai báo của các chương trình con.
- 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:
o Chương trình sáng sủa, dễ hiểu, dễ theo dõi.
o Tư duy giải thuật rõ ràng.
Nhược điểm:
- Lập trình cấu trúc không hỗ trợ mạnh việc sử dụng lại mã nguồn: giải thuật
luôn 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, nghĩa là phải viết lại chương trình.
- Không phù hợp với các phần mềm lớn: tư duy cấu trúc với các giải thuật chỉ
phù hợp với các bài toán nhỏ, nằm trong phạm vi một modul của chương trình.
Với dự án phần mềm lớn, lập trình cấu trúc tỏ ra không hiệu quả trong việc
giải quyết mối quan hệ vĩ mô giữa các modul của phần mềm.
Vấn đề: vấn đề cơ bản của lập trình cấu trúc là bằng cách nào để phân chia
chương trình chính thành các chương trình con cho phù hợp với yêu cầu, chức
năng và mục đích của mỗi bài toán. Thông thường, để phân rã bài toán trong lập
trình cấu trúc, người ta sử dụng phương pháp thiết kế top – down.
Phương pháp thiết kế Top – down:
Phương pháp thiết kế top – down tiếp cận bài toán theo hướng từ trên
xuống dưới, từ tổng quát đến chi tiết. Theo đó, một bài toán được chia thành các
bài toán con nhỏ hơn. Mỗi bài toán con lại được chia nhỏ tiếp, nếu có thể, thành
các bài toán con nhỏ hơn nữa. Quá trình này còn được gọi là quá trình làm mịn
Bài giảng Kỹ thuật lập trình – Ngành Truyền thông đa phương tiện
5
Bộ môn Truyền thông đa phương tiện – Trường Đại học Công nghệ thông tin và truyền thông
dần. Quá trình này sẽ dừng lại khi các bài toán con không cần chia nhỏ thêm nữa.
Nghĩa là, khi mỗi bài toán con đều có thể giải quyết bằng một chương trình con
với một giải thuật đơn giản.
Ví dụ, sử dụng phương pháp top-down để giải quyết bài toán xây một căn
nhà mới. Chúng ta có thể phân rã bài toán theo các bước như sau:
- Mức 1: chia bài toán xây nhà thành các bài toán nhỏ hơn như làm móng,
đổ cột, đổ trần, xây tường, lợp mái.
- Mức 2: phân rã các công việc ở mức 1 như việc làm móng nhà có thể
phân rã tiếp thành các công việc đào móng, gia cố nền, làm khung sắt, đổ
bê tông; công việc đổ cột được phân rã thành...
- Mức 3: phân rã các công việc ở mức 2 như việc đào móng có thể phân
chia tiếp thành các công việc như đo đạc, cắm mốc, chăng dây, đào và
kiểm tra móng. Việc gia cố nền được phân rã thành...
Quá trình phân rã có thể dừng ở mức này, bởi vì các công việc con thu được
như đo đạc, cắm mốc, chăng dây, đào... có thể thực hiện được ngay, không
cần chia nhỏ thêm nữa.
Lưu ý: Cùng sử dụng phương pháp top-down với cùng một bài toán, nhưng có
thể cho ra nhiều kết quả khác nhau. Nguyên nhân là do sự khác nhau trong tiêu
chí để phân rã một bài toán thành các bài toán con.
Ví dụ: vẫn áp dụng phương pháp top-down để giải quyết bài toán xây nhà, nhưng
nếu sử dụng một cách khác để phân chia bài toán, ta có thể thu được kết quả khác
biệt so với phương pháp ban đầu:
- Mức 1: chia bài toán xây nhà thành các bài toán nhỏ hơn như làm phần gỗ,
làm phần sắt, làm phần bê tông và làm phần gạch.
- Mức 2: phân rã các công việc ở mức thứ nhất là làm phần gỗ có thể chia
thành các công việc như xẻ gỗ, gia công gỗ, tạo khung, lắp vào nhà. Việc
làm sắt có thể chia nhỏ thành...
Rõ ràng, với cách làm mịn thế này, ta sẽ thu được một kết quả khác hẳn với
cách thức đã thực hiện ở phần trên.
1.1.3. Lập trình hướng đối tượng
Trong lập trình hướng đối tượng:
- Người ta có thể coi các thực thể trong chương trình là các đối tượng và sau đó
trừu tượng hóa đối tượng thành lớp đối tượng.
- Dữ liệu được tổ chức thành các thuộc tính của lớp. Người ta ngăn chặn việc thay
đổi tùy tiện dữ liệu trong chương trình bằng các cách giới hạn truy nhập như chỉ
cho phép truy nhập dữ liệu thông qua đối tượng, thông qua các phương thức mà
đối tượng được cung cấp...
Bài giảng Kỹ thuật lập trình – Ngành Truyền thông đa phương tiện
6
Bộ môn Truyền thông đa phương tiện – Trường Đại học Công nghệ thông tin và truyền thông
- Quan hệ giữa các đối tượng là quan hệ ngang hàng hoặc quan hệ kế thừa: Nếu
lớp B kế thừa từ lớp A thì A được gọi là lớp cơ sở và B gọi là lớp dẫn xuất.
Ngôn ngữ lập trình hướng đối tượng phổ biến hiện nay là Java, C++, C#... Mặc
dù C++ cũng có những đặc trưng cơ bản của lập trình hướng đối tượng nhưng
vẫn không phải là ngôn ngữ lập trình thuần hướng đối tượng.
Đặc trưng:
Lập trình hướng đối tượng có ba đặc trưng cơ bản:
- Tính đóng gói: dữ liệu luôn đượ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.
- Tính đa dạng: hiểu theo đúng nghĩa là khả năng thể hiện nhiều hình thái.
Trong C++, tính đa dạng cho phép một tên được thực hiện nhiều hành động.
Chẳng hạn, chúng ta có 3 lớp box, triangle và circle, mỗi lớp mô tả một hình,
tất cả các lớp đó đều có hàm show() là hàm vẽ các đối tượng lên màn hình.
Điều này có nghĩa là hàm này thể hiện trong 3 lớp và nó sẽ vẽ ra 3 hình khác
nhau ở trên. Như bạn có thể thấy trong C++, một hàm có thể có nhiều mục
đích người ta gọi là hàm nạp chồng (function overloading), toán tử nạp chồng
(operator overloading) hiểu là tính đa dạng của C++. Mục đích chính của tính
đa dạng là thực hiện nhiều chức năng phức tạp
- Tính 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. Nói cách khác, tính kế thừa cho phép một lớp mới được định
nghĩa bằng cách mở rộng hay sửa đổi từ một hoặc nhiều lớp đã tồn tại. Lớp
mới là lớp dẫn xuất hay lớp kế thừa còn lớp đã tồn tại là lớp cơ sở. Khi đó,
trong các 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.
1.2. Lập trình hướng đối tượng và ngôn ngữ C++
1.2.1. Ưu điểm của lập trình hướng đối tượng
- Không còn nguy cơ 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 vào 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 một đối tượng, không cần 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 đối tượng
dẫn xuất. Điều này hạn chế sự ảnh hưởng xấu của việc thay đổi dữ liệu đến
các đối tượng khác trong chương trình.
- 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
Bài giảng Kỹ thuật lập trình – Ngành Truyền thông đa phương tiện
7
Bộ môn Truyền thông đa phương tiện – Trường Đại học Công nghệ thông tin và truyền thông
cơ sở như những phương thức của chính nó, mà không cần thiết 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.2.2. Một số khái niệm cơ bản
Trong mục này, chúng ta sẽ làm quen với một số khái niệm cơ bản trong lập
trình hướng đối tượng. Bao gồm:
- Khái niệm đối tượng (object)
- Khái niệm đóng gói dữ liệu (encapsulation)
- Khái niệm kế thừa (inheritance)
- Khái niệm đa hình (polymorphism)
Đối tượng (Object)
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
Lớp (Class)
Khi có nhiều đối tượng giống nhau về mặt dữ liệu và phương thức, chúng được
nhóm lại với nhau và gọi chung là lớp:
- Lớp là sự trừu tượng hóa của đối tượng.
- Đối tượng là một thể hiện của lớp.
Đóng gói dữ liệu (Encapsulation)
- 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 đó.
Tuy nhiên, vì C++ chỉ là ngôn ngữ lập trình nửa đối tượng cho nên C++ vẫn
cho phép định nghĩa các biến dữ liệu và các hàm tự do, đây là 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.
Bài giảng Kỹ thuật lập trình – Ngành Truyền thông đa phương tiện
8
Bộ môn Truyền thông đa phương tiện – Trường Đại học Công nghệ thông tin và truyền thông
Kế thừa (Inheritance)
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 phạm vi lớp cơ sở mà không cần thay đổi
trong các lớp dẫn xuất.
Đa hình (Polymorphsim)
Đa hình là khái niệm luôn đi kèm với 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 đối tượng đó 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 đó.
Đó là tính đa hình.
1.2.3. Lập trình hướng đối tượng trong C++
Vì C++ là một ngôn ngữ lập trình được mở rộng từ một ngôn ngữ lập trình cấu
trúc C nên C++ được xem 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ừ khóa phạm vi: public, protected,
private.
- Cho phép kế thừa lớp với các kiểu kế thừa khác nhau tùy thuộc vào từ khóa
dẫn xuất.
- Cho phép lớp dẫn xuất sử dụng các phương thức của lớp cơ sở (trong phạm vi
quy định).
- Cho phép định nghĩa chồng phương thức trong lớp dẫn xuất.
Những hạn chế hướng đối tượng của C++
Bài giảng Kỹ thuật lập trình – Ngành Truyền thông đa phương tiện
9
Bộ môn Truyền thông đa phương tiện – Trường Đại học Công nghệ thông tin và truyền thông
Những hạn chế này là do C++ được phát triển từ một ngôn ngữ lập trình thuần
cấu trúc 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 trong C++.
1.3. Giao diện công cụ lập trình C++
Visual Studio 2010 là một môi trường biên dịch tích hợp của Microsoft. Nó là
trình biên dịch tốt nhất, hiện đại nhất trên hệ điều hành windows. Chúng ta có thể sử
dụng nó để biên dịch C++, C#, Visual Basic, J#... Ta sẽ tìm hiểu Visual Studio theo
hướng tiếp cận với C++. Một điều cần lưu ý, với phiên bản 2010 này, Visual Studio có
hai phiên bản dành cho C++: C++ for Net và C++ for Win32. Chúng ta chỉ tìm hiểu về
tính năng C++ for Win32. Trong nội dung của giáo trình này ta sẽ xây dựng các ứng
dụng Console trên nền Win32 mà không thảo luận thêm về Visual C++ for Net bởi vì
nó thuộc một phạm trù tương đối khác so với Visual C++ for Win32.
Khởi động Visual studio 2010:
Có thể thực hiện một trong 2 cách sau:
- Nhấp đôi chuột vào biểu tượng VS 2010 trên nền Desktop.
- Vào Start > All Programs > Microsoft VS 2010 > Chọn biểu tượng VS
2010.
Bài giảng Kỹ thuật lập trình – Ngành Truyền thông đa phương tiện
10
Bộ môn Truyền thông đa phương tiện – Trường Đại học Công nghệ thông tin và truyền thông
Tạo mới dự án trong VS 2010:
VS quản lý theo các workspace và các dự án. Trong VS, workspace được gọi là
Solution. Trong mỗi workspace có thể chứa nhiều dự án. Nếu chưa tạo một dự án
nào thì khi tạo mới một dự án workspace sẽ tự động được tạo. Để tạo một dự án
mới, ta vào File > New > Project (hoặc tổ hợp phím tắt Ctrl + Shift + N). Trong hộp
thoại xuất hiện chúng ta chọn Win32 Console Application.
- Mục name: nhập tên dự án
- Mục location: nhấp vào nút Browse để chọn vị trí lưu trữ. Mặc định, VS
sẽ lưu trữ dự án ở thư mục Documents.
- Mục Solution name: tạo một thư mục con trong thư mục dự án hay tạo
trực tiếp trong thư mục dự án.
Bài giảng Kỹ thuật lập trình – Ngành Truyền thông đa phương tiện
11
Bộ môn Truyền thông đa phương tiện – Trường Đại học Công nghệ thông tin và truyền thông
- Nhóm Application Type:
+ Windows application: tạo ứng dụng winform
+ Console application: tạo ứng dụng chạy trên DOS
+ Dll: tạo thư viện dll
+ Static library: tạo thư viện tĩnh
- Nhóm Add common header file:
+ Alt: tạo header từ lớp thư viện Alt
+ MFC: tạo header từ lớp thư viện MFC
- Nhóm Additional options:
+ Empty project: tạo dự án rỗng không có tập tin
+ Export symbols: xuất bản các biểu tượng
+ Precompiled header: tạo tập tin tiêu đề tiền biên dịch
Hãy chọn Console Application và chọn Empty Project, sau đó nhấp Finish
Tạo các tập tin trong dự án:
Trong cửa sổ Solution Explorer, chuột phải và chọn Add:
Bài giảng Kỹ thuật lập trình – Ngành Truyền thông đa phương tiện
12
Bộ môn Truyền thông đa phương tiện – Trường Đại học Công nghệ thông tin và truyền thông
- Nếu tập tin đã tồn tại, chọn Add Existing Items. Sau đó, duyệt đến vị trí tồn tại
của tệp tin.
- Nếu tệp chưa tồn tại, chọn Add New Items. Trong cửa sổ xuất hiện, tùy thuộc
vào tập tin mà chúng ta cần, hãy chọn loại tương ứng. Thông thường, trong dự
án của C++, chúng ta sử dụng 2 tập tin tiêu đề .h và thân chương trình .cpp.
Sau đó, nhập tên của tập tin và nhấp OK. Ngoài ra, nó có thể chứa các hàm
macro, các khai báo hằng và biến toàn cục được sử dụng trong toàn bộ chương
trình. Tập tin .cpp thường chứa phần thân của hàm hoặc lớp. Khi làm việc với
các dự án trong C++, chúng ta nên tách chương trình thành nhiều phần và nên
sử dụng các tệp tiêu đề để làm cho chương trình gọn gàng và dễ hiểu hơn.
Sau khi chọn được tập tin cần tạo, nhập tên tập tin, sau đó nhấp nút Add. Tập
tin mới sẽ được bổ sung vào dự án.
- Add Class: bổ sung các lớp đối tượng cho dự án. ở đây chúng ta chọn C++ class.
Bài giảng Kỹ thuật lập trình – Ngành Truyền thông đa phương tiện
13
Bộ môn Truyền thông đa phương tiện – Trường Đại học Công nghệ thông tin và truyền thông
Biên dịch dự án:
- Để biên dịch và thực thi một dự án: Debug > Start Debigging (hoặc Start
without Debugging)
- Để biên dịch toàn bộ dự án mà không thực thi dự án: Build > Build Solution
Một số phím tắt trong VS 2010:
1.4. Cấu trúc một chương trình C++
Mã chương trình Kết quả
[1.] // my first program
[2.] #include
[3.] using namespace std;
[4.] int main()
[5.] {
[6.] cout << “Hello, world!”;
[7.] return 0;
[8.] }
Hello, world!
Giải thích:
[1.] Các kí tự nằm sau dấu // sẽ không được biên dịch mà nó được hiểu là dòng chú
thích. Việc chú thích trên 1 dòng sẽ được đặt sau dấu //. Nếu muốn tạo chú
thích nhiều dòng, ta sử dụng dấu /* tạo chú thích ở đây */
Bài giảng Kỹ thuật lập trình – Ngành Truyền thông đa phương tiện
14
Bộ môn Truyền thông đa phương tiện – Trường Đại học Công nghệ thông tin và truyền thông
[2.] Dòng này bắt đầu bằng kí tự #include, tiếp đến là tên tệp tin tiêu đề chứa các
thư viện. Thư viện iostream chứa các hàm xuất nhập cơ bản, hàm này là một
phần của namespace std.
[3.] Trong C++, các thành phần của thư viện chuẩn được khai báo trong namespace.
Để có thể truy xuất đến các thành phần của nó, ta sử dụng từ khóa using. Trong
thư viện chuẩn của C++, đối tượng cout được tổ chức trong namespace std.
[4.] Bất kì một chương trình C++ nào cũng cần có một hàm main để thực thi
chương trình. Tên hàm là main, tiếp theo là cặp dấu ngoặc đơn dùng để chứa
tham số đính kèm. Thông thường một chương trình ứng dụng sẽ chứa 2 tham số
trong hàm main là int argc và char* args[]. Các tham số này gọi là tham số
dòng lệnh. Tiếp theo là dấu {}. Bên trong cặp dấu này là chương trình chính.
[5.] Dấu mở khối.
[6.] Xuất dữ liệu ra màn hình. Hàm printf vẫn hoạt động tốt trong trường hợp này.
Nếu dùng hàm printf thì không cần khai báo thư viện iostream và namespace
std ở trên. Khi sử dụng đối tượng cout, chúng ta có thể bỏ qua dòng lệnh [3.] và
thay vào đó ta sẽ viết std::cout. Khi sử dụng đối tượng cout, ta có thêm 1 cách
thức để xuống dòng thay vì dùng \n, đó là endln. Đối tượng cout thường đi với
toán tử xuất <<. Chúng ta có thể dùng nhiều toán tử này khi muốn xuất nhiều
phần tử riêng biệt: cout << string1 << string2 <<... << endln
[7.] Câu lệnh return dùng để trả về giá trị của hàm main. Nếu hàm là void thì không
cần return.
[8.] Dấu đóng khối.
Chú ý
- Cũng như C, C++ là ngôn ngữ phân biệt chữ hoa và chữ thường.
- Kết thúc 1 dòng lệnh trong C++ bao giờ cũng phải có dấu ;
1.5 Ngôn ngữ lập trình C++
1.5.1 Nhắc lại các kiến thức lập trình C
1.5.1.1 Biến, hằng và các kiểu dữ liệu
a. Từ khóa
Từ khóa trong C++ có thể có 1 hoặc nhiều từ. Nếu từ khóa có nhiều từ thì giữa các
từ có dấu gạch chân _. Kí tự trắng và kí tự đặc biệt không được phép sử dụng trong từ
khóa, tên hàm, tên biến. Tên của chúng không được bắt đầu bằng kí tự số.
Bảng từ khóa chuẩn trong C++
Bài giảng Kỹ thuật lập trình – Ngành Truyền thông đa phương tiện
15
Bộ môn Truyền thông đa phương tiện – Trường Đại học Công nghệ thông tin và truyền thông
asm, auto, bool, break, case, catch, char, class, const, const_cast, continue, default,
do, double, dynamic_cast, else, enum, explicit, export, extern, false, float, for, friend,
goto, if, inline, int, long, mutable, namespace, new, operator, private, protected,
public, register, reinterpret_cast, return, short, signed, sizeof, static, static_cast,
struct, switch, template, this, throw, true, try, typedef, typeid, typename, union,
unsigned, using, virtual, void, volatile, wchar_t, while
b. Biến
- Biến dùng để lưu giá trị và nó có thể thay đổi được. Một biến sẽ được quy định
bởi 1 kiểu dữ liệu nào đó. Kiểu dữ liệu thường có 2 loại: kiểu dữ liệu nguyên
thủy và kiểu dữ liệu do người dùng tự định nghĩa.
- Khai báo biến: ;
Vd: int a;
float mynumber;
bool istrue;
long num1, num2, num3;
- Khởi tạo giá trị cho biến:
C1: type tên_biến = giá_trị_khởi_tạo;
Vd: int a = 0;
C2: type tên_biến (giá_trị_khởi_tạo);
Vd: int a (0);
- Các kiểu dữ liệu tham chiếu khác (ngoài kiểu dữ liệu ở trên và kiểu string)
không thể sử dụng hai cách khởi tạo này.
c. Hằng
Khái niệm
- Hằng là một phần tử có giá trị cố định, giá trị của nó được khởi tạo ngay khi hằng
được tạo ra.
- Thường sử dụng các chữ cái để đặt tên cho hằng. Tên hằng không chứa các kí tự
đặc biệt, kí tự trắng hay bắt đầu bằng số, không được trùng với từ khóa.
Định nghĩa một hằng
#define tên_hằng giá_trị
Vd: #define PI 3.14
Ví dụ chương trình tính diện tích hình tròn:
#include
using namespace std;
#define PI 3.14
Bài giảng Kỹ thuật lập trình – Ngành Truyền thông đa phương tiện
16
Bộ môn Truyền thông đa phương tiện – Trường Đại học Công nghệ thông tin và truyền thông
int main(){
int r=1;
float s;
s=PI*r*r;
cout<<s;
system("pause");
}
Khai báo hằng
#const kiểu_DL tên_hằng = giá_trị;
Vd: const int a = 10;
const char x = ‘\t’;
Khai báo hằng tương tự như khai báo biến, chỉ có duy nhất một điểm khác biệt là
thêm từ khóa const vào trước khai báo này. Hằng và biến cũng tượng tự nhau, chỉ khác
một điểm duy nhất là giá trị của hằng không thể thay đổi còn biến thì có thể thay đổi.
d. Kiểu dữ liệu nguyên thủy
- Khi lập trình, chúng ta lưu các biến trong bộ nhớ máy tính, nhưng máy tính cần
phải biết loại mà chúng ta muốn lưu. Điều này giúp đảm bảo đủ số lượng ô nhớ
cần thiết để lưu dữ liệu.
- Trong máy tính, bộ nhớ được tổ chức theo các byte. Một byte là một đơn vị đo
lường tối thiểu mà chúng ta có thể quản lý trong C++.
Tên Mô tả Kích thước Vùng giá trị
Char Kí tự hoặc số nguyên bé 1 byte
Signed: -128 -> 127
Unsigned: 0 -> 255
Short Số nguyên ngắn 2 byte
Signed: - -> - 1
Unsigned: 0 -> - 1
Int Số nguyên 4 byte
Signed: - -> - 1
Unsigned: 0 -> - 1
Long Số nguyên dài 4 byte
Signed: - -> - 1
Unsigned: 0 -> - 1
Long long Số nguyên cực dài 8 byte Signed: - -> - 1
Bài giảng Kỹ thuật lập trình – Ngành Truyền thông đa phương tiện
17
Bộ môn Truyền thông đa phương tiện – Trường Đại học Công nghệ thông tin và truyền thông
Unsigned: 0 -> - 1
Bool Giá trị logic – true/false 1 byte True và False
Float Số thập phân 4 byte 7 số thập phân
double Số thập phân chấm động 8 byte 15 số thập phân
Long double
Số thập phân chấm động
dài
8 byte 15 số thập phân
Wchar_t Kí tự dài 2/4 byte
Chú ý
- Khi khai báo biến thuộc kiểu nguyên mà ta không sử dụng khai báo có dấu
(signed) hoặc không dấu (unsigned) thì chương trình mặc định sẽ quy định là
kiểu nguyên có dấu.
Int num; // tương đương với singed int num;
- Để kiểm tra kích thước của kiểu dữ liệu, ta sử dụng hàm sizeof(tên biến) hoặc
sizeof(kiểu dữ liệu).
1.5.1.2 Toán tử
Ngôn ngữ lập trình C++ cung cấp cho ta khá đầy đủ các toán tử, các toán tử đó bao
gồm:
- Toán tử gán (=):
Vd: a = b + 2;
a = a +1;
a = b= c= 5;
- Toán tử số học: +, -, *, /, %
- Toán tử gán hợp nhất: +=, -=, *=, /=, %=, &=, |=
- Toán tử tăng và giảm: a++; ++a; a--; --a;
- Toán tử so sánh: ==, !=, >, =, <=
- Toán tử logic: !, &&, ||
- Toán tử điều kiện:
(bt_điều_kiện)?(kết_quả_1):(kết_quả_2);
- Toán tử chuyển đổi kiểu dữ liệu: (kiểu_dữ_liệu)biến;
Thứ tự ưu tiên các toán tử:
Bài giảng Kỹ thuật lập trình – Ngành Truyền thông đa phương tiện
18
Bộ môn Truyền thông đa phương tiện – Trường Đại học Công nghệ thông tin và truyền thông
Mức ưu tiên Toán tử
Toán tử tăng, giảm ++, --,
Toán tử số học *, /, %, +, -
Toán tử so sánh =, >, ==, !=
Toán tử logic &&, ||, !
Toán tử gán =, *=, /=, %=, +=, -=, &=, |=
1.5.1.3 Các cấu trúc lệnh điều khiển
Một chương trình khi thực thi, nó không chỉ đơn thuần là một dãy các câu lệnh
tuần tự. Trong quá trình xử lý, nó có thể kiểm tra điều kiện rồi thực thi đoạn mã hoặc
lặp đi lặp lại một đoạn mã nào đó... với mục đích đó, C++ cung cấp cho ta các cấu trúc
điều khiển.
a. Cấu trúc rẽ nhánh
Cấu trúc lệnh điều kiện if
If (biểu_thức_điều_kiện_đúng)
{
Các_lệnh;
}
Giải thích: kiểm tra giá trị của biểu thức điều kiện, nếu đúng thì các lệnh bên trong sẽ
được thực hiện, ngược lại sẽ không được thực hiện.
Trường hợp biểu_thức_điều_kiện sai, cần thực thi một mệnh đề khác. Ta sẽ sử dụng từ
khóa else:
If (biểu_thức_điều_kiện_đúng)
{
...
}else
{
...
}
Cấu trúc if có thể lồng nhau, khi đó ta có cấu trúc lệnh phức hợp.
Cấu trúc lựa chọn: switch
switch (biểu_thức){
case hằng_1:
nhóm_các_lệnh;
break;
Bài giảng Kỹ thuật lập trình – Ngành Truyền thông đa phương tiện
19
Bộ môn Truyền thông đa phương tiện – Trường Đại học Công nghệ thông tin và truyền thông
case hằng_2:
nhóm_các_lệnh;
break;
...
default:
nhóm_các_lệnh;
}
Giải thích: kiểm tra giá trị của biểu thức, nếu giá trị của biểu thức rơi vào danh sách
hằng, thì nó sẽ thực hiện các lệnh trong từng trường hợp case tương ứng. Nếu không
thuộc vào danh sách hằng thì nó sẽ thực hiện lệnh trong trường hợp default.
b. Cấu trúc lặp
Vòng lặp while:
While (biểu_thức_điều_kiện_đúng)
{
...
}
Giải thích: nếu biểu thức điều kiện đúng, các lệnh bên trong vòng lặp sẽ được thực
hiện cho đến khi nó nhận giá trị sai.
Ví dụ:
Ví dụ Kết quả
int main(){
int n;
cout<<"nhap n:";
cin>>n;
while (n>0){
cout<<n<<"\n";
n--;
}
system("pause");
}
Nhập n:5
5
4
3
2
1
Vòng lặp do ... while:
do
{
....
} while (biểu_thức_điều_kiện_đúng);
Giải thích: thực hiện các lệnh trong vòng lặp sau đó kiểm tra biểu thức điều kiện. Nếu
biểu thức điều kiện còn đúng thì tiếp tục lặp.
Vòng lặp for:
Bài giảng Kỹ thuật lập trình – Ngành Truyền thông đa phương tiện
20
Bộ môn Truyền thông đa phương tiện – Trường Đại học Công nghệ thông tin và truyền thông
For (biểu_thức_khởi_tạo; biểu_thức_giới_hạn; biểu_thức_tăng_giảm;)
{
....
}
Giải thích: thực hiện vòng lặp, với số vòng lặp từ biểu thức khởi tạo đến biểu thức
giới hạn theo mức tăng là biểu thức tăng giảm.
c. Các câu lệnh nhảy
Câu lệnh break
Để thoát ra khỏi vòng lặp. Thông thường khi ta sử dụng các vòng lặp không xác
định được số lần lặp, để tránh lặp vô hạn ta thường sử dụng câu lệnh break để thoát
khỏi vòng lặp.
Ví dụ Kết quả
#include
using namespace std;
int main ()
{
int n;
for (n=10; n>0; n--) {
cout << n << ", ";
if (n==3)
{
cout << "countdown aborted!";
break;
}
}
system("pause");
}
10, 9, 8, 7, 6, 5, 4, countdown aborted!
Câu lệnh continue
Thường được dùng trong các vòng lặp. Khi continue được gọi, bước lặp hiện tại sẽ
bỏ qua và tiến hành bước lặp tiếp theo.
Ví dụ Kết quả
#include
using namespace std;
int main ()
{
for (int n=10; n>0; n--) {
if (n==5) continue;
cout << n << ", ";
}
cout << "FIRE!";
system("pause");
}
10, 9, 8, 7, 6, 4, 3, 2, 1, FIRE!
Bài giảng Kỹ thuật lập trình – Ngành Truyền thông đa phương tiện
21
Bộ môn Truyền thông đa phương tiện – Trường Đại học Công nghệ thông tin và truyền thông
Câu lệnh goto
Cho phép tạo ra một bước nhảy đến 1 nhãn được ấn định sẵn. Tên nhãn sẽ được đặt
như sau tên_nhãn: . ta nên hạn chế tối đa việc sử dụng lệnh goto vì nó thường làm
phá vỡ cấu trúc của lập trình hiện đại.
Ví dụ Kết quả
#include
using namespace std;
int main ()
{
int n=10;
loop: ;
cout << n << ", ";
n--;
if (n>0) goto loop;
cout << "FIRE!";
system("pause");
}
10, 9, 8, 7, 6, 5, 4, 3, 2, 1,FIRE!
Tóm tắt
Tên lệnh Cách dùng
If ... else Khi cần kiểm tra một hoặc vài điều kiện mang tính chất logic
Switch
Khi cần kiểm tra điều kiện hoặc phép tính thuộc vào của một biến
số/biểu thức trong một danh sách hằng tương ứng.
For Lặp có số vòng lặp xác định
While
Cần kiểm tra điều kiện lặp trước khi thực hiện lệnh, lặp không xác
định số vòng lặp
Do... while
Kiểm tra điều kiện lặp sau khi thực hiện lệnh, lặp không xác định số
vòng lặp
Break Cần thoát khỏi vòng lặp
Continue Bỏ qua vòng lặp hiện tại, thực thi bước lặp tiếp theo
Goto
Nhảy đến 1 nhãn được chỉ định. Nên tránh sử dụng, chỉ sử dụng
trong những trường hợp thực sự cần thiết.
1.5.1.4 Hàm
Là một khối lệnh được thực hiện khi nó được gọi từ một điểm khác của chương trình.
a. Khai báo hàm
Kiểu_dữ_liệu tên_hàm (danh_sách_tham_số)
{
Thân hàm;
Bài giảng Kỹ thuật lập trình – Ngành Truyền thông đa phương tiện
22
Bộ môn Truyền thông đa phương tiện – Trường Đại học Công nghệ thông tin và truyền thông
}
Hàm không trả về giá trị - hàm void
b. Tham biến và tham trị
Tham trị: khi gọi hàm, các giá trị từ các đối số truyền vào trong hàm sẽ được sao
chép sang các tham số này, các tham số đó chỉ đóng vai trò là các tham số hình
thức, chúng không lưu lại giá trị cho các đối số truyền vào, mà các giá trị đó đã bị
các lệnh trong hàm làm thay đổi.
Ví dụ Kết quả
#include
#include
using namespace std;
void setNum(int a)
{
a=0;
}
void main() {
int b=1;
setNum(b);
cout<<b;
system("pause");
}
1
Tham biến: bổ sung thêm & ở trước tên tham số đó. Bằng cách này, các biến là đối
số trong lời gọi hàm sẽ bị làm thay đổi giá trị sau khi kết thúc lời gọi hàm. Kiểu
khai báo tham biến sử dụng dấu và (&) chỉ có trong C++, trong ngôn ngữ C chúng
ta phải sử dụng con trỏ để làm việc tương tự như thế.
Ví dụ Kết quả
#include
#include
using namespace std;
void setNum(int &a)
{
a=0;
}
void main() {
int b=1;
setNum(b);
0
Bài giảng Kỹ thuật lập trình – Ngành Truyền thông đa phương tiện
23
Bộ môn Truyền thông đa phương tiện – Trường Đại học Công nghệ thông tin và truyền thông
cout<<b;
system("pause");
}
Giá trị mặc định của tham số hình thức: nếu các tham số được gán giá trị mặc định,
thì khi gọi hàm, chúng ta sẽ có một vài cách gọi tương ứng với số lượng khác nhau
của các tham số.
Ví dụ Kết quả
#include
using namespace std;
int sum(int a, int b=0, int c=0)
{
return a+ b+ c;
}
void main() {
cout<< sum(1)<<"\n";
cout<< sum(1,2)<<"\n";
cout<< sum(1,2,3)<<"\n";
system("pause");
}
1
3
6
c. Hàm nội tuyến
Hàm nội tuyến: với hàm nội tuyến, trình biên dịch sẽ khởi tạo 1 thân hàm và chèn
nó vào vị trí được gọi tại mỗi thời điểm mà hàm đó được gọi, thay vì nó chỉ chèn lời
gọi hàm. Việc này cải thiện đáng kể tốc độ biên dịch chương trình. Trong hầu hết các
trình biên dịch hiện đại, việc quy định hàm inline là không cần thiết. Chỉ định inline
chỉ có tác dụng định hướng cho chương trình dịch.
Inline type tên_hàm(danh_sách_tham_số)
{
Thân hàm;
}
Bài giảng Kỹ thuật lập trình – Ngành Truyền thông đa phương tiện
24
Bộ môn Truyền thông đa phương tiện – Trường Đại học Công nghệ thông tin và truyền thông
1.5.1.5 Các kiểu dữ liệu có cấu trúc
a. Mảng
Mảng là một dãy các phần tử có cùng kiểu được đặt liên tiếp trong bộ nhớ và có thể
truy xuất đến từng phần tử bằng cách thêm một chỉ số vào sau tên của mảng. Điều này
có ưu điểm là chúng ta có thể khai báo nhiều biến cùng kiểu giá trị một lúc mà không
cần khai báo riêng biệt.
Mảng 1 chiều:
- Khai báo: kiểu_dữ_liệu tên_mảng[số_phần_tử];
Vd: string human[5];
int number[10];
- Khởi tạo mảng:
String human[5] = {“Lan”, “Nam”, “Binh”, “Hoa”};
Ngoài ra, có thể khai báo với số lượng phần tử chưa xác định như sau:
int y[] = {7, 8, 9, 10};
- Truy xuất tới các phần tử mảng
tenMang[chiSo];
Mảng 2 chiều: lưu trữ theo thứ tự dòng – cột.
- Khai báo: kiểu_dữ_liệu tên_mảng[số_hàng][số_cột];
Ví dụ: int a[3][4];
- Khởi tạo mảng:
Int a[2][3] = {{1, 2, 3}, {4, 5, 6}};
- Truy xuất tới phần tử mảng
tenMang[chiSoHang][chiSoCot];
b. Xâu kí tự
C++ chứa một lớp string rất mạnh mẽ mà nó có thể hữu dụng trong việc thực thi
các tác vụ xử lý xâu. Tuy nhiên, bởi vì xâu là 1 mảng các kí tự nên có thể xử lý xâu
như xử lý trên mảng.
Khai báo: char jenny[20];
Kích thước cực đại này không cần phải luôn luôn dùng đến. Ví dụ, jenny có thể lưu
xâu "Hello" hay "Merry christmas". Vì các mảng kí tự có thể lưu các xâu kí tự ngắn
hơn độ dài của nó, trong C++ đã có một quy ước để kết thúc một nội dung của một xâu
kí tự bằng một kí tự null, có thể được viết là '\0'.
Bài giảng Kỹ thuật lập trình – Ngành Truyền thông đa phương tiện
25
Bộ môn Truyền thông đa phương tiện – Trường Đại học Công nghệ thông tin và truyền thông
Chúng ta có thể biểu diễn jenny (một mảng có 20 phần tử kiểu char) khi lưu trữ
xâu kí tự "Hello" và "Merry Christmas" theo cách sau:
Chú ý rằng sau nội dung của xâu, một kí tự null ('\0') được dùng để báo hiệu kết
thúc xâu. Những ô màu xám biểu diễn những giá trị không xác định.
Khởi tạo: có thể khởi tạo xâu mystring theo một trong hai cách sau đây:
char mystring [] = { 'H', 'e', 'l', 'l', 'o', '\0' };
char mystring [] = "Hello";
Việc gán sử dụng dấu ngoặc kép (") chỉ hợp lệ khi khởi tạo mảng, tức là lúc khai
báo mảng. Các biểu thức trong chương trình như:
mystring = "Hello";
mystring[] = "Hello";
là không hợp lệ, cả câu lệnh dưới đây cũng vậy:
mystring = { 'H', 'e', 'l', 'l', 'o', '\0' };
Gán giá trị cho xâu kí tự
mystring[0] = 'H';
mystring[1] = 'e';
mystring[2] = 'l';
mystring[3] = 'l';
mystring[4] = 'o';
mystring[5] = '\0';
c. Kiểu cấu trúc
Kiểu dữ liệu mảng mà chúng ta đã thảo luận ở trên chỉ giúp chúng ta lưu một tập
dữ liệu cùng loại. Nếu chúng ta không đơn thuần lưu trữ cùng loại dữ liệu, mà có thể là
nhiều dữ liệu khác nhau thì mảng không thể giải quyết được vấn đề. Trong C++ (và cả
C) cung cấp cho chúng ta một kiểu dữ liệu giúp ta giải quyết vấn đề này, đó là struct.
Struct
Được hiểu như một tập hợp kiểu dữ liệu, có tác dụng gộp các thông tin có liên quan
với nhau. Các thành phần của struct có quan hệ logic với nhau. Struct là kiểu dữ liệu
do người dùng tự định nghĩa.
Khai báo:
Struct tên_struct{
Type thành_viên_1;
Bài giảng Kỹ thuật lập trình – Ngành Truyền thông đa phương tiện
26
Bộ môn Truyền thông đa phương tiện – Trường Đại học Công nghệ thông tin và truyền thông
Type thành_viên_2;
.......
Type thành_viên_n;
}[tên_đối_tượng_struct];
Ví dụ:2 cách khai báo sau là tương đương:
struct sinhvien{
char hoten[30];
char lop[10];
float dtb;
} sv1, sv2;
struct sinhvien{
char hoten[30];
char lop[10];
float dtb;
};
sinhvien sv1, sv2;
Truy xuất đến các thành viên của struct:
Tên_biến.phần_tử_thành_viên
Ví dụ: sv1.hoten = “Nguyen Van A”;
Sv1.lop = “TTDPT K11A”;
Sv1.dtb = 3.0;
Mảng cấu trúc:
Khai báo: tên_struct tên_mảng[số_phần_tử];
Ví dụ: sinhvien sv[30];
Kích thước bộ nhớ của struct
- Theo lý thuyết: là tổng kích thước của các dữ liệu thành viên.
- Thực tế: dựa vào cách thức tổ chức của bộ nhớ.
Ví dụ:
struct sinhvien{
char ht;
int diem;
char lop;
}sv1;
...
cout <<sizeof(sv1);
// 12
struct sinhvien{
char ht;
char lop;
int diem;
}sv1;
...
cout <<sizeof(sv1);
//8
Struct lồng nhau
Khai báo lồng bên trong Khai báo tuần tự
Bài giảng Kỹ thuật lập trình – Ngành Truyền thông đa phương tiện
27
Bộ môn Truyền thông đa phương tiện – Trường Đại học Công nghệ thông tin và truyền thông
struct sinhvien{
char ht;
char lop;
int diem;
struct date{
int ngay;
int thang;
int nam;
}namsinh;
}sv1;
struct date{
int ngay;
int thang;
int nam;
};
struct sinhvien{
char ht;
char lop;
int diem;
date namsinh;
}sv1;
Cách truy xuất đến các biến thành viên tương tự như trên:
sv1.namsinh.ngay
1.5.1.6 Con trỏ
- Bộ nhớ của máy tính được tổ chức theo các ô nhớ - là các bytes. Mỗi ô nhớ đánh
dấu một cách liên tục, theo cách tổ chức này, mỗi ô nhớ có một địa chỉ định danh
duy nhất.
- Khi mô tả một biến, hệ điều hành sẽ cung cấp 1 số lượng ô nhớ để lưu trữ giá trị
của biến. Ta không quyết định một cách trực tiếp vị trí chính xác để lưu trữ biến
bên trong mảng các ô nhớ đó, mà tác vụ này hoàn toàn tự động. Trong một vài
trường hợp, ta quan tâm đến địa chỉ mà các biến được lưu trữ.
a. Khai báo và khởi tạo biến con trỏ
- Biến con trỏ là biến có chứa địa chỉ của một vùng trong bộ nhớ và có kiểu xác
định.
- Khai báo bằng cách thêm dấu * trước tên biến:
Kiểu_dữ_liệu *tên_con_trỏ;
Vd: int * pint;
char *pchar;
float *pfloat;
Toán tử lấy địa chỉ (&)
- Vào thời điểm mà chúng ta khai báo một biến thì nó phải được lưu trữ trong một
vị trí cụ thể trong bộ nhớ. Nói chung chúng ta không quyết định nơi nào biến đó
được đặt - thật may mắn rằng điều đó đã được làm tự động bởi trình biên dịch và
hệ điều hành, nhưng một khi hệ điều hành đã gán một địa chỉ cho biến thì chúng
ta có thể muốn biết biến đó được lưu trữ ở đâu.
Bài giảng Kỹ thuật lập trình – Ngành Truyền thông đa phương tiện
28
Bộ môn Truyền thông đa phương tiện – Trường Đại học Công nghệ thông tin và truyền thông
- Điều này có thể được thực hiện bằng cách đặt trước tên biến một dấu và (&), có
nghĩa là "địa chỉ của".
- Toán tử & là toán tử một ngôi, trả về địa chỉ bộ nhớ của toán hạng đó.
Tên_con_trỏ = &Tên_biến;
- Ví dụ:
ted = &andy;
sẽ gán cho biến ted địa chỉ của biến andy, vì khi đặt trước tên biến andy dấu và
(&) chúng ta không còn nói đến nội dung của biến đó mà chỉ nói đến địa chỉ của
nó trong bộ nhớ.
Toán tử tham chiếu (*)
- Cách lấy giá trị của con trỏ đang trỏ tới ô nhớ: sử dụng ký tự *
- Bằng cách sử dụng con trỏ chúng ta có thể truy xuất trực tiếp đến giá trị được lưu
trữ trong biến được trỏ bởi nó bằng cách đặt trước tên biến con trỏ một dấu sao
(*) - ở đây có thể được dịch là "giá trị được trỏ bởi".
- Là toán tử một ngôi, trả về giá trị tại địa chỉ con trỏ trỏ đến. *Tên_con_trỏ
a = *p;
Ví dụ minh họa:
char c = 'A';
int *pInt;
short s = 50;
int a = 10;
pInt = &a;
*pInt = 100;
Địa chỉ các biến trong bộ nhớ theo
thứ tự tăng dần ở đây chỉ có tính
chất minh hoạ. Trong thực tế,
stack được cấp phát từ cao xuống
thấp => biến khai báo sau sẽ có
địa chỉ nhỏ hơn.
Con trỏ NULL:
- Là 1 hằng con trỏ chứa địa chỉ 0, mang ý nghĩa đặc biệt là không trỏ tới địa chỉ
nào trong bộ nhớ.
- Không được gán giá trị cho con trỏ NULL:
Vd: int *p = NULL;
*p = 100; // lỗi
Bài giảng Kỹ thuật lập trình – Ngành Truyền thông đa phương tiện
29
Bộ môn Truyền thông đa phương tiện – Trường Đại học Công nghệ thông tin và truyền thông
- Cần phân biệt con trỏ NULL và con trỏ chưa được khởi tạo (trỏ đến địa chỉ ngẫu
nhiên).
- Con trỏ NULL thường được dùng để xác định tính hợp lệ của một biến con trỏ
=> để tránh lỗi, luôn gán con trỏ bằng NULL khi chưa hoặc tạm thời không được
dùng tới.
Khởi tạo con trỏ
- Khi khai báo con trỏ có thể chúng ta sẽ muốn chỉ định rõ ràng chúng sẽ trỏ tới
biến nào,
int number;
int *tommy = &number;
là tương đương với:
int number;
int *tommy;
tommy = &number;
- Trong một phép gán con trỏ chúng ta phải luôn luôn gán địa chỉ mà nó trỏ tới chứ
không phải là giá trị mà nó trỏ tới. Bạn cần phải nhớ rằng khi khai báo một biến
con trỏ, dấu sao (*) được dùng để chỉ ra nó là một con trỏ, và hoàn toàn khác với
toán tử tham chiếu. Đó là hai toán tử khác nhau mặc dù chúng được viết với cùng
một dấu. Vì vậy, các câu lệnh sau là không hợp lệ:
int number;
int *tommy;
*tommy = &number;
b. Các phép toán số học trên con trỏ
Vd: char *pchar;
pchar ++; ++pchar;
pchar--; --pchar;
Biến con trỏ có thể được dùng trong biểu thức toán học.
Vd: y = *p1 * *p2;
sum = sum + *p1;
z = 5* (*p1/ *p2);
c. Con trỏ, mảng và xâu kí tự
Con trỏ và mảng liên kết với nhau rất chặt chẽ. Trong thực tế, tên của một mảng
tương đương với địa chỉ phần tử đầu tiên của nó, giống như một con trỏ tương đương
với địa chỉ của phần tử đầu tiên mà nó trỏ tới, vì vậy thực tế chúng hoàn toàn như
nhau. Ví dụ, cho hai khai báo sau:
Bài giảng Kỹ thuật lập trình – Ngành Truyền thông đa phương tiện
30
Bộ môn Truyền thông đa phương tiện – Trường Đại học Công nghệ thông tin và truyền thông
int numbers [20];
int * p;
lệnh sau sẽ hợp lệ:
p = numbers;
Ở đây p và numbers là tương đương và chúng có cũng thuộc tính, sự khác biệt duy
nhất là chúng ta có thể gán một giá trị khác cho con trỏ p trong khi numbers luôn trỏ
đến phần tử đầu tiên trong số 20 phần tử kiểu int mà nó được định nghĩa với. Vì vậy,
không giống như p - đó là một biến con trỏ bình thường, numbers là một con trỏ
hằng. Lệnh gán sau đây là không hợp lệ:
numbers = p;
bởi vì numbers là một mảng (con trỏ hằng) và không có giá trị nào có thể được gán
cho các hằng.
Để hiểu rõ mối quan hệ giữa chúng, ta xem đoạn chương trình sau:
int x[10], *p;
p = x; // biến p được ấn định là địa chỉ của phần tử đầu tiên của mảng x[0].
Truy xuất đến các phần tử của mảng thông qua chỉ số hoặc con trỏ. Vd: x[5] hoặc
*(p+5). Lệnh p = &x[0] tương đương với p =x;
Ví dụ 1:Viết chương trình sử dụng con trỏ để sắp xếp dãy theo thứ tự tăng dần.
using namespace std;
const int MAX = 10;
int a[MAX], *p, i, j;
void main() {
int temp;
cout << "Nhap mang:";
for (i =0; i<MAX; i++)
cin >> a[i];
p = a;
for (i =0; i<MAX-1; i++)
for (j =i+1; j<MAX; j++)
if (*(p+i) > *(p +j))
{
temp = *(p+i);
*(p+i) = *(p+j);
*(p+j) = temp;
}
cout <<"Mang sau khi sap xep:";
for (i =0; i<MAX; i++)
cout <<a[i] << " ";
system("pause");
Bài giảng Kỹ thuật lập trình – Ngành Truyền thông đa phương tiện
31
Bộ môn Truyền thông đa phương tiện – Trường Đại học Công nghệ thông tin và truyền thông
}
Ví dụ 2: Sử dụng mảng con trỏ xâu kí tự
using namespace std;
char *day[7] = {"Monday","Tuesday","Wednesday","Thusday","Friday","Saturday","Sunday"};
void main() {
for(int i=0; i<7; i++)
cout <<day[i] << " \n";
system("pause");
}
Nếu không sử dụng biến con trỏ ta phải khai báo như sau:
char day[7][12] = {"Monday","Tuesday","Wednesday","Thusday","Friday","Saturday","Sunday"};
!Mảng xâu kí tự có thể được khai báo theo kiểu truyền thống hoặc theo cấu trúc con
trỏ.
char xau[20]; // khai báo truyền thống
char *xau; // khai báo con trỏ
string xau; // khai báo xâu
d. Con trỏ trỏ tới con trỏ
C++ cho phép sử dụng con trỏ đa tầng, nghĩa là con trỏ trỏ vào con trỏ. Để khai
báo con trỏ loại này, chúng ta chỉ cần bổ sung thêm vào biến trỏ một toán tử *.
float x = 1.5;
float *pX = &x;
float **ppX = &pX;
cout<< **ppX; /* in ra giá trị 1.5 *
**ppX = 2.3;
e. Con trỏ cấu trúc
Tương ứng với mảng, ta cũng có con trỏ trỏ vào struct. Với con trỏ trỏ vào struct,
ta có thể tạo ra một mảng struct động.
Khai báo: có 2 cách
struct sinhvien{
char hoten[30];
char lop;
float dtb;
struct sinhvien{
char hoten[30];
char lop;
float dtb;
Bài giảng Kỹ thuật lập trình – Ngành Truyền thông đa phương tiện
32
Bộ môn Truyền thông đa phương tiện – Trường Đại học Công nghệ thông tin và truyền thông
} *sv1, sv2;
};
sinhvien *sv1, sv2;
Tham chiếu: tương tự như con trỏ trỏ vào kiểu dữ liệu nguyên thủy.
sv1=&sv2;
- Truy xuất giá trị của thành viên:
sv1->hoten hoặc (*sv1).hoten
sv1->dtb hoặc (*sv1).dtb
f. Quản lý bộ nhớ cấp phát động
Khi tiến hành chạy chương trình, chương trình dịch sẽ bố trí các ô nhớ cụ thể
cho các biến được khai báo trong chương trình. Vị trí cũng như số lượng các ô nhớ này
tồn tại và cố định trong suốt thời gian chạy chương trình, chúng xem như đã bị chiếm
dụng và sẽ không được sử dụng vào mục đích khác và chỉ được giải phóng sau khi
chấm dứt chương trình. Việc phân bổ bộ nhớ như vậy được gọi là cấp phát tĩnh (vì
được cấp sẵn trước khi chạy chương trình và không thể thay đổi tăng, giảm kích thước
hoặc vị trí trong suốt quá trình chạy chương trình). Ví dụ nếu ta khai báo một mảng
nguyên chứa 1000 số thì trong bộ nhớ sẽ có một vùng nhớ liên tục 4000 bytes để chứa
dữ liệu của mảng này. Khi đó dù trong chương trình ta chỉ nhập vào mảng và làm việc
với một vài số thì phần mảng rỗi còn lại vẫn không được sử dụng vào việc khác. Đây
là hạn chế thứ nhất của kiểu mảng. Ở một hướng khác, một lần nào đó chạy chương
trình ta lại cần làm việc với hơn 1000 số nguyên. Khi đó vùng nhớ mà chương trình
dịch đã dành cho mảng là không đủ để sử dụng. Đây chính là hạn chế thứ hai của
mảng được khai báo trước.
Khắc phục các hạn chế trên của kiểu mảng, bây giờ chúng ta sẽ không khai
báo (bố trí) trước mảng dữ liệu với kích thước cố định như vậy. Kích thước cụ thể sẽ
được cấp phát trong quá trình chạy chương trình theo đúng yêu cầu của NSD. Nhờ
vậy chúng ta có đủ số ô nhớ để làm việc mà vẫn tiết kiệm được bộ nhớ, và khi không
dùng nữa ta có thể thu hồi (còn gọi là giải phóng) số ô nhớ này để chương trình sử
dụng vào việc khác. Hai công việc cấp phát và thu hồi này được thực hiện thông qua
các toán tử new, delete và con trỏ p. Thông qua p ta có thể làm việc với bất kỳ địa chỉ
nào của vùng được cấp phát. Cách thức bố trí bộ nhớ như thế này được gọi là cấp phát
động.
Toán tử new: để cấp phát bộ nhớ
Vd: int *num = new int;
int *bobby = new int[5];
Bài giảng Kỹ thuật lập trình – Ngành Truyền thông đa phương tiện
33
Bộ môn Truyền thông đa phương tiện – Trường Đại học Công nghệ thông tin và truyền thông
Biểu thức đầu tiên được dùng để cấp phát bộ nhớ chứa một phần tử có kiểu int.
Lệnh thứ hai được dùng để cấp phát một khối nhớ (một mảng) gồm các phần tử kiểu
int. Trong trường hợp này, hệ điều hành dành chỗ cho 5 phần tử kiểu int trong bộ nhớ
và trả về một con trỏ trỏ đến đầu của khối nhớ. Vì vậy lúc này bobby trỏ đến một
khối nhớ hợp lệ gồm 5 phần tử int.
Bạn có thể hỏi tôi là có gì khác nhau giữa việc khai báo một mảng với việc cấp phát
bộ nhớ cho một con trỏ như chúng ta vừa làm. Điều quan trọng nhất là kích thước của
một mảng phải là một hằng, điều này giới hạn kích thước của mảng đến kích thước mà
chúng ta chọn khi thiết kế chương trình trong khi đó cấp phát bộ nhớ động cho phép
cấp phát bộ nhớ trong quá trình chạy với kích thước bất kì.
Bộ nhớ động nói chung được quản lí bởi hệ điều hành và trong các môi trường đa
nhiệm có thể chạy một lúc vài chương trình có một khả năng có thể xảy ra là hết bộ
nhớ để cấp phát. Nếu điều này xảy ra và hệ điều hành không thể cấp phát bộ nhớ như
chúng ta yêu cầu với toán tử new, một con trỏ null (zero) sẽ được trả về. Vì vậy các
bạn nên kiểm tra xem con trỏ trả về bởi toán tử new có bằng null hay không:
int * bobby;
bobby = new int [5];
if (bobby == NULL) {
// error assigning memory. Take measures.
};
Toán tử delete: để giải phóng bộ nhớ
Vì bộ nhớ động chỉ cần thiết trong một khoảng thời gian nhất định, khi nó không
cần dùng đến nữa thì nó sẽ được giải phóng để có thể cấp phát cho các nhu cầu khác
trong tương lai. Để thực hiện việc này ta dùng toán tử delete, dạng thức của nó như
sau:
Vd: delete num;
delete[] nums;
Biểu thức đầu tiên được dùng để giải phóng bộ nhớ được cấp phát cho một phần
tử và lệnh thứ hai dùng để giải phóng một khối nhớ gồm nhiều phần tử (mảng). Trong
hầu hết các trình dịch cả hai biểu thức là tương đương mặc dù chúng là rõ ràng là hai
toán tử khác nhau.
Ví dụ: Viết chương trình cấp phát bộ nhớ để lưu 10 số nguyên và in các giá trị đó ra.
Bài giảng Kỹ thuật lập trình – Ngành Truyền thông đa phương tiện
34
Bộ môn Truyền thông đa phương tiện – Trường Đại học Công nghệ thông tin và truyền thông
using namespace std;
const int MAX = 10;
int *p, i;
void input()
{
p = new int[MAX];
for (i=0; i<MAX; i++)
cin >> *(p+i);
}
void output()
{
for (i=0; i<MAX; i++)
cout << *(p+i) << " ";
}
void main() {
input();
output();
delete p;
system("pause");
}
1.5.2 Nhập, xuất trong C++
- Xuất dữ liệu: cout<<biến_1<<...<<biến_n;
- Nhập dữ liệu: cin>>biến_1>>...>>biến_n;
Chú ý
- Các hàm xuất nhập cơ bản nằm trong tệp header là iostream. Có 2 lớp thư viện
có chức năng hỗ trợ xuất nhập cơ bản là iostream và iostream.h. Thư viện
iostream có nhiều ưu điểm hơn hẳn so với iostream.h. Thư viện iostream.h ra
đời cách đây quá lâu trong khi thư viện iostream mới hơn nhiều. Nếu các lớp
thư viện có 2 dạng tồn tại song song là .h và không có .h thì ta nên sử dụng thư
viện không có .h. Trong trường hợp không có dạng tương ứng ta bắt buộc phải
sử dụng thư viện .h
- Iostream được đặc tả trong namespace std, trong khi thư viện iostream.h được
khai báo toàn cục. Việc khai báo toàn cục bao giờ cũng chiếm dụng không gian
bộ nhớ lớn hơn.
Bài giảng Kỹ thuật lập trình – Ngành Truyền thông đa phương tiện
35
Bộ môn Truyền thông đa phương tiện – Trường Đại học Công nghệ thông tin và truyền thông
1.5.3 Namespace
Từ khóa namespace
Nhờ vào namespace, ta có thể nhóm các thực thể như lớp, đối tượng và các
hàm dưới một tên gọi tương ứng với từ khóa namespace. Theo cách này, các phạm
vi toàn cục lại được chia thành các phạm vi toàn cục nhỏ hơn mà mỗi phạm vi này
lại có một tên gọi riêng.
- Cú pháp khai báo một namespace:
namespace ten_namespace
{
//các thực thể
}
- Để truy cập tới các thực thể bên trong namespace này ta sử dụng toán tử phạm vi ::
Trong ví dụ trên ta thấy có 2 biến toàn cục là var (phạm vi hoạt động toàn chương
trình). Mặc dù trùng tên nhưng đều này vẫn hợp lệ vì chúng thuộc 2 namspace
khác nhau.
Từ khóa using
Từ khóa using được sử dụng để đưa một tên gọi từ namespace sang vùng khai
báo hiện tại. Khi sử dụng using namespace ten_namespace, chúng ta không cần sử
dụng tới ten_namespace khi gọi đến các thực thể của nó.
Bài giảng Kỹ thuật lập trình – Ngành Truyền thông đa phương tiện
36
Bộ môn Truyền thông đa phương tiện – Trường Đại học Công nghệ thông tin và truyền thông
Tuy nhiên cũng cần nhớ rằng nếu chúng ta using cả hai namespace thì vẫn cần
dùng tới tên namspace và toán tử phạm vi :: để gọi tới thực thể trong namespace.
Phạm vi của namespace
Một namespace được khai báo sử dụng bằng từ khóa using chỉ có tác dụng
trong phạm vi mà nó được khai báo. Điều đó có nghĩa là nếu ta sử dụng using
namespace ten_namespace, thì nó chỉ có tác dụng trong khối lệnh mà ta khai báo.
1.5.4 Ngoại lệ
Các ngoại lệ là cách thức giúp chúng ta tác động ngược trở lại với các tình huống
sinh ra ngoại lệ đó. Để nắm bắt được ngoại lệ chúng ta sử dụng cú pháp trycatch
hoặc throw.
Nếu một chương trình hay một đoạn chương trình có khả năng nảy sinh ngoại lệ
(phát sinh lỗi) chúng ta cần đặt nó trong khối lệnh của từ khóa try, nếu ngoại lệ phát
sinh thì hành động xử lý với ngoại lệ đó sẽ được đặt trong khối lệnh của từ khóa catch.
Bài giảng Kỹ thuật lập trình – Ngành Truyền thông đa phương tiện
37
Bộ môn Truyền thông đa phương tiện – Trường Đại học Công nghệ thông tin và truyền thông
Trong ví dụ trên lệnh throw sẽ phát sinh ra một ngoại lệ có mã là 20 nhưng ngoại lệ
này đã bị bắt bởi khối lệnh trycatch và câu lệnh trong khối catch đã được thực hiện.
Các khối câu lệnh trycatch có thể lồng nhau.
Các file đính kèm theo tài liệu này:
- bai_giang_c_p1_8467.pdf