Top Banner
true class cout C++ operator catch virtual throw try friend bool cin new inline private OOP delete using false STL public 1 ThS. Đặng Bình Phương [email protected] VCBB © 13.09a this Bộ môn Công nghệ phần mềm Khoa Công nghệ thông tin Trường Đại học Khoa học Tự nhiên PP LT HƯỚNG ĐỐI TƯỢNG MỘT SỐ VẤN ĐỀ TRONG LẬP TRÌNH
61

Một số vấn đề thường gặp trong lập trình - Đăng Bình Phương (ĐH KHTN)

Jul 04, 2015

Download

Education

Welcome message from author
This document is posted to help you gain knowledge. Please leave a comment to let me know what you think about it! Share it to your friends and learn new things together.
Transcript
Page 1: Một số vấn đề thường gặp trong lập trình - Đăng Bình Phương (ĐH KHTN)

trueclass

cout

C++operator

catch

virtual throw

try

friend bool

cinnew

inline private OOP

deleteusing falseSTL

public

1

T h S . Đ ặ n g B ì n h P h ư ơ n gd b p h u o n g @ f i t . h c m u s . e d u . v n

VCBB© 13.09a

this

Bộ môn Công nghệ phần mềm

Khoa Công nghệ thông tin

Trường Đại học Khoa học Tự nhiên

PP LT HƯỚNG ĐỐI TƯỢNG

MỘT SỐ VẤN ĐỀ

TRONG LẬP TRÌNH

Page 2: Một số vấn đề thường gặp trong lập trình - Đăng Bình Phương (ĐH KHTN)

2

VC

BB

2 3 4 5 6 7 81

Nội dung

Một số vấn đề trong lập trình

Sự tổng quát hóa trong lập trình

Vấn đề về các hàm cùng tên

Vấn đề về giá trị mặc định của tham số hàm

Vấn đề về các hàm mã nguồn y hệt nhau

Vấn đề về lệnh gộp (macro) và hàm nội tuyến (inline function)

Vấn đề về hàm có số lượng tham số không biết trước

Nhu cầu về kí hiệu phép toán cho kiểu dữ liệu mới

Nhu cầu về gia tăng tính tái sử dụng của mã nguồn #include <iostream>using namespace std;

void main(){

cout << “Hello World”;cout << endl;

Page 3: Một số vấn đề thường gặp trong lập trình - Đăng Bình Phương (ĐH KHTN)

3

VC

BB

Sự tổng quát hóa trong lập trình2 3 4 5 6 7 81

Một số ví dụ

Hàm tìm số lớn nhất trong mảng

Một số vấn đề trong lập trình

1234567891011121314

int findMax(int a[], int n)

{

int i, iMax = 0;

for (i = 1; i < n; i++)

{

if (a[i] > a[iMax]) // a[i] “tốt” hơn?

{

iMax = i;

}

}

return a[iMax];

}

Page 4: Một số vấn đề thường gặp trong lập trình - Đăng Bình Phương (ĐH KHTN)

4

VC

BB

Sự tổng quát hóa trong lập trình2 3 4 5 6 7 81

Một số ví dụ

Hàm tìm số lớn nhất trong mảng

Một số vấn đề trong lập trình

1234567891011121314

int findMin(int a[], int n)

{

int i, iMin = 0;

for (i = 1; i < n; i++)

{

if (a[i] < a[iMin]) // a[i] “tốt” hơn?

{

iMin = i;

}

}

return a[iMin];

}

Page 5: Một số vấn đề thường gặp trong lập trình - Đăng Bình Phương (ĐH KHTN)

5

VC

BB

Sự tổng quát hóa trong lập trình2 3 4 5 6 7 81

Một số ví dụ

Hàm tìm số lớn nhất trong mảng

Một số vấn đề trong lập trình

1234567891011121314

int findBest(int a[], int n, hàm bool isBetter(int, int))

{

int i, iBest = 0;

for (i = 1; i < n; i++)

{

if (isBetter(a[i], a[iBest])) // a[i] “tốt” hơn?

{

iBest = i;

}

}

return a[iBest];

}

Page 6: Một số vấn đề thường gặp trong lập trình - Đăng Bình Phương (ĐH KHTN)

6

VC

BB

Sự tổng quát hóa trong lập trình2 3 4 5 6 7 81

Con trỏ hàm

Khái niệm

Hàm cũng đuợc lưu trữ trong bộ nhớ tại một

địa chỉ xác định.

Con trỏ hàm là con trỏ trỏ đến vùng nhớ chứa

hàm và có thể gọi hàm thông qua con trỏ đó.

Một số vấn đề trong lập trình

……

0A 0B 0C 0D 0E 0F 10 11 12 13 14 15 16 17

pfn int add(int, int)

11 00 00 00

Page 7: Một số vấn đề thường gặp trong lập trình - Đăng Bình Phương (ĐH KHTN)

7

VC

BB

Sự tổng quát hóa trong lập trình2 3 4 5 6 7 81

Khai báo con trỏ hàm

Khai báo tường minh

Ví dụ

Một số vấn đề trong lập trình

int (*pfn1)(int);

void(*pfn2)(int, int);

char(*pfn3)(char* []);

void(*pfn4)();

1234567

return-type (*var-name)(param-type-list);1

Page 8: Một số vấn đề thường gặp trong lập trình - Đăng Bình Phương (ĐH KHTN)

8

VC

BB

Sự tổng quát hóa trong lập trình2 3 4 5 6 7 81

Khai báo con trỏ hàm

Khai báo không tường minh

Ví dụ

int (*pfn1)(int, int); // Tường minh

// Định nghĩa kiểu con trỏ hàm mới

typedef int (*TPFnCalculate)(int, int);

TPFnCalculate pfn2, pfn3; // Không tường minh

123456

12

typedef return-type (*func-ptr-type-name)(param-type-list);

func-ptr-type-name func-ptr-var-name;

Một số vấn đề trong lập trình

Page 9: Một số vấn đề thường gặp trong lập trình - Đăng Bình Phương (ĐH KHTN)

9

VC

BB

Sự tổng quát hóa trong lập trình2 3 4 5 6 7 81

Gán giá trị cho con trỏ hàm

Cú pháp

Ví dụ

Một số vấn đề trong lập trình

int add(int nX, int nY) { return nX + nY; }

int subtract(int nX, int nY) { return nX - nY; }

int (*pfnCalculate)(int, int) = NULL;

pfnCalculate = &add; // Trỏ đến hàm add()

pfnCalculate = subtract; // Trỏ đến hàm subtract()

123456

12

func-ptr-var-name = &func-name; // Dạng chính quy

func-ptr-var-name = func-name; // Dạng ngắn gọn

Page 10: Một số vấn đề thường gặp trong lập trình - Đăng Bình Phương (ĐH KHTN)

10

VC

BB

Sự tổng quát hóa trong lập trình2 3 4 5 6 7 81

So sánh con trỏ hàm

Ví dụ

Một số vấn đề trong lập trình

123456789101112

if (pfnCalculate != NULL)

{

if (pfnCalculate == &add) // Dạng chính quy

printf(“Point to add()”);

else

if (pfnCalculate == subtract) // Dạng ngắn gọn

printf(“Point to subtract()”);

else

printf(“Point to other function!”);

}

else

printf(“Point to none!”);

Page 11: Một số vấn đề thường gặp trong lập trình - Đăng Bình Phương (ĐH KHTN)

11

VC

BB

Sự tổng quát hóa trong lập trình2 3 4 5 6 7 81

Gọi hàm sử dụng con trỏ hàm

Cú pháp

Ví dụ

Một số vấn đề trong lập trình

int add(int nX, int nY) { return nX + nY; }

int subtract(int nX, int nY) { return nX - nY; }

int (*pfnCalculate)(int, int) = &add;

int nResult1 = (*pfnCalculate)(3, 6); // Dạng chính quy

int nResult2 = pfnCalculate(3, 6); // Dạng ngắn gọn

123456

12

(*func-ptr-var-name)(arg-list); // Dạng chính quy

func-ptr-var-name(arg-list); // Dạng ngắn gọn

Page 12: Một số vấn đề thường gặp trong lập trình - Đăng Bình Phương (ĐH KHTN)

12

VC

BB

Sự tổng quát hóa trong lập trình2 3 4 5 6 7 81

Truyền đối số là con trỏ hàm

Ví dụ

Một số vấn đề trong lập trình

12345678910111213

int add(int nX, int nY) { return nX + nY; }

int subtract(int nX, int nY) { return nX - nY; }

int doCalculation(int nX, int nY, int (*pfnCalculate)(int, int))

{

int nResult = (*pfnCalculate)(nX, nY); // Gọi hàm

return nResult;

}

void main()

{

int (*pfnCalculate)(int, int) = &add;

int nResult1 = doCalculation(3, 6, pfnCalculate);

int nResult2 = doCalculation(3, 6, &subtract);

}

Page 13: Một số vấn đề thường gặp trong lập trình - Đăng Bình Phương (ĐH KHTN)

13

VC

BB

Sự tổng quát hóa trong lập trình2 3 4 5 6 7 81

Trả về con trỏ hàm

Ví dụ (khai báo tường minh)

Một số vấn đề trong lập trình

12345678910111213

int (*getCalculation(char cOperator))(int, int)

{

int (*pfnCalculate)(int, int) = NULL;

switch (cOperator)

{

case ‘+’: pfnCalculate = &add; break;

case ‘-’: pfnCalculate = &subtract; break;

}

return pfnCalculate;

}

int (*pfnCalculate)(int, int) = getCalculation(‘+’);

int nResult = pfnCalculate(3, 6);

Page 14: Một số vấn đề thường gặp trong lập trình - Đăng Bình Phương (ĐH KHTN)

14

VC

BB

Sự tổng quát hóa trong lập trình2 3 4 5 6 7 81

Trả về con trỏ hàm

Ví dụ (khai báo không tường minh)

Một số vấn đề trong lập trình

1234567891011121314

typedef int (*TPFnCalculate)(int, int);

TPFnCalculate getCalculation (char cOperator)

{

TPFnCalculate pfnCalculate = NULL;

switch (cOperator)

{

case ‘+’: pfnCalculate = &add; break;

case ‘-’: pfnCalculate = &subtract; break;

}

return pfnCalculate;

}

TPFnCalculate pfnCalculate = getCalculation(‘+’);

int nResult = pfnCalculate(3, 6);

Page 15: Một số vấn đề thường gặp trong lập trình - Đăng Bình Phương (ĐH KHTN)

15

VC

BB

Sự tổng quát hóa trong lập trình2 3 4 5 6 7 81

Mảng con trỏ hàm

Ví dụ

Một số vấn đề trong lập trình

1234567891011121314

typedef int (*TPFnCalculate)(int, int);

void main()

{

int (*apfnCalculate1[2])(int, int); // Cách 1

TPFnPhepToan apfnCalculate2[2]; // Cách 2

apfnCalculate1[0] = apfnCalculate2[1] = &add;

apfnCalculate1[1] = apfnCalculate2[0] = &subtract;

int nResult1 = (*apfnCalculate1[0])(3, 6);

int nResult2 = apfnCalculate1[1](3, 6));

int nResult3 = apfnCalculate2[0](3, 6));

int nResult4 = apfnCalculate2[1](3, 6));

}

Page 16: Một số vấn đề thường gặp trong lập trình - Đăng Bình Phương (ĐH KHTN)

16

VC

BB

Vấn đề về các hàm trùng tên2 3 4 5 6 7 81

Hàm trùng tên

Nhu cầu

Thực hiện công việc bằng nhiều cách khác

nhau. Nếu các hàm khác tên sẽ khó nhớ.

Ví dụ

Một số vấn đề trong lập trình

12345678

// Tính trị tuyệt đối trong C (math.h)

int abs(int);

long labs(long);

double fabs(double);

// Tính căn bậc 2 trong C (math.h)

float sqrtf(float);

double sqrt(double);

Page 17: Một số vấn đề thường gặp trong lập trình - Đăng Bình Phương (ĐH KHTN)

17

VC

BB

Vấn đề về các hàm trùng tên2 3 4 5 6 7 81

Khái niệm

Là các hàm cùng tên nhưng có tham số đầu vào

hoặc kiểu trả về khác nhau nhằm cho phép

người dùng chọn cách thuận lợi nhất để thực

hiện công việc.

Việc sử dụng các hàm trùng tên được gọi là

chồng hàm (function overloading).

Một số vấn đề trong lập trình

12345

void printNumbers(int n); // 1, 2, …, n

void printNumbers(int x, int y); // x, x + 1, …, y

void printNumbers(int x, int y, int a); // x, x + a, …, y

Page 18: Một số vấn đề thường gặp trong lập trình - Đăng Bình Phương (ĐH KHTN)

18

VC

BB

Vấn đề về các hàm trùng tên2 3 4 5 6 7 81

Một số lưu ý

Nguyên mẫu hàm (prototype) sau khi bỏ tên

tham số phải khác nhau.

Một số vấn đề trong lập trình

1234567

// Các hàm sau đây là như nhau vì cùng nguyên mẫu hàm

// Nguyên mẫu hàm chung: int add(int, int);

int add(int nA, int nB);

int add(int nB, int nA);

int add(int nX, int nY);

Page 19: Một số vấn đề thường gặp trong lập trình - Đăng Bình Phương (ĐH KHTN)

19

VC

BB

Vấn đề về các hàm trùng tên2 3 4 5 6 7 81

Một số lưu ý

Sử dụng con trỏ hàm để lấy địa chỉ của các hàm

cùng tên.

Một số vấn đề trong lập trình

123456789101112

void printNumbers(int n); // Xuất 1, 2, …, n

void printNumbers(int x, int y); // Xuất x, x + 1, …, y

void main()

{

void (*pfn1)(int) = &printNumbers;

void (*pfn2)(int, int) = &printNumbers;

cout << pfn1 << endl; // Địa chỉ printNumbers(int)

cout << pfn2 << endl; // Địa chỉ printNumbers(int, int)

}

void printNumbers(int n) { /* … */ }

void printNumbers(int x, int y) { /* … */ }

Page 20: Một số vấn đề thường gặp trong lập trình - Đăng Bình Phương (ĐH KHTN)

20

VC

BB

Vấn đề về các hàm trùng tên2 3 4 5 6 7 81

Sự nhập nhằng, mơ hồ

Ví dụ

Một số vấn đề trong lập trình

123456789101112

float f(float fX) { return fX / 2; }

double f(double dX) { return dX / 2; }

void main()

{

float fA = 29.12;

double dB = 17.06;

float fResult1 = f(fA); // f(float)

double dResult2 = f(dB); // f(double)

float fResult3 = f(369); // ???

}

Page 21: Một số vấn đề thường gặp trong lập trình - Đăng Bình Phương (ĐH KHTN)

21

VC

BB

Vấn đề về các hàm trùng tên2 3 4 5 6 7 81

Sự nhập nhằng, mơ hồ

Ví dụ

Một số vấn đề trong lập trình

12345678910111213

void f(unsigned char cX) { printf(“%d”, cX); }

void f(char cX) { printf(“%c”, cX); }

void main()

{

char c = ‘A’;

f(c); // f(char)

f(‘A’); // f(char)

f(65); // ???

f((char)65); // f(char)

f((unsigned char)65); // f(unsigned char)

}

Page 22: Một số vấn đề thường gặp trong lập trình - Đăng Bình Phương (ĐH KHTN)

22

VC

BB

Vấn đề về các hàm trùng tên2 3 4 5 6 7 81

Sự nhập nhằng, mơ hồ

Ví dụ

Một số vấn đề trong lập trình

12345678910

int f(int nX, int nY) { return nX + nY; }

int f(int nX, int& nY) { return nX + nY; }

void main()

{

int nA = 1, nB = 2;

int nResult1 = f(nA, 2); // f(int, int)

int nResult2 = f(nA, nB); // ???

}

Page 23: Một số vấn đề thường gặp trong lập trình - Đăng Bình Phương (ĐH KHTN)

23

VC

BB

Vấn đề về các hàm trùng tên2 3 4 5 6 7 81

Sự nhập nhằng, mơ hồ

Ví dụ

Một số vấn đề trong lập trình

12345678

int f(int nX) { return nX * nX; }

int f(int nX, int nY = 1) { return nX * nY; }

void main()

{

int nResult1 = f(2912, 1706); // f(int, int)

int nResult2 = f(2912); // ???

}

Page 24: Một số vấn đề thường gặp trong lập trình - Đăng Bình Phương (ĐH KHTN)

24

VC

BB

Vấn đề về giá trị mặc định của tham số hàm2 3 4 5 6 7 81

Nhu cầu sử dụng?

Một số vấn đề trong lập trình

Page 25: Một số vấn đề thường gặp trong lập trình - Đăng Bình Phương (ĐH KHTN)

25

VC

BB

Vấn đề về giá trị mặc định của tham số hàm2 3 4 5 6 7 81

Hàm có tham số có đối số mặc định

Khái niệm

Là hàm có một hay nhiều tham số hình thức

được gán sẵn giá trị mặc định. Các tham số

này nhận giá trị mặc định đó nếu không có đối

số tương ứng được truyền vào.

Các tham số mặc định phải được dồn về tận

cùng bên phải.

Ví dụ

Một số vấn đề trong lập trình

123

void printFraction(int nNum, int nDenom = 1);

float getFinalMark(float fTheoreticalMark, float fPracticalMark,

int nTheoreticalRatio = 1, int nPracticalRatio = 2);

Page 26: Một số vấn đề thường gặp trong lập trình - Đăng Bình Phương (ĐH KHTN)

26

VC

BB

Vấn đề về giá trị mặc định của tham số hàm2 3 4 5 6 7 81

Hàm có tham số có đối số mặc định

Lưu ý

Muốn truyền đối số khác thay cho đối số mặc

định, phải truyền đối số thay cho các đối số

mặc định trước nó.

Ví dụ

Một số vấn đề trong lập trình

123456

float getFinalMark(float fTheoreticalMark, float fPracticalMark,

int nTheoreticalRatio = 1, int nPracticalRatio = 2);

// Điểm LT = 8, Điểm TH = 9, Hệ số LT = 2, Hệ số TH = 3

float fResult1 = getFinalMark(8, 9, 3); // Sai!!!

float fResult2 = getFinalMark(8, 9, 2, 3); // OK

Page 27: Một số vấn đề thường gặp trong lập trình - Đăng Bình Phương (ĐH KHTN)

27

VC

BB

Vấn đề về giá trị mặc định của tham số hàm2 3 4 5 6 7 81

Hàm có tham số có đối số mặc định

Tình huống sử dụng

Nếu 𝑥 = 𝑎 thường xuyên xảy ra thì nên chuyển

𝑥 thành tham số có đối số mặc định là 𝑎.

Ví dụ, 𝐺𝑖𝑜𝑖𝑇𝑖𝑛ℎ = 0 (Nam), 𝑇𝑢𝑜𝑖 = 18.

Thứ tự của các tham số có đối số mặc định

Nếu 𝑥 = 𝑎 và 𝑦 = 𝑏 thường xuyên xảy ra

nhưng 𝑦 = 𝑏 thường xuyên hơn thì nên đặt

tham số mặc định 𝑦 sau 𝑥.

Ví dụ, 𝑇𝑢𝑜𝑖 = 18 xảy ra nhiều hơn𝐺𝑖𝑜𝑖𝑇𝑖𝑛ℎ = 1do đó nên đặt 𝑇𝑢𝑜𝑖 sau 𝐺𝑖𝑜𝑖𝑇𝑖𝑛ℎ.

Một số vấn đề trong lập trình

Page 28: Một số vấn đề thường gặp trong lập trình - Đăng Bình Phương (ĐH KHTN)

28

VC

BB

Vấn đề về các hàm mã nguồn y hệt nhau2 3 4 5 6 7 81

Các hàm mã nguồn y hệt nhau

Ví dụ

Một số vấn đề trong lập trình

1234567891011

// Hàm tìm số nhỏ nhất trong 2 số nguyên kiểu int

int findMin(int x, int y)

{

return (x < y) ? x : y;

}

// Hàm tìm số nhỏ nhất trong 2 số thực kiểu float

float findMin(float x, float y)

{

return (x < y) ? x : y;

}

Page 29: Một số vấn đề thường gặp trong lập trình - Đăng Bình Phương (ĐH KHTN)

29

VC

BB

Vấn đề về các hàm mã nguồn y hệt nhau2 3 4 5 6 7 81

Khuôn mẫu hàm (function template)

Cú pháp

Ví dụ

Một số vấn đề trong lập trình

1 template <template-type-list> function-definition

12345678910

// Hàm tìm số nhỏ nhất trong 2 số kiểu T bất kỳ

template <class T>

T findMin(T x, T y)

{

return (x < y) ? x : y;

}

void main()

{

int nMin = findMin<int>(2912, 1706);

}

Page 30: Một số vấn đề thường gặp trong lập trình - Đăng Bình Phương (ĐH KHTN)

30

VC

BB

Vấn đề về các hàm mã nguồn y hệt nhau2 3 4 5 6 7 81

Khuôn mẫu hàm (function template)

Lợi ích của việc sử dụng khuôn mẫu hàm

Dễ viết do chỉ cần viết hàm tổng quát nhất.

Dễ hiểu do chỉ quan tâm đến kiểu tổng quát nhất.

Có kiểu an toàn do trình biên dịch kiểm tra

kiểu lúc biên dịch chương trình.

Khi phối hợp với quá tải hàm, quá tải toán tử

hoặc con trỏ hàm ta có thể viết được các

chương trình rất hay, ngắn gọn, linh động và

có tính tiến hóa cao.

Một số vấn đề trong lập trình

Page 31: Một số vấn đề thường gặp trong lập trình - Đăng Bình Phương (ĐH KHTN)

31

VC

BB

Vấn đề về lệnh gộp (macro) và hàm nội tuyến (inline function)2 3 4 5 6 7 81

Lệnh gộp – lệnh tắt (macro)

Trong C, ngoài việc sử dụng để định nghĩa các

hằng ký hiệu thì #define còn được dùng để

định nghĩa các lệnh gộp – lệnh tắt (macro).

Cú pháp

Ở mọi chỗ xuất hiện của name với lượng

tham số param-list đưa vào phù hợp sẽ

được thay thế văn bản (textual substitution)

bởi expression (tham số được thay thế

tương ứng).

Một số vấn đề trong lập trình

1 #define name(param-list) expression

Page 32: Một số vấn đề thường gặp trong lập trình - Đăng Bình Phương (ĐH KHTN)

32

VC

BB

Vấn đề về lệnh gộp (macro) và hàm nội tuyến (inline function)2 3 4 5 6 7 81

Lệnh gộp – lệnh tắt (macro)

Ví dụ

Một số vấn đề trong lập trình

1234567891011121314

#include <stdio.h>

#define SHOW_MESSAGE(szMessage) printf(szMessage)

#define EPSILON 0.0001

#define FLOAT_EQ(u, v) (((v-EPSILON)<x) && (x<(v+EPSILON)))

void main()

{

float a = 1.234f;

float b = 2.345f;

float c = a + b;

if (FLOAT_EQ(c, 3.579))

SHOW_MESSAGE(“Equal\n”);

else

SHOW_MESSAGE(“Not equal\n”);

}

Page 33: Một số vấn đề thường gặp trong lập trình - Đăng Bình Phương (ĐH KHTN)

33

VC

BB

Vấn đề về lệnh gộp (macro) và hàm nội tuyến (inline function)2 3 4 5 6 7 81

Hàm nội tuyến (inline function)

Ví dụ

Một số vấn đề trong lập trình

1234567891011121314

#define PI 3.14159f

float addPi(float s)

{

return s + PI;

}

void main()

{

float s1 = 0, s2 = 0;

for (int i = 1; i <= 100000; i++) // Cách 1

s1 = s1 + PI; // ~0,7 giây

for (int j = 1; j <= 100000; j++) // Cách 2

s2 = addPI(s2); // ~1,4 giây

}

Page 34: Một số vấn đề thường gặp trong lập trình - Đăng Bình Phương (ĐH KHTN)

34

VC

BB

Vấn đề về lệnh gộp (macro) và hàm nội tuyến (inline function)2 3 4 5 6 7 81

Hàm nội tuyến (inline function)

Nhận xét

Sử dụng hàm giúp chương trình dễ hiểu

nhưng lại tốn chi phí cho lời gọi hàm.

Khắc phục

Trong C++, sử dụng hàm nội tuyến bằng cách

đặt từ khóa inline trước prototype của hàm.

Trình biên dịch thực hiện tương tự như macro

bằng cách sao chép (triển khai nội tuyến)

thân hàm đến bất cứ nơi nào hàm được gọi.

Một số vấn đề trong lập trình

1 inline float addPi(float s) { return s + PI; }

Page 35: Một số vấn đề thường gặp trong lập trình - Đăng Bình Phương (ĐH KHTN)

35

VC

BB

Vấn đề về lệnh gộp (macro) và hàm nội tuyến (inline function)2 3 4 5 6 7 81

Hàm nội tuyến (inline function)

Một số đặc điểm và lưu ý

Giảm thời gian thực hiện hàm (gọi và kết thúc).

Giảm không gian bộ nhớ do các hàm con

chiếm dụng khi hàm được gọi.

Không cho phép các hàm nội tuyến đệ quy.

Phần lớn không cho phép thực hiện nội tuyến

các hàm sử dụng vòng lặp while.

Chỉ inline các hàm nhỏ, inline các hàm lớn

sẽ gây phản tác dụng (bộ nhớ cho hàm

inline chiếm giữ sẽ lâu giải phóng hơn).

Một số vấn đề trong lập trình

Page 36: Một số vấn đề thường gặp trong lập trình - Đăng Bình Phương (ĐH KHTN)

36

VC

BB

Vấn đề về lệnh gộp (macro) và hàm nội tuyến (inline function)2 3 4 5 6 7 81

So sánh giữa macro và hàm nội tuyến

macro không kiểm tra kiểu dữ liệu và kiểm tra

các tham số có định dạng đúng hay không mà chỉ

thực hiện thay thế văn bản (textual substitution)

nên có thể dẫn đến các hiệu ứng phụ và sự không

hiệu quả nằm ngoài dự tính do việc đánh giá lại

các tham số và trật tự tính toán.

Lỗi biên dịch trong các macro thường rất khó hiểu

vì lỗi nằm trong phần mã đã khai triển, chứ không

phải phần mã do lập trình viên viết.

Một số vấn đề trong lập trình

12

#define SQRT(X) X*X // SQRT(1+2) 1+2*1+2 = 5!!!

#define SQRT(X) (X)*(X) // SQRT(1+2) (1+2)*(1+2) = 9

Page 37: Một số vấn đề thường gặp trong lập trình - Đăng Bình Phương (ĐH KHTN)

37

VC

BB

Vấn đề về lệnh gộp (macro) và hàm nội tuyến (inline function)2 3 4 5 6 7 81

So sánh giữa macro và hàm nội tuyến

Khó biểu diễn các cấu trúc phức tạp bằng macro

trong khi đó hàm nội tuyến sử dụng cấu trúc

thông thường và có thể được chuyển hoặc

bỏ chế độ nội tuyến một cách dễ dàng.

macro không cho phép triển khai đệ qui trong khi

đó một số trình biên dịch cho phép triển khai đệ

qui đối với hàm nội tuyến.

Bjarne Stroustrup (cha đẻ của C++) nhấn mạnh

rằng nên hạn chế sử dụng macro mỗi khi có thể

tránh được và nên sử dụng các hàm nội tuyến.

Một số vấn đề trong lập trình

Page 38: Một số vấn đề thường gặp trong lập trình - Đăng Bình Phương (ĐH KHTN)

38

VC

BB

Vấn đề về hàm có số lượng tham số không biết trước2 3 4 5 6 7 81

Nhu cầu

Trong một số trường hợp, các hàm hay các

phương thức không thể biết trước số lượng các

tham số ngay tại thời điểm biên dịch mà chỉ biết

vào lúc chạy chương trình (chẳng hạn các hàm

printf() và scanf() của C/C++).

Ví dụ

Một số vấn đề trong lập trình

123456

scanf(“%d”, &a); // Nhận 2 đối số

scanf(“%d”, &b, &c); // Nhận 3 đối số

printf(“%d + %d = ”, a, b); // Nhận 3 đối số

printf(“%d\n”, a + b); // Nhận 2 đối số

Page 39: Một số vấn đề thường gặp trong lập trình - Đăng Bình Phương (ĐH KHTN)

39

VC

BB

Vấn đề về hàm có số lượng tham số không biết trước2 3 4 5 6 7 81

Khai báo tham số …

Cú pháp

Lưu ý

Hàm có số lượng tham số không biết trước và

thường cùng kiểu (không được là char,

unsigned char, float).

Phải có ít nhất 1 tham số biết trước.

Tham số … đặt ở cuối cùng.Một số vấn đề trong lập trình

1234

return-type func-name(known-param-list, …)

{

// Các câu lệnh

}

Page 40: Một số vấn đề thường gặp trong lập trình - Đăng Bình Phương (ĐH KHTN)

40

VC

BB

Vấn đề về hàm có số lượng tham số không biết trước2 3 4 5 6 7 81

Khai báo tham số …

Ví dụ

Một số vấn đề trong lập trình

1234567891011121314

void printSum1(char* szMessage, int n, ...)

{

// Xuất n số nguyên kèm thông báo szMessage

}

void printSum2(char* szMessage, ...)

{

// Xuất các số nguyên kèm thông báo szMessage

}

int sum(int a, ...)

{

// Tính và trả về tổng các số nguyên bắt đầu là a

}

Page 41: Một số vấn đề thường gặp trong lập trình - Đăng Bình Phương (ĐH KHTN)

41

VC

BB

Vấn đề về hàm có số lượng tham số không biết trước2 3 4 5 6 7 81

Truy xuất danh sách tham số …

Sử dụng kiểu và các macro sau (stdarg.h)

va_list: kiểu dữ liệu chứa các tham số có trong

va_start(va_list ap, lastfix): macro thiết

lập ap chỉ đến tham số đầu tiên trong … với

lastfix là tên tham số cố định cuối cùng.

type va_arg(va_list ap, type): macro trả về

tham số có kiểu type tiếp theo.

va_end(va_list ap): macro giúp cho hàm trả

về giá trị một cách “bình thường”.

Một số vấn đề trong lập trình

Page 42: Một số vấn đề thường gặp trong lập trình - Đăng Bình Phương (ĐH KHTN)

42

VC

BB

Vấn đề về hàm có số lượng tham số không biết trước2 3 4 5 6 7 81

Truy xuất danh sách tham số …

Ví dụ

Một số vấn đề trong lập trình

1234567891011121314

#include <stdarg.h>

void printSum1(char* szMessage, int n, ...)

{

va_list ap;

va_start(ap, n); // Đối số đã biết cuối cùng

int nValue, nSum = 0;

for (int i = 0; i < n; i++)

{

nValue = va_arg(ap, int);

nSum = nSum + nValue;

}

va_end(ap);

printf(“%s %d”, szMessage, nSum);

}

Page 43: Một số vấn đề thường gặp trong lập trình - Đăng Bình Phương (ĐH KHTN)

43

VC

BB

Vấn đề về hàm có số lượng tham số không biết trước2 3 4 5 6 7 81

Truy xuất danh sách tham số …

Ví dụ

Một số vấn đề trong lập trình

12345678910111213

#include <stdarg.h>

void printSum2(char* szMessage, ...)

{

va_list ap;

va_start(ap, szMessage); // Đối số đã biết cuối cùng

int nValue, nSum = 0;

while ((nValue = va_arg(ap, int)) != 0)

{

nSum = nSum + nValue;

}

va_end(ap);

printf(“%s %d”, szMessage, nSum);

}

Page 44: Một số vấn đề thường gặp trong lập trình - Đăng Bình Phương (ĐH KHTN)

44

VC

BB

Vấn đề về hàm có số lượng tham số không biết trước2 3 4 5 6 7 81

Truy xuất danh sách tham số …

Ví dụ

Một số vấn đề trong lập trình

12345678910111213

#include <stdarg.h>

int sum(int a, …)

{

va_list ap;

va_start(ap, a); // Đối số đã biết cuối cùng

int nValue, nSum = a;

while ((nValue = va_arg(ap, int)) != 0)

{

nSum = nSum + nValue;

}

va_end(ap);

return nSum;

}

Page 45: Một số vấn đề thường gặp trong lập trình - Đăng Bình Phương (ĐH KHTN)

45

VC

BB

Nhu cầu về kí hiệu phép toán cho kiểu dữ liệu mới2 3 4 5 6 7 81

Ví dụ

Một số vấn đề trong lập trình

Page 46: Một số vấn đề thường gặp trong lập trình - Đăng Bình Phương (ĐH KHTN)

46

VC

BB

Nhu cầu về kí hiệu phép toán cho kiểu dữ liệu mới2 3 4 5 6 7 81

Nạp chồng toán tử

Cách thực hiện

Nạp chồng toán tử (operator overloading)

bằng cách viết thêm hàm toán tử mới.

Cú pháp

# là toán tử (trừ . :: * ?)

param-list (danh sách tham số) phụ thuộc

vào toán tử được nạp chồng.Một số vấn đề trong lập trình

1234

return-type operator#(param-list)

{

// Các thao tác cần thực hiện…

}

Page 47: Một số vấn đề thường gặp trong lập trình - Đăng Bình Phương (ĐH KHTN)

47

VC

BB

Nhu cầu về kí hiệu phép toán cho kiểu dữ liệu mới2 3 4 5 6 7 81

Một số lưu ý

Toán tử 1 ngôi (chỉ có một toán hạng)

Bao gồm: tăng (++), giảm (--), đảo dấu (-)

Có thể thay đổi toán hạng.

Có thể trả kết quả về cho phép toán tiếp theo.

Toán tử 2 ngôi (gồm 2 toán hạng)

Bao gồm: gán (=), số học (+, -, *, /, %), quan

hệ (<, <=, >, >=, !=, ==), luận lý (&&, ||, !)

Có thể thay đổi toán hạng vế trái (toán tử gộp).

Có thể trả kết quả về cho phép toán tiếp theo.

Một số vấn đề trong lập trình

Page 48: Một số vấn đề thường gặp trong lập trình - Đăng Bình Phương (ĐH KHTN)

48

VC

BB

Nhu cầu về kí hiệu phép toán cho kiểu dữ liệu mới2 3 4 5 6 7 81

Một số lưu ý

Ưu điểm

Cho phép thực hiện trên kiểu dữ liệu do

người lập trình tự định nghĩa.

Khuyết điểm

Không thể tạo toán tử mới.

Không thể định nghĩa lại toán tử trên kiểu

dữ liệu cơ sở.

Không thể thay đổi số ngôi của toán tử

(số lượng toán hạng tham gia) của toán tử.

Không thể thay đổi độ ưu tiên của toán tử.Một số vấn đề trong lập trình

Page 49: Một số vấn đề thường gặp trong lập trình - Đăng Bình Phương (ĐH KHTN)

49

VC

BB

Nhu cầu về kí hiệu phép toán cho kiểu dữ liệu mới2 3 4 5 6 7 81

Ví dụ minh họa

Nạp chồng toán tử +

Một số vấn đề trong lập trình

12345678910111213

typedef struct { int m_nNum, m_nDenom; } SFraction;

SFraction operator+(SFraction fracL, SFraction fracR)

{

SFraction fracResult;

fracResult.m_nNum = fracL.m_nNum * fracR.m_nDenom

+ fracR.m_nNum * fracL.m_nDenom;

fracResult.m_nDenom = fracL.m_nDenom * fracR.m_nDenom;

return fracResult;

}

SFraction frac1 = {1, 2}, frac2 = {3, 4}, frac3 = {5, 6};

SFraction frac4 = frac1 + frac2 + frac3; // OK

Page 50: Một số vấn đề thường gặp trong lập trình - Đăng Bình Phương (ĐH KHTN)

50

VC

BB

Nhu cầu về kí hiệu phép toán cho kiểu dữ liệu mới2 3 4 5 6 7 81

Ví dụ minh họa

Nạp chồng toán tử +

Một số vấn đề trong lập trình

12345678910111213

typedef struct { int m_nNum, m_nDenom; } SFraction;

void operator+(SFraction& fracL, SFraction fracR)

{

SFraction fracResult;

fracResult.m_nNum = fracL.m_nNum * fracR.m_nDenom

+ fracR.m_nNum * fracL.m_nDenom;

fracResult.m_nDenom = fracL.m_nDenom * fracR.m_nDenom;

fracL = fracResult;

}

SFraction frac1 = {1, 2}, frac2 = {3, 4}, frac3 = {5, 6};

SFraction frac4 = frac1 + frac2 + frac3; // Lỗi

Page 51: Một số vấn đề thường gặp trong lập trình - Đăng Bình Phương (ĐH KHTN)

51

VC

BB

Nhu cầu về kí hiệu phép toán cho kiểu dữ liệu mới2 3 4 5 6 7 81

Ví dụ minh họa

Nạp chồng toán tử +

Một số vấn đề trong lập trình

1234567891011

typedef struct { int m_nNum, m_nDenom; } SFraction;

SFraction operator+(SFraction frac, int n)

{

frac.m_nNum = frac.m_nNum + n * frac.m_nDenom;

return frac;

}

SFraction frac1 = {2912, 1706};

SFraction frac2 = frac1 + 369; // OK

SFraction frac3 = 369 + frac1; // Lỗi sai thứ tự toán hạng

Page 52: Một số vấn đề thường gặp trong lập trình - Đăng Bình Phương (ĐH KHTN)

52

VC

BB

Nhu cầu về kí hiệu phép toán cho kiểu dữ liệu mới2 3 4 5 6 7 81

Ví dụ minh họa

Nạp chồng toán tử ==

Một số vấn đề trong lập trình

12345678910111213

typedef struct { int m_nNum, m_nDenom; } SFraction;

int operator==(SFraction fracL, SFraction fracR)

{

return (fracL.m_nNum * fracR.m_nDenom

== fracR.m_nNum * fracL.m_nDenom);

}

SFraction frac1 = {1, 2}, frac2 = {2, 4};

if (frac1 == frac2)

cout << “Two fractions are equal.” << endl;

else

cout << “Two fractions are not equal.” << endl;

Page 53: Một số vấn đề thường gặp trong lập trình - Đăng Bình Phương (ĐH KHTN)

53

VC

BB

Nhu cầu về kí hiệu phép toán cho kiểu dữ liệu mới2 3 4 5 6 7 81

Ví dụ minh họa

Nạp chồng toán tử ++ (trước)

Một số vấn đề trong lập trình

1234567891011

typedef struct { int m_nNum, m_nDenom; } SFraction;

SFraction operator++(SFraction& frac)

{

frac.m_nNum = frac.m_nNum + frac.m_nDenom;

return frac;

}

SFraction frac1 = {1, 2}, frac2 = {1, 2};

SFraction frac3 = ++frac1;

SFraction frac4 = frac2++; // OK nhưng có cảnh báo

Page 54: Một số vấn đề thường gặp trong lập trình - Đăng Bình Phương (ĐH KHTN)

54

VC

BB

Nhu cầu về kí hiệu phép toán cho kiểu dữ liệu mới2 3 4 5 6 7 81

Ví dụ minh họa

Nạp chồng toán tử ++ (sau)

Một số vấn đề trong lập trình

123456789101112

typedef struct { int m_nNum, m_nDenom; } SFraction;

SFraction operator++(SFraction& frac, int nNotUsed)

{

SFraction fracResult = frac;

frac.m_nNum = frac.m_nNum + frac.m_nDenom;

return fracResult;

}

SFraction frac1 = {1, 2}, frac2 = {1, 2};

SFraction frac3 = ++frac1; // Gọi toán tử ++ trước

SFraction frac4 = frac2++; // Gọi toán tử ++ sau

Page 55: Một số vấn đề thường gặp trong lập trình - Đăng Bình Phương (ĐH KHTN)

55

VC

BB

Nhu cầu về kí hiệu phép toán cho kiểu dữ liệu mới2 3 4 5 6 7 81

Ví dụ minh họa

Nạp chồng toán tử - (đảo dấu)

Một số vấn đề trong lập trình

12345678910

typedef struct { int m_nNum, m_nDenom; } SFraction;

SFraction operator-(SFraction& frac)

{

frac.m_nNum = -frac.m_nNum;

return frac;

}

SFraction frac1 = {1, 2};

SFraction frac2 = -frac1;

Page 56: Một số vấn đề thường gặp trong lập trình - Đăng Bình Phương (ĐH KHTN)

56

VC

BB

Nhu cầu về gia tăng tính tái sử dụng của mã nguồn2 3 4 5 6 7 81

Thảo luận

Một số vấn đề trong lập trình

Page 57: Một số vấn đề thường gặp trong lập trình - Đăng Bình Phương (ĐH KHTN)

57

VC

BB

1 2 3 4 5 6

Một số thuật ngữ

ambiguous: tính nhập nhằng, mơ hồ.

default parameter: tham số mặc định.

duplicate code: trùng mã.

function overloading: nạp chồng hàm.

function pointer: con trỏ hàm.

function template: khuôn mẫu hàm.

operator overloading: nạp chồng toán tử.

Một số vấn đề trong lập trình

Page 58: Một số vấn đề thường gặp trong lập trình - Đăng Bình Phương (ĐH KHTN)

58

VC

BB

1 2 3 4 5 6

Tài liệu tham khảo

ThS. Đặng Bình Phương, Bài giảng KTLT

Hàm nâng cao (phần 1 và phần 2)

[Primer] Chapter 7 – Functions: C++’s

Programming Modules

Pointers to Functions (trang 327-331)

[Primer] Chapter 8 – Adventures In Functions

Default Arguments (trang 362-365)

Function Overloading (trang 365-370)

Function Templates (trang 370-388)

Một số vấn đề trong lập trình

Page 59: Một số vấn đề thường gặp trong lập trình - Đăng Bình Phương (ĐH KHTN)

59

VC

BB

1 2 3 4 5 6

Bài tập 1.1

Viết 3 phiên bản hàm sau:

Viết hàm tìm số nhỏ nhất trong một mảng agồm n số nguyên kiểu int cho trước.

Viết hàm tìm số nhỏ nhất trong một mảng agồm n phần tử có kiểu bất kỳ cho trước

(gợi ý sử dụng khuôn mẫu hàm – template).

Viết hàm tìm số “tốt nhất” (theo một tiêu chí

nào đó) trong một mảng a gồm n phần tử

có kiểu bất kỳ cho trước (gợi ý sử dụng khuôn

mẫu hàm kết hợp với con trỏ hàm).

Một số vấn đề trong lập trình

Page 60: Một số vấn đề thường gặp trong lập trình - Đăng Bình Phương (ĐH KHTN)

60

VC

BB

1 2 3 4 5 6

Bài tập 1.2

Viết 3 phiên bản hàm sau:

Viết hàm sắp xếp tăng dần một mảng agồm n số nguyên kiểu int cho trước.

Viết hàm sắp xếp tăng dần một mảng agồm n phần tử có kiểu bất kỳ cho trước

(gợi ý sử dụng khuôn mẫu hàm – template).

Viết hàm sắp xếp mảng a gồm n phần tử có

kiểu bất kỳ cho trước theo một tiêu chí sắp

xếp được xác định lúc gọi hàm (gợi ý sử dụng

khuôn mẫu hàm kết hợp với con trỏ hàm).

Một số vấn đề trong lập trình

Page 61: Một số vấn đề thường gặp trong lập trình - Đăng Bình Phương (ĐH KHTN)

61

VC

BB

1 2 3 4 5 6

Bài tập 1.3

Viết các hàm toán tử thao tác trên kiểu phân số

(SFraction):

Toán tử nhập xuất: >>, <<

Toán tử tăng giảm (trước và sau): ++, --

Toán tử đảo dấu: -

Toán tử nghịch đảo: ~

Toán tử tính toán: +, -, *, /, +=, -=, *=, /=

Toán tử so sánh: >, >=, <, <=, ==, !=

(lưu ý cho phép tính toán/so sánh giữa 2

phân số và giữa phân số với số nguyên).Một số vấn đề trong lập trình