• Với mỗi bài toán, làm thế nào để:

– Thiết kế giải thuật nhằm giải quyết bài toán đó – Cài đặt giải thuật bằng một chương trình máy tính

- Hãy làm cho chương trình chạy đúng trước khi tăng tính hiệu quả của chương trình - Hãy tăng tính hiệu quả của chương trình và đồng thời thể hiện tốt phong cách lập trình của cá nhân

CHƯƠNG III. CÁC KỸ THUẬT XÂY DỰNG CHƯƠNG TRÌNH PHẦN MỀM

I. Mở đầu II. Làm việc với biến III. Viết mã chương trình hiệu quả IV. Thiết kế chương trình V. Xây dựng hàm/thủ tục

V. CÁC KỸ THUẬT XÂY DỰNG HÀM/THỦ TỤC

1. Một số khái niệm thường gặp 2. Nguyên tắc chung 3. Các quy tắc tăng tốc độ 4. Kỹ thuật chồng/ đa năng hóa các hàm/toán tử

1. Một số khái niệm thường gặp

• Biểu thức (expression): tính toán giá

trị đích dựa trên giá trị nguồn

• Lệnh gán (assigment): lưu trữ giá trị

của biểu thức hoặc của biến nguồn vào trong 1 biến đích

a. Hàm định nghĩa sẵn

• Được định nghĩa trong các thư viện • Cần khai báo thư viện ở đầu chương trình để có thể dùng các hàm này • Ví dụ: trong thư viện cmath, hàm sqrt tính căn bậc hai của một số •

the_root = sqrt(9.0);

dụng như một biểu thức

– 9.0 : tham số, cũng có thể là một biến hoặc là một biểu thức – the_root : biến lưu kết quả trả về (3.0) – sqrt(9.0) : lời gọi hàm (kích hoạt việc thực hiện hàm sqrt), cũng có thể được sử

bonus = sqrt(sales) / 10; cout << “Cạnh của hình vuông có diện tích “

<< area << “ là “

<< sqrt(area);

b. Hàm do LTV định nghĩa

• Khai báo hàm:

– Chỉ ra cách thức gọi hàm – Phải khai báo trước khi gọi hàm – Cú pháp

Kiểu_trả_về Tên_hàm (Kiểu_1 tên_tham_số_1,..,

Kiểu_n tên_tham_số_n);

Kiểu_trả_về Tên_hàm (Kiểu_1,.., Kiểu_n);

//Chú thích: hàm dùng để làm gì

• Ví dụ: khai báo hàm cho phép tính tổng chi phí theo công thức: tổng chi phí = số lượng hàng * giá mỗi mặt hàng + 5% thuế giá trị gia tăng double total_cost(int number_par, double price_par); double total_cost(int, double);

b. Hàm do LTV định nghĩa

• Định nghĩa hàm:

– Chỉ ra cách thức thực hiện nhiệm vụ của hàm – Định nghĩa trước hoặc sau khi gọi hàm đều được – Cú pháp: Kiểu_trả_về Tên_hàm (Danh_sách_tham_số) {

//code return kết_quả;

} • Ví dụ:

double total_cost(int number_par, double price_par) { const double TAX_RATE = 0.05; //5% tax double subtotal; subtotal = price_par * number_par; return (subtotal + subtotal * TAX_RATE); }

c. Gọi hàm

• Tên_hàm(tham_số_1, …, tham_số_n) • Giá trị được truyền vào các tham số của hàm sẽ được

sử dụng trong phần thân hàm. – Pass by value: giá trị truyền vào là bản sao của giá trị lưu

trữ trong biến đóng vai trò tham số đầu vào

• Giá trị tham số không thay đổi khi được sử dụng trong thân

hàm

• Gọi hàm tham trị loại bỏ các thay đổi ngoài ý muốn lên các

tham số

– Pass by reference: giá trị truyền vào là địa chỉ của tham số

đầu vào

• Giá trị tham số có thể thay đổi khi được sử dụng trong thân

hàm, do truyền biến gốc chứ không phải bản sao

• Thay đổi giá trị của đối số trong hàm sẽ ảnh hưởng hoặc thay

đổi trực tiếp lên biến gốc

Ví dụ

Khai báo hàm

Truyền địa chỉ của p vào hàm square_num

Hàm thay đổi giá trị của p thành 25

Định nghĩa hàm, tham số vào là con trỏ num trỏ điến giá trị kiểu int

void square_num (int *); int main() { int p = 5; printf ("P is now %d\n", p); square_num (&p); printf ("P is now %d\n", p); return 0; } void square_num (int *num) { (*num)= (*num) * (*num); }

* biểu diễn giá trị mà con trỏ num trỏ đến

Viết hàm để hoán đổi giá trị hai biến?

if (a[i] < a[j]) trao_doi(a[i], a[j]);

void trao_doi(int so1, int so2) { int temp; temp = so1; so1 = so2; so2 = temp; } void sap_xep_mang(int *a, int n) { int i, j, temp; for (i=0; i

Viết hàm để hoán đổi giá trị hai biến?

void trao_doi(int *so1, int *so2) { int temp; temp = *so1; *so1 = *so2; *so2 = temp; } void sap_xep_mang(int *a, int n) { int i, j; for (i=0; i

if (a[i] < a[j]) trao_doi(&a[i], &a[j]);

}

d. Biến tham chiếu trong C++

• Cú pháp:

– kieuDL &ten_bien; • Bí danh của biến khác

– Thay đổi biến tham chiếu (bí danh) sẽ làm thay đổi giá trị của biến được tham chiếu

• Ví dụ:

– int count = 1; – int &ref = count; //ref là bí danh của count – ++ref; //tăng count lên 1,sử dụng bí danh ref

e. Tham số kiểu tham chiếu trong C++

• Giống tham số được khai báo var trong Pascal • Thay đổi tham số kiểu tham chiếu (tham số hình thức) trong

thân hàm sẽ làm thay đổi tham số thực khi truyền.

int a=5, b=8; trao_doi(a, b);

void trao_doi(int &x, int &y) { int temp = x; x = y; y = temp; } • Gọi hàm: • Với cách gọi hàm này, C++ tự gửi địa chỉ của a và b làm tham

số cho hàm trao_doi().

Ví dụ - Hàm tham chiếu

#include int x = 4; int & myFunc() { return x; } int main() { cout<<"X="<

#include #include int myAbs(int X) { return abs(X); } long myAbs(long X) { return labs(X); } double myAbs(double X) { return fabs(X); } int main() { }

4.2. Đa năng hoá toán tử

• Trong ngôn ngữ C, khi chúng ta tự tạo ra một

kiểu dữ liệu mới, chúng ta thực hiện các thao tác liên quan đến kiểu dữ liệu đó thường thông qua các hàm, điều này trở nên không thoải mái.

• Ví dụ: Cài đặt các phép toán cộng và trừ số phức

Ví dụ trong C

#include

struct SP {double THUC; double AO; } ;

SP setSP(double R,double I);

SP addSP(SP C1,SP C2);

SP subSP(SP C1,SP C2);

void displaySP(SP C);

int main(void) {

SP C1,C2,C3,C4;

C1 = SetSP(1.0,2.0); C2 = SetSP(-3.0,4.0);

cout <<"\nSo phuc thu nhat:"; displaySP(C1);

cout << "\nSo phuc thu hai:"; displaySP(C2);

C3 = AddSP(C1,C2); C4 = SubSP(C1,C2);

cout <<"\nTong hai so phuc nay:";

displaySP(C3);

cout << "\nHieu hai so phuc nay:";

displaySP(C4);

return 0;

}

Ví dụ trong C (2)

tmp.AO = C1.AO + C2.AO; return tmp;

cout <

SP setSP(double r,double i) { SP tmp; tmp.THUC = r; tmp.AO = i; return tmp; } SP addSP(SP C1, SP C2) { SP tmp; tmp.THUC = C1.THUC + C2.THUC; } SP subSP(SP C1,SP C2) { SP tmp; tmp.THUC = C1.THUC - C2.THUC; tmp.AO = C1.AO - C2.AO; return tmp; } void displaySP(SP C) { }

Đa năng hóa toán tử trong C++

• Trong ví dụ C trên, ta dùng hàm để cài đặt các phép toán

cộng và trừ hai số phức

 phức tạp, không thoải mái, tự nhiên khi sử dụng, vì thực chất thao tác cộng và trừ là các toán tử chứ không phải là hàm.

• C++ cho phép chúng ta có thể định nghĩa lại chức năng của các toán tử đã có sẵn một cách tiện lợi và tự nhiên hơn rất nhiều

 Điều này gọi là đa năng hóa toán tử • Cú pháp định nghĩa toán tử:

kieuDL operator kyHieuToanTu(danhSachThamSo) { ……………………………… }

Ví dụ - Bài toán số phức trong C++

#include

struct { double THUC; double AO; } SP;

SP setSP(double r,double i);

void displaySP(SP C);

SP operator + (SP C1,SP C2);

SP operator - (SP C1,SP C2);

void main() {

SP C1,C2,C3,C4;

C1 = SetSP(1.1,2.0); C2 = SetSP(-3.0,4.0);

cout<<"\nSo phuc thu nhat:"; displaySP(C1);

cout<<"\nSo phuc thu hai:"; displaySP(C2);

C3 = C1 + C2; C4 = C1 - C2;

cout<<"\nTong hai so phuc nay:"; displaySP(C3);

cout<<"\nHieu hai so phuc nay:"; displaySP(C4);

}

Ví dụ - Bài toán số phức trong C++ (2)

setSP(double r, double i) {

SP tmp; tmp.THUC = r; tmp.AO = i; return tmp;

}

//Cong hai so phuc

SP operator +(SP C1, SP C2) {

SP tmp; tmp.THUC = C1.THUC+C2.THUC;

tmp.AO = C1.AO+C2.AO; return tmp;

}

//Tru hai so phuc

SP operator -(SP C1, SP C2) {

SP tmp; tmp.THUC = C1.THUC-C2.THUC;

tmp.AO = C1.AO-C2.AO; return tmp;

}

//Hien thi so phuc

void displaySP(SP C) { cout<<"("<

}

Các giới hạn của đa năng hóa toán tử

• Không thể định nghĩa các toán tử mới • Hầu hết các toán tử của C++ có thể đa năng hóa trừ:

– :: Toán tử định phạm vi. – .* Truy cập đến con trỏ là trường của struct hay class. – . Truy cập đến trường của struct hay class. – ?: Toán tử điều kiện – sizeof – Các ký hiệu tiền xử lý

• Không thể thay đổi thứ tự ưu tiên của một toán tử

cũng như số các toán hạng của nó.

• Không thể thay đổi ý nghĩa của các toán tử khi áp

dụng cho các kiểu có sẵn

• Không thể có các tham số có giá trị mặc định