Bài giảng Kỹ thuật lập trình hệ cơ điện tử: Chương 13 - C++ nâng cao được biên soạn bao gồm các nội dung chính sau: Xử lý ngoại lệ (Exception Handling; Bộ nhớ độn; Namespace; Template; Bộ tiền xử lý (Preprocessor); Xử lý tín hiệu (Signal Handling); Đa luồng (Multithread); Một số lớp quan trọng. Mời các bạn cùng tham khảo bài giảng!
Trang 3• Exception cung cấp một cách để truyền điều khiển từ một phần của một chương trình tới phần khác. Exception Handling (Xử lý ngoại lệ) trong C++ được xây dựng dựa
trên 3 từ khóa là: try, catch, và throw.
Trang 4X lý ngo i l (Exception Handling) ử ạ ệ
Gi i thi u ớ ệ
§ throw: Một chương trình ném một Exception khi một vấn
đề xuất hiện. Việc này được thực hiện bởi sử dụng từ
khóa throw trong C++.
§ catch: Một chương trình bắt một Exception với một
Exception Handler tại vị trí trong một chương trình nơi
bạn muốn xử lý vấn đề đó. Từ khóa catch trong C++ chỉ
dẫn việc bắt một exception
Trang 5và cú pháp để sử dụng try/catch trong C++ như sau:
Trang 6X lý ngo i l (Exception Handling) ử ạ ệ
Gi i thi u ớ ệ
Trang 8X lý ngo i l (Exception Handling) ử ạ ệ
B t Exception trong C++ ắ
• Khối catch theo sau khối try trong C++ sẽ bắt bất kỳ
exception nào. Bạn có thể xác định kiểu của exception bạn muốn bắt và điều này được xác định bởi khai báo exception mà xuất hiện trong các dấu ngoặc đơn theo sau từ khóa catch trong C++
Trang 10X lý ngo i l (Exception Handling) ử ạ ệ
Ví dụ
Trang 11X lý ngo i l (Exception Handling) ử ạ ệ
Ví dụ
Trang 12X lý ngo i l (Exception Handling) ử ạ ệ
Standard Exception trong C++
• C++ cung cấp một danh sách các Standard Exception
được định nghĩa trong <exception> mà chúng ta có thể
sử dụng trong các chương trình. Những exception này được sắp xếp theo cấu trúc thứ tự cha-con như sau:
Trang 13X lý ngo i l (Exception Handling) ử ạ ệ
Standard Exception trong C++
Trang 14X lý ngo i l (Exception Handling) ử ạ ệ
Standard Exception trong C++
• C++ cung cấp một danh sách các Standard Exception
được định nghĩa trong <exception> mà chúng ta có thể
sử dụng trong các chương trình. Những exception này được sắp xếp theo cấu trúc thứ tự cha-con như sau:
Trang 16X lý ngo i l (Exception Handling) ử ạ ệ
Trang 17X lý ngo i l (Exception Handling) ử ạ ệ
Đ nh nghĩa Exception m i trong C++ ị ớ
Trang 19B nh đ ng ộ ớ ộ
Khái ni m ệ
• Bạn có thể cấp phát bộ nhớ tại run time bên trong Heap cho biến đó với một kiểu đã cho bởi sử dụng một toán tử đặc biệt trong C/C++ mà trả về địa chỉ của không gian đã
cấp phát. Toán tử này gọi là toán tử new trong C/C++.
• Nếu bạn không cần thiết bộ nhớ động đã cấp phát nữa,
bạn có thể sử dụng toán tử delete trong C/C++, sẽ giải
phóng bộ nhớ đã được cấp phát trước đó bởi toán tử new
Trang 20B nh đ ng ộ ớ ộ
Toán t new và delete trong C/C++ ử
• Đây là cú pháp chung để sử dụng toán tử new để cấp
phát bộ nhớ động cho bất kỳ kiểu dữ liệu nào trong C/C++:
• Ở đây, kieu_du_lieu có thể là bất kỳ kiểu dữ liệu có sẵn
nào ví dụ như mảng hoặc các kiểu dữ liệu tự định nghĩa như lớp hoặc cấu trúc
Trang 21B nh đ ng ộ ớ ộ
Ví dụ
Trang 22B nh đ ng ộ ớ ộ
Toán t new và delete trong C/C++ ử
• Tại bất kỳ thời điểm, khi bạn cảm thấy một biến đã được cấp phát động là không cần thiết nữa, bạn có thể giải phóng bộ nhớ mà nó đã chiếm giữ trong phần bộ nhớ rỗi
với toán tử delete trong C/C++, như sau:
Trang 23B nh đ ng ộ ớ ộ
Ví dụ
Trang 24B nh đ ng ộ ớ ộ
C p phát b nh đ ng cho M ng trong C/C++ ấ ộ ớ ộ ả
Trang 25B nh đ ng ộ ớ ộ
C p phát b nh đ ng cho Đ i t ấ ộ ớ ộ ố ượ ng
Trang 26B nh đ ng ộ ớ ộ
C p phát b nh đ ng cho Đ i t ấ ộ ớ ộ ố ượ ng
Trang 27• Một namespace trong C++ giúp bạn vượt qua tình huống
này và được sử dụng như là thông tin bổ sung để phân biệt các hàm, lớp, biến, … cùng tên có sẵn trong các thư viện khác nhau. Sử dụng namespace trong C++, bạn có thể định nghĩa bối cảnh trong đó các tên được định nghĩa. Về bản chất, một namespace định nghĩa một phạm vi (scope) trong C++
Trang 28Đ nh nghĩa m t Namespace ị ộ
• Một định nghĩa namespace bắt đầu với từ khóa namespace được theo sau bởi tên của namespace đó, như sau:
• Để gọi phiên bản namespace của hàm hoặc biến, bạn phụ thêm vào sau tên của namespace, như sau:
Trang 29Namespace
Ví dụ
Trang 30Ví dụ
Trang 31compiler rằng dãy code ở sau đang sử dụng các tên được xác định trong namespace đã xác định đó. Bạn xét
ví dụ:
Trang 32using namespace directive
Trang 33Namespace
using namespace directive
Trang 34Namespace không k nhau ề
• Một namespace có thể được định nghĩa thành một số phần và vì thế một namespace được tạo từ một tập hợp các phần được định nghĩa riêng biệt. Các phần riêng biệt này của một namespace có thể ở nhiều file khác nhau
• Vì thế, nếu một phần của namespace đó yêu cầu một tên được định nghĩa ở file khác, thì tên đó vẫn phải được khai báo. Định nghĩa namespace sau hoặc định nghĩa một namespace mới hoặc thêm các phần tử mới tới
Trang 36Ví dụ
Trang 37Namespace
Ví dụ
Trang 38Khái ni m ệ
• Template là nền tảng của lập trình tổng quát (generic programming), tức là viết code theo các mà độc lập với bất kỳ kiểu cụ thể nào
• Một Template là một blueprint hoặc là phương thức để tạo một lớp hoặc một hàm tổng quát. Các Library Container như Iterator và các thuật toán là các ví dụ của lập trình tổng quát và đã được phát triển bởi sử dụng khái niệm Template
Trang 39§ Function Template: là một khuôn mẫu hàm, cho phép định nghĩa các hàm tổng quát thao tác cho nhiều kiểu dữ liệu.
§ Class template: là một khuôn mẫu lớp, cho phép định nghĩa các lớp tổng quát cho nhiều kiểu dữ liệu
Trang 41Template
Ví dụ
Trang 42Class Template
• Ở đây, kieu_du_lieu là tên kiểu, mà sẽ được xác định khi
một lớp được khai báo. Bạn có thể định nghĩa nhiều hơn một kiểu dữ liệu tổng quát (generic) bởi sử dụng một danh sách phân biệt nhau bởi dấu phảy
Trang 43Template
Ví dụ
Trang 44Ví dụ
Trang 45Template
Ví dụ
Trang 46B ti n x lý (Preprocessor) ộ ề ử
Khái ni m ệ
• Bộ tiền xử lý (Preprocessor) là các directive (chỉ thị),
cung cấp chỉ lệnh tới bộ biên dịch để tiền xử lý thông tin trước khi bắt đầu biên dịch thực sự
• Tất cả chỉ thị tiền xử lý (Preprocessor directive) bắt đầu với #, và chỉ có các ký tự khoảng trống trắng (white-space) là có thể xuất hiện ở trước một chỉ thị tiền xử lý trên một dòng. Chỉ thị tiền xử lý không là các lệnh trong C++, vì thế chúng không kết thúc với một dấu chấm phảy
Trang 48B ti n x lý (Preprocessor) ộ ề ử
FunctionLike Macro
Trang 49• Chỉ lệnh tiền xử lý có điều kiện khá giống với cấu trúc lựa chọn if. Bạn xét code sau:
Trang 50B ti n x lý (Preprocessor) ộ ề ử
Biên d ch có đi u ki n ị ề ệ
• Bạn có thể biên dịch một chương trình với mục đích
debug và có thể tắt hoặc bật việc debug này bởi sử dụng một macro trong C++, như sau:
• Lệnh cerr để được biên dịch trong chương trình nếu biểu
tượng hằng DEBUG đã được định nghĩa ở trước chỉ thị
Trang 51B ti n x lý (Preprocessor) ộ ề ử
Ví dụ
Trang 52B ti n x lý (Preprocessor) ộ ề ử
Các toán t # và ## ử
• Các toán tử tiền xử lý # và ## là có sẵn trong C++ và ANSI/ISO C. Toán tử # thông báo rằng đoạn văn bản thay thế sẽ được chuyển đổi sang một chuỗi bao quanh bởi dấu ngoặc kép
Trang 53B ti n x lý (Preprocessor) ộ ề ử
Các toán t # và ## ử
• Toán tử ## được sử dụng để nối chuỗi hai token. Ví dụ:
Trang 54B ti n x lý (Preprocessor) ộ ề ử
Macro ti n đ nh nghĩa ề ị
• C++ cung cấp một số macro được định nghĩa trước như liệt kê dưới đây:
LINE Chứa số dòng hiện tại của chương trình khi nó đang được biên dịch FILE Chứa tên file hiện tại của chương trình khi nó đang được biên dịch
Trang 55B ti n x lý (Preprocessor) ộ ề ử
Macro ti n đ nh nghĩa ề ị
Trang 56X lý tín hi u (Signal Handling) ử ệ
Khái ni m ệ
• Tín hiệu (Signal) là các ngắt (interrupt) được phân phối
tới một tiến trình xử lý bởi hệ điều hành mà có thể kết thúc một chương trình. Bạn có thể tạo các ngắt bằng việc nhấn CTRL+C trên hệ thống UNIX, LINUX, Mac OS hoặc Windows
• Có các signal mà không thể bị bắt bởi chương trình,
nhưng cũng có signal mà bạn có thể bắt trong chương trình của bạn, và có thể thực hiện các hành động thích
Trang 57X lý tín hi u ử ệ
Khái ni m ệ
Signal Miêu tả
SIGABRT Sự kết thúc bất thường của chương trình, ví dụ một lời gọi tới abort
SIGFPE Một hoạt động số học không đúng, ví dụ như chia cho số 0 hoặc một hoạt động làm tràn luồng (overflow)SIGILL Sự phát hiện một chỉ lệnh không hợp lệ
SIGINT Nhận một tín hiệu tương tác
SIGSEGV Một truy cập không hợp lệ tới storage
SIGTERM Một yêu cầu kết thúc được gửi tới chương trình
Trang 59X lý tín hi u ử ệ
Ví dụ
Trang 60X lý tín hi u ử ệ
Hàm raise() trong C++
• Bạn có thể tạo các tín hiệu bởi hàm raise() trong C++,
mà nhận một số integer biểu diễn signal number như một tham số và có cú pháp như sau:
• Ở đây, sig là signal number để gửi bất kỳ loại tín hiệu
Trang 61X lý tín hi u ử ệ
Ví dụ
Trang 62X lý tín hi u ử ệ
Ví dụ
Trang 63• Đa nhiệm dựa trên tiến trình xử lý việc thực thi đồng thời của các chương trình. Đa nhiệm dựa trên luồng xử lý
việc thực thi đồng thời các phần của cùng một chương trình
Trang 64Đa lu ng (Multithread) ồ
Khái ni m ệ
• Một chương trình đa luồng chứa hai hoặc nhiều phần mà có thể chạy đồng thời. Mỗi phần của chương trình đó được gọi là một thread, và mỗi thread định nghĩa một path riêng biệt của
sự thực thi.
• Trong các hệ thống đa lõi và đa xử lý thì đa luồng tức là các thread được thực hiện cùng lúc trên lõi hoặc bộ vi xử lý khác nhau.
• Đối với hệ thống lõi đơn thì đa luồng chia thời gian giữa các
Trang 66Đa lu ng (Multithread) ồ
How to create a thread?
Trang 67Đa lu ng (Multithread) ồ
Khái ni m ệ
• Bạn có thể thử chạy đoạn code trên, biên dịch không có vấn đề gì, tuy nhiên bạn sẽ lập tức đối mặt với 1 runtime error: WHYYYYYY?
• Thực tế thì câu trả lời khá đơn giản: Main thread tạo 1 thread mới là funcTest1 với paramaters threadFunc.
Main thread không đợi funcTes1 huỷ. Nó tiếp tục hoạt động và sẽ kết thúc, nhưng funcTest1 vẫn chạy. Nó sẽ dẫn đễn lỗi. TẤT CẢ CÁC THREAD PHẢI HUỶ TRƯỚC KHI MAIN THREAD HUỶ
• Vậy làm các nào khắc phục vấn đề này?????
Trang 68Đa lu ng (Multithread) ồ
Join threads
• Thread class cung cấp method join(), hàm này chỉ return khi tất cả các thread kết thúc, điều đó có nghĩa là main thread sẽ đợi đến khi tất cả các thread con hoàn thành công việc của nó
Add thêm đoạn call hàm joind vào sample trên và chạy lại
Trang 69• Not joinable thread có thể huỷ 1 cách an toàn.
• Hàm joinable để checks thread có là joinable thread hay không
Trang 70Đa lu ng (Multithread) ồ
Joinable and not Joinable threads
• Nên sử dụng hàm này trước khi call hàm join();
• This function returns true if the thread is joinable and false otherwise. It’s better to check if the thread is joinable before join() function is called:
Trang 71Đa lu ng (Multithread) ồ
Detaching thread
• Như đã nhắc ở trên, thread trở thành not joinable sau khi hàm detach được gọi
• Hàm này tách 1 thread từ 1 thread cha, nó cho phép
thread cha và thread con được chạy ngay lập tức từ cái còn lại. Sau khi call detach functon, các thread sẽ không đồng bộ trong bất kỳ cách nào
Trang 72Đa lu ng (Multithread) ồ
Detaching thread
Trang 74Đa lu ng (Multithread) ồ
Initializing thread with an object
• Bây giờ bạn có thể khởi tạo thread bằng cách truyền
object của class myFunctor vào hàm khởi tạo của thread:
Trang 75Đa lu ng (Multithread) ồ
Initializing thread with an object
• Nếu bạn muốn khởi tạo thread với 1 public function của class, bạn phải định nghĩa function và truyền object của class định nghĩa function đó:
• Bây giờ có thể khởi tạo thread với hàm publicFunction của myFunctor class:
Trang 78to the overloading version of operator ():
Trang 80Đa lu ng (Multithread) ồ
Passing arguments to thread
• Có thể sử dụng 1 hàm của class như params của thread. Add 1 public function vào myFunctorParam class:
Trang 81Đa lu ng (Multithread) ồ
Passing arguments to thread
• Truyền argument vào member function:
Trang 82Đa lu ng (Multithread) ồ
ThreadID
• Mọi thread đều có 1 unique identifier. Và trong class thread có 1 public function cho phép get ra giá trị của thread Id
giá trị trả về kiểu id được định nghĩa trong Thread
class
Trang 83Đa lu ng (Multithread) ồ
ThreadID