Tải bản đầy đủ (.doc) (174 trang)

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

Bạn đang xem bản rút gọn của tài liệu. Xem và tải ngay bản đầy đủ của tài liệu tại đây (536.55 KB, 174 trang )

Lập trình hớng đối tợng với C++
Mục lục
Mục lục.........................................................1
lời nói đầu.....................................................5
chơng 1: Mở đầu..........................................6
Bài 1. So sánh giữa lập trình hớng đối tợng và
lập trình hớng thủ tục..........................................6
1. Lập trình hớng thủ tục.............................................6
2. Lập trình hớng đối tợng...........................................7
Bài 2. Giới thiệu ngôn ngữ C++..............9
1. Chú thích trong C và trong C++.............................9
2. Các từ khóa mới .......................................................9
3. Phép chuyển kiểu bắt buộc....................................10
4. Khai báo biến..........................................................10
5. Hàm main................................................................11
6. Truyền tham số cho hàm.......................................11
7. Nhắc lại về biến con trỏ.........................................15
8. Sử dụng tham số truyền ngầm định trong hàm. .17
9. Cấp phát và giải phóng bộ nhớ.............................19
10. Các hàm tải bội (Overloaded function)..............20
11. Hàm inline.............................................................22
12. Khả năng vào ra mới của C++............................26
Bài 3. Một số khái niệm cơ bản của lập trình h-
ớng đối tợng .......................................................30
1. Khái niệm đối tợng (Object)..................................30
2. Lớp đối tợng (Class)...............................................30
3. Trừu tợng hoá dữ liệu và bao gói thông tin
(Abstraction and Encapsulation) ..............................30
4. Sự kế thừa (Inheritance)........................................31
5. Sự đa hình và sự tải bội (Polymorphism and
Oveloading)..................................................................33


Lập trình hớng đối tợng với C++
chơng 2: Lớp các đối tợng ........................37
Bài 1. lớp các đối tợng ...........................37
1. Khai báo lớp các đối tợng .....................................37
2. Các thành phần trong lớp (Data member and
function member)........................................................41
3. Đối tợng trong tham số của hàm...........................52
Bài 2. Constructor và destructor...........56
Bài 3. Toán tử tải bội và toán tử tải bội thân
thiện....................................................................60
1. Khái quát chung.....................................................60
2. Toán tử tải bội ........................................................60
3. Bài tập 1...................................................................65
4. Toán tử tải bội thân thiện......................................66
5. Bài tập 2...................................................................75
6. Chuyển đổi kiểu....................................................76
Bài 4. Vài vấn đề về sử dụng từ khoá const 84
1. Sử dụng biến kiểu const ........................................84
2. Truyền tham số kiểu const cho hàm.....................85
3. Đối tợng hằng của lớp ...........................................87
4. Hàm thành phần const (hàm thành phần có khai
báo const phía sau)......................................................89
5. Con trỏ this kiểu const (hàm thành phần có khai
báo const phía trớc).....................................................92
6. Lớp các dãy bit........................................................94
7. Nghiên cứu lớp lớp String......................................98
chơng 3: Sự kế thừa - inheritance.........105
Bài 1. Sự tơng ứng trong kế thừa........105
1. Khái niệm chung...................................................105
2

Nguyễn Văn Tân

2. Kế thừa đơn...........................................................106
3. Thành phần protected trong kế thừa.................108
4. Kế thừa đa mức ....................................................113
5. Kế thừa phân cấp.................................................114
6. Kế thừa bội...........................................................114
7. Kế thừa kép (lai ghép)..........................................116
8. Constructor trong các lớp kế thừa......................120
Bài 2. Con trỏ xác định thành phần của lớp và
con trỏ xác định đối tợng.................................124
1. Các ký hiệu cú pháp về con trỏ...........................124
2. Các ví dụ................................................................125
3. Con trỏ xác định đối tợng....................................127
Bài 3. Hàm ảo và tơng ứng bội trong kế thừa
...........................................................................132
1. Các hàm dịch chuyển (Override function)........132
2. Con trỏ xác định đối tợng trong quan hệ kế thừa
.....................................................................................133
3. Con trỏ hàm ảo và tơng ứng bội ........................135
4. Kế thừa lai ghép và lớp cơ sở ảo..........................138
5. Hàm rỗng (Null Function)...................................141
6. Hàm ảo thực sự và lớp trừu tợng (pure function &
abstract class).............................................................142
7. Hàm thân thiện ảo và toán tử ảo dịch chuyển ..144
chơng 4: lớp mẫu và hàm mẫu...............145
Khái quát ..................................................................145
1. Hàm mẫu...............................................................145
2. Lớp mẫu ................................................................148
Phụ lục - Giới thiệu Một số bài toán phân tích

thiết kế hớng đối tợng trên C++.........................154
3
Lập trình hớng đối tợng với C++
1. Sơ lợc 5 bớc thực hiện...........................................154
2. Xây dựng lớp hình học phẳng.............................155
3. Quản lý các lô đất.................................................165
Tài liệu tham khảo...................................174
4
Nguyễn Văn Tân

lời nói đầu
Trong những năm 1980, ngôn ngữ C đã khẳng định
đợc vị trí quan trọng trong các ngôn ngữ lập trình có cấu
trúc bởi tính đa năng của mình. Tuy nhiên khi độ phức tạp
của bài toán khá lớn thì C không còn đáp ứng đợc đối với
các dự án lớn. Cần phải thiết kế những phần mềm mang
những đợc những đặc tính của thế giới thực bên bên ngoài.
Kỹ thuật lập trình xuất phát trên ý tởng này mang tên là là
kỹ thuật lập trình hớng đối tợng (Object Oriented
Programing viết tắt là OOP) và trên kỹ thuật này nhiều
trình biên dịch đã đợc thiết kế nh Smalltalk, C++...
Lập trình hớng đối tợng đợc phát triển từ ngôn ngữ
lập trình có cấu trúc nhng thay vì xoay quanh chức năng
của nhiệm vụ đã đặt ra, lập trình hớng đối tợng lại đặt
trọng tâm vào việc xử lý các dữ liệu để thực hiện các chức
năng đó. Trong lập trình hớng đối tợng, khái niệm về đối t-
ợng (Object) trở thành một khái niệm trọng tâm và hầu nh
mọi công việc trong một chơng trình đều đợc tiến hành trên
các đối tợng này.
Những vấn đề trọng tâm và mới mẻ của lập trình h-

ớng đối tợng với C++ mà chúng ta cần phải tập trung
nghiên cứu là:
Sự bao gói (Encapsulation)
Sự kế thừa (Inheritance)
Sự tơng ứng bội (Polymorphism)
5
Lập trình hớng đối tợng với C++
chơng 1:Mở đầu
Bài 1. So sánh giữa lập trình hớng đối t-
ợng và lập trình hớng thủ tục
1. Lập trình hớng thủ tục
* Theo cách tiếp cận truyền thống của lập trình h-
ớng thủ tục thì chơng trình đợc xem nh dãy các công việc
cần thực hiện: Nhập dữ liệu, Xử lý dữ liệu, Tính toán, Lập
báo cáo, In kết quả...
* Vậy trọng tâm của lập trình hớng thủ tục là phân
rã bài toán thành các hàm chức năng theo kỹ thuật top-
down. Bài toán đợc giải quyết bởi một số hàm chính. Các
hàm chính lại có thể phân chia thành các bài toán con - tức
là các hàm chức năng nhỏ hơn nữa. Một số hàm có thể gọi
thực hiện (dùng chung) một số hàm chức năng nhỏ hơn.
Nh vậy, cấu trúc các hàm của một chơng trình lập trình h-
ớng thủ tục là cấu trúc phân cấp các hàm:

* Việc chú ý tới các hàm trong chơng trình sẽ không
quan tâm nhiều đến dữ liệu. Dữ liệu của toàn bộ chơng
trình đợc các hàm dùng chung và biến đổi từ hàm này sang
hàm khác. Bản thân trong các hàm cũng có dữ liệu riêng.
6
main

Hàm1 Hàm2 Hàm3
Hàm4
Hàm5 Hàm6 Hàm7 Hàm8
Nguyễn Văn Tân

* Vậy đặc trng của lập trình hớng thủ tục là
1. Tập trung vào công việc cần thực hiện - thuật
toán.
2. Chơng trình đợc chia thành các hàm nhỏ.
3. Phần lớn các hàm dùng chung dữ liệu.
4. Dữ liệu trong chơng trình chuyển động từ hàm
này sang hàm khác.
5. Hàm biến đổi dữ liệu từ dạng này sang dạng khác.
6. Thiết kế chơng trình theo kỹ thuật top-down
2. Lập trình hớng đối tợng
* Lập trình hớng đối tợng đặt trọng tâm vào đối tợng
và không cho dữ liệu chuyển động trong hệ thống. Dữ liệu
gắn chặt vào các hàm và thủ tục tạo thành một vùng riêng
và không cho các hàm bên ngoài truy nhập một cách tuỳ
tiện.
* Lập trình hớng đối tợng phân tích bài toán thành
các thực thể gọi là đối tợng, sau đó xây dựng các hàm xung
quanh đối tợng đó.
* Dữ liệu của đối tợng chỉ có thể truy nhập đợc bởi
các hàm của đối tợng. Chỉ có một số hàm của đối tợng này
có thể gọi thực hiện các hàm của đối tợng khác biểu thị sự
trao đổi thông báo giữa các đối tợng.
7
Dữ liệu chung
Dữ liệu

chung
Hàm 1
Dữ liệu riêng 1
Hàm 2
Dữ liệu riêng 2
Hàm 3
Dữ liệu riêng 3
Đối tượng A
Dữ liệu
Các hàm
Đối tượng A
Dữ liệu
Các hàm
Đối tượng A
Dữ liệu
Các hàm
Lập trình hớng đối tợng với C++
* Nh vậy đặc trng của lập trình hớng đối tợng là:
1. Tập trung vào dữ liệu thay cho hàm.
2. Chia chơng trình thành các đối tợng.
3. Dữ liệu đợc thiết kế sao cho đặc tả đợc đối tợng.
4. Các hàm đợc xây dựng trên các vùng dữ liệu của
đối tợng và đợc gắn liền với nhau trên cấu trúc dữ liệu đó.
5. Dữ liệu đợc bao bọc, che dấu không cho các hàm
ngoại lai truy nhập.
6. Các đối tợng trao đổi thông báo với nhau thông
qua các hàm.
7. Dữ liệu và các hàm dễ dàng bổ sung vào đối tợng.
8. Chơng trình đợc thiết kế theo kỹ thuật botom up.
8

Nguyễn Văn Tân

Bài 2. Giới thiệu ngôn ngữ C++
Trong bài này: với mục đích so sánh ngôn ngữ C và
C++ sẽ làm rõ một số đặc điểm mạnh của C++ so với C,
đồng thời nhấn mạnh thêm hoặc giải thích chi tiết một số
vấn đề quan trọng, thờng gặp khi lập trình với C++.
1. Chú thích trong C và trong C++
Trớc hết C++ đợc phát triển từ C nên nó vẫn sử dụng
lại tất cả các cú pháp ngôn ngữ nh C và bổ sung thêm một
số cú pháp mới.
C++ đa ra một kiểu chú thích mới để tiện chú thích
trên một dòng nếu dùng ký pháp // và cũng tiện để viết trên
nhiều dòng liên tục nếu dùng cặp ký pháp /*... */
Chú thích trong C Chú thích trong C++
/* dong 1 chu thich cua C */
/* dong 2 chu thich cua C */
x=y=0; /*chu thich sau lenh
*/
/* Bat dau chu thich
Dong 2 chu thich
Ket thuc chu thich */
x=y=10; // chu thich sau
lenh
2. Các từ khóa mới
C++ bổ sung vào C một số từ khóa mới
asm
friend
new
protected

this
catch
operator
inline
virtual
class
public
private
cdecl
pascal
delete
templed
9
Lập trình hớng đối tợng với C++
3. Phép chuyển kiểu bắt buộc
Ngoài phép chuyển kiểu nh trong ngôn ngữ C, C++
còn đa ra phép chuyển kiểu mới. Xét ví dụ sau đây:
int x=0;
long z=(long)x; // chuyen kieu cua C
long z=long(x); // chuyen kieu cua C++
Nh vậy phép chuyển kiểu trong C++ có dạng nh một
hàm số chuyển kiểu đang đợc gọi. Cách chuyển kiểu này
rõ ràng vừa khoa học vừa dễ dọc.
4. Khai báo biến
Nhìn chung phạm vi hoạt động của biến phụ thuộc
vào kiểu của biến và vị trí khai báo của biến trong chơng
trình.
Ngôn ngữ C đòi hỏi phải khai báo biến trớc phạm
vi mà các biến đang sử dụng. Cách khai báo đơn giản nhất
trong C là khai báo biến toàn cục lên trớc tất cả các hàm và

khai báo biến cục bộ ở đầu thân từng hàm.
Trong C++, khác với C, cho phép một cách khai báo
biến mới, với cách khai báo này, một biến có thể khai báo
ở vị trí bất kỳ miễn sao là trớc khi sử dụng nó, phạm vi
hoạt động của biến trong trờng hợp này là trong khối nơi
biến đó đợc khai báo.
Ví dụ 1: Khai báo sau đây trong C ++ chấp nhận nh-
ng trong C là lỗi vì C yêu cầu khai báo biến trớc lệnh.
clrscr();
int x;
Ví dụ 2:
void simple()
{ int n; // khai bao cuc bo
for(int i=0; i<10; i++) // cho phep khai bao i trong for
{ int m;
if (i&1) n+=1
10
Nguyễn Văn Tân

}
// su dung bien m ngoai pham vi khoi cua m nhu sau
la sai
m = n/2; // sai
n = i; // dung
}
5. Hàm main
Hàm main trong C không đợc định nghĩa kiểu.
Hàm main trong C++ đợc định nghĩa kiểu để trả lại
một giá trị nguyên cho hệ điều hành DOS, do đó phải khai
báo là kiểu int, ngầm định-vì vậy sẽ là int. Tuy nhiên ta

vẫn có thể định nghĩa kiểu void cho hàm main.
Khi chơng trình bắt đầu thực hiện, hàm main đợc gọi
bởi một chơng trình khởi động đặc biệt (start up code) có
sẵn trong C++.
Start up code trong C++ có 4 cách gọi (tùy chọn vào
Option\ Application):
Dos standard (file C0.ASM)
Dos overlays (file C0.ASM)
Windows Application (file C0w.ASM)
Windows DLL (file C0d.ASM)
Giá trị trả lại của hàm main sẽ đợc Dos sử dụng để
kiểm tra lỗi khi thực hiện chơng trình. Giá trị này bằng 0
nếu không có lỗi. Các giá trị khác 0 đợc C++ biện luận
theo các lỗi khi thực hiện chơng trình.
6. Truyền tham số cho hàm
C++ vẫn sử dụng 2 cách truyền tham số cho hàm
trong C là truyền theo giá trị và truyền theo con trỏ.
Tuy nhiên, C++ có thêm một cách mới để truyền
tham số cho hàm là truyền tham số kiểu tham chiếu. Cách
mới này rất hay sử dụng vì thích hợp truyền tham số cho
11
Lập trình hớng đối tợng với C++
hàm là địa chỉ của đối tợng - một kiểu dữ liệu mới chỉ có
trong C++ (kiểu lớp).
Dới đây ta nhắc lại 2 cách truyền thống và cung cấp
cách mới thứ 3.
6.1. Truyền tham số kiểu giá trị cho hàm
Theo cách truyền theo giá trị thì chỉ có bản sao của
đối đợc hàm sử dụng. Trong thân hàm, các giá trị này có
thể bị thay đổi nhng ra khỏi hàm thì giá trị ban đầu của đối

vẫn giữ nguyên. Đơng nhiên giá trị đầu vào của đối đợc giữ
nguyên vì không phải là chính bản thân đối truyền cho hàm
mà là bản sao của đối đợc truyền cho hàm. Sự thay đổi các
giá trị trong hàm là chỉ thay đổi giá trị của bản sao của nó
mà thôi.
Ví dụ 1 (thso1.cpp)
#include <stdio.h>
#include <conio.h>
void swap(int,int);
main() {
int x=10,y=5;
clrscr();
swap(x,y);
printf("x=%d y=%d\n",x,y);
getch();
}
void swap(int x,int y)
{int z=x; x=y; y=z;}
6.2. Truyền tham số kiểu con trỏ cho hàm
Khi đối truyền cho hàm kiểu con trỏ thì lúc gọi hàm
các con trỏ này sẽ thay thế bằng địa chỉ của các biến để
truyền vào hàm hoặc là một con trỏ hoàn toàn xác định
nghĩa là đang chứa một địa chỉ của một biến nào đó. Nh
vậy về bản chất thì bản gốc của đối đợc truyền cho hàm
12
Kết quả
x = 10 y=5
Nguyễn Văn Tân

chứ không phải là bản sao của nó. Vì vậy, những giá trị của

đối bị thân hàm làm thay đổi sẽ đợc giữ lại khi hàm kết
thúc.
Ví dụ 2 (thso2.cpp)
#include <stdio.h>
#include <conio.h>
void swap(int*,int*);
main() {
int x=10,y=5;
clrscr();
swap(&x,&y);
printf("x=%d y=%d\n",x,y);
getch();}
void swap(int *x,int *y){int z=*x; *x=*y; *y=z;}
Chú ý
- Khi khai báo: void swap(int*,int*)
- Khi định nghĩa: void swap(int *x,int *y) { // dùng
*x và *y; }
- Khi sử dụng: swap(&x,&y)
6.3. Truyền tham số kiểu tham chiếu cho hàm
Tác dụng của việc truyền tham số cho hàm theo
tham chiếu cũng giống nh việc truyền tham số cho hàm
theo con trỏ. Bản chất của hai cách truyền tham số theo
con trỏ và theo tham chiếu là truyền cho hàm địa chỉ của
đối chứ không phải là bản sao của đối. Vậy các giá trị của
đối bị hàm thay đổi cũng sẽ đợc giữ lại khi ra khỏi hàm.
Ví dụ 3 (thso3.cpp)
#include <stdio.h>
#include <conio.h>
void swap(int &,int &);
void main() {

13
Kết quả
x = 5 y=10
Kết quả
x = 5 y=10
Lập trình hớng đối tợng với C++
int x=10,y=5;
clrscr();
swap(x,y);
printf("x=%d y=%d\n",x,y);
getch();
}
void swap(int &x,int &y)
{int z=x; nx=y; y=z;}
Rõ ràng việc sử dụng tham chiếu đơn giản hơn so
với con trỏ. Khi truyền theo tham chiếu thì ta chỉ báo hiệu
địa chỉ của đối ở phần khai báo còn lời gọi hàm và nội
dung hàm ta vẫn viết tên đối một cách bình thờng.
Hai cách truyền theo con trỏ và truyền theo tham
chiếu khác nhau ở chỗ: truyền theo tham chiếu tức là làm
việc trực tiếp với địa chỉ của đối truyền vào hàm, còn
truyền theo con trỏ thì can thiệp gián tiếp vào địa chỉ của
đối, vì con trỏ trong trờng hợp này dùng để chứa địa chỉ
của đối và truyền vào hàm.
Điều khác biệt này thể hiện rõ hơn với các chú ý sau
đây:
+ Không đợc cấp phát bộ nhớ động cho biến tham
chiếu. Trong khi đó, biến con trỏ thì đợc phép xin cấp phát
bộ nhớ động.
+ Không dùng các phép toán tăng giảm địa chỉ cho

biến tham chiếu. Trong khi đó biến con trỏ cho phép các
phép toán này. Việc tăng giảm địa chỉ của biến con trỏ là
làm thay đổi địa chỉ bộ nhớ (biến) mà con trỏ đang trỏ vào.
Ngợc lại biến tham chiếu gắn liền với địa chỉ tĩnh trong bộ
nhớ và do đó không đợc phép thay đổi địa chỉ này.
14
Nguyễn Văn Tân

7. Nhắc lại về biến con trỏ
7.1. So sánh con trỏ và tham chiếu địa chỉ của biến
Chẳng hạn nếu có khai báo:
int *p; và gán *p=10; thì đơng nhiên có thể viết (*p)
++; nhng thậm chí có thể viết p++; Trong khi đó ta không
thể có lệnh tăng địa chỉ (&x)++ nếu có khai báo int x;
C++ cho phép viết nh vậy và 2 cách viết có tác dụng
khác nhau: Lệnh (*p)++ là tăng 1 đơn vị cho biến nguyên
xác định bởi con trỏ p. Lệnh p++ là tăng địa chỉ của con trỏ
p, tức là con trỏ p sẽ trỏ vào 2 byte nhớ tiếp theo. Vậy lệnh
p++ chỉ sử dụng khi dùng p để trỏ vào không phải là 1 số
nguyên mà là một dãy các số nguyên chứa trong một dãy
các biến động (các bytes nhớ) liên tiếp nhau.
Biến con trỏ có 2 cách sử dụng trong chơng trình:
- Sử dụng con trỏ để chứa địa chỉ của biến.
- Sử dụng con trỏ để xin cấp phát bộ nhớ động.
Ví dụ 1 sau đây nói rằng khi có khai báo biến con
trỏ int *p; thì việc sử dụng biến *p giống nh việc sử dụng
một biến nguyên nào đó một cách bình thờng.
Ví dụ 1 (contro1.cpp)
#include <iostream.h>
#include <conio.h>

void main() {
int *p;
*p=10;
cout<<*p<<endl;
(*p)++;
cout<<*p<<endl;
getch();
}
7.2. Sử dụng con trỏ để chứa địa chỉ của biến.
Nếu ta có khai báo
15
Lập trình hớng đối tợng với C++
int x;
int *p;
Thì sau khi gán lệnh
p=&x; // nghĩa là con trỏ p chứa địa chỉ của biến x
ta sẽ có đẳng thức *p = x
Ví dụ 2:
#include <stdio.h>
#include <conio.h>
main() {
int x=10;
int *p;
p=&x;
printf("x= %d", *p); // ket qua x = 10
*p+=20;
printf("x= %d", *p); // ket qua x = 20
getch();
}
7.3. Sử dụng con trỏ để xin cấp phát và quản lý dãy

biến động
Ví dụ 3: (contro2.cpp)
#include <iostream.h>
#include <conio.h>
void main() {
int *p;
int i,n=5;
p=new int[n];
int *q;
q=p;
for(i=0;i<n;i++)
{ cout<<"phan tu thi "<<i+1<<": ";
cin>>*q;
q++;
}
q=p;
for(i=0;i<n;i++)
16
Nguyễn Văn Tân

{ cout<<*q<<" ";
q++;
}
delete [] p;
getch();
}
Ví dụ 3 là một cách đơn giản tạo ra một "danh sách
liên kết" các biến động mà biến động đầu danh sách đợc
trỏ bởi con trỏ p. Khi duyệt trên danh sách này luôn sử
dụng thêm một biến con trỏ phụ để chạy trên các biến

động, con trỏ p phải đợc giữ nguyên có vai trò lu giữ địa
chỉ của danh sách. Ta gọi là danh sách liên kết để tạo ra
một cảm giác các biến động có liên kết với nhau. Về mặt
bản chất đó là các ô nhớ liên tục đợc cấp phát động, chứ
không có liên kết gì cả. Lúc chạy chơng trình, phép tăng
địa chỉ q++ làm cho q chỉ vào 2 bytes tiếp theo nghĩa là
truy xuất đến biến động tiếp theo tơng ứng.
8. Sử dụng tham số truyền ngầm định trong
hàm
C++ còn mạnh hơn C ở chỗ nó cho phép khởi tạo
mặc định cho các tham số truyền cho hàm. Ta gọi tắt các
tham số có khởi tạo giá trị mặc định là các tham số ngầm
định.
Khi có tham số ngầm định truyền cho hàm, có 2 tr-
ờng hợp sau xảy ra lúc gọi hàm:
+ Nếu lời gọi hàm khuyết các tham số ngầm định thì
các giá trị ngầm định đợc sử dụng.
+ Nếu lời gọi hàm có mặt các tham số ngầm định thì
các giá trị mới sẽ đợc sử dụng thay vì sử dụng các giá trị
mặc định.
Ví dụ 1:
void f(int i = 100);
void main()
17
Lập trình hớng đối tợng với C++
{ f(); // sử dụng i =100
f(200) ; // sử dụng i = 200
}
void f(int i)
{ printf("i = %d ", i);

}
Khi dùng các tham số ngầm định, có 2 nguyên tắc
sau đây:
+ Các tham số ngầm định phải nằm ở cuối danh sách
tham số.
+ Khi gọi hàm khuyết các tham số ngầm định thì
phải khuyết từ phải sang trái và các tham số đợc gọi cần
liên tục không đợc ngắt quãng.
Ví dụ 2:
- Khai báo sau đây là sai:
void g(int a = 1, int b, int c = 3, int d = 4); vì các
tham số ngầm định phải khai báo cuối danh sách tham số.
- Khai báo sau đây là đúng:
void g(int a, int b=2, int c = 3, int d = 4);
- Với khai báo đúng trên, xét các lời gọi hàm sau
đây:
g(); // sai vì a không có giá trị mặc định
g(10,20,30,40); // đúng vì đầy đủ tham số
g(10,20,,40); // sai vì sử dụng ngắt quãng
g(10); // đúng và nhận các giá trị mặc định
của b,c,d là 2,3,4
g(10,20) ; // đúng và nhận các giá trị mặc định
của c, d là 3,4
Trong khi khai báo các hàm thành phần của lớp, đặc
biệt là constructor (constructor) ngời ta rất hay sử dụng các
tham số ngầm định.
18
Nguyễn Văn Tân

9. Cấp phát và giải phóng bộ nhớ

Trớc hết về cú pháp của lệnh cấp phát và giải phóng
bộ nhớ, C++ đã cung cấp thêm 2 lệnh mới ngắn gọn và dễ
dùng hơn là new và delete.
Xét ví dụ sau đây:
Trong C Trong C++
char *s;
char tg[100];
int len;
/* Cấp phát bộ nhớ cho con trỏ s
kiểu char */ /* để có thể chứa
đợc dãy ký tự trong */
/* chuỗi tg */
gets(tg); len=strlen(tg);
s=(char*) malloc
((len+1)*sizeof(char));
strcpy(s,tg);
free(s); /* Giải phóng bộ nhớ
*/
char *s;
char tg[100];
gets(tg);
/* cấp phát bộ nhớ cho
con trỏ s kiểu char để
chứa giá trị của chuỗi tg
*/
int len=strcpy(tg);
s = new char[len]
strcpy(s,tg);
delete s; // Giải phóng bộ
nhớ

Trong ví dụ trên toán tử new xin cấp phát một vùng
nhớ có số bytes là: len*sizeof(char). Nếu không đủ bộ nhớ
cấp phát thì new trả lại giá trị của con trỏ NULL. Đây là
giá trị cho phép ngời lập trình sử dụng để kiểm tra toán tử
new có thực hiện thành công hay không.
Mặc dù new và malloc đều dùng để chỉ đến địa chỉ
đầu của vùng nhớ xin cấp phát nhng giữa chúng vẫn có
điểm khác biệt.
Ví dụ 1:
#include <alloc.h>
// cần thiết cho sử dụng malloc
void main()
19
Lập trình hớng đối tợng với C++
{ int* ip = new int[0];
//trả lại giá trị không xác định của con trỏ
int* ip = (int*) malloc (0);
// trả lại giá trị NULL pointer.
}
Qua ví dụ trên ta thấy toán tử new trả lại giá trị khác
0 của con trỏ thậm chí ta không đòi hỏi phải cấp phát bất
cứ một bytes nào trong bộ nhớ. Giá trị này của con trỏ nếu
đợc sử dụng cho mục đích khác sẽ sinh lỗi trong chơng
trình. Ngợc lại hàm malloc trong trờng hợp này trả lại giá
trị NULL, điều này có nghĩa là con trỏ cha trỏ đến bất cứ vị
trí nào trong bộ nhớ.
Cũng cần lu ý là trong C++, hàm malloc có kiểu trả
lại là void* do đó khi sử dụng phải thực hiện việc chuyển
đổi kiểu tơng ứng với kiểu của đối tợng xin cấp phát bộ
nhớ.

Ngoài hai khác biệt trên về bản chất cả new và
malloc đều dùng để cấp phát bộ nhớ động cho các biến
trong quá trình thực hiện chơng trình và con trỏ trả lại theo
hai cách này khi thành công đều không có gì khác biệt.
Trong lập trình C++, toán tử new đợc a dùng vì nó
mềm dẻo do khả năng định nghĩa chồng các toán tử của
lớp.
10. Các hàm tải bội (Overloaded function)
C++ đa ra một khả năng hoàn toàn mới và rất mạnh
so với C đó là khả năng định nghĩa chồng các hàm, nghĩa
là cho phép định nghĩa các hàm thực hiện các chức năng
khác nhau nhng có cùng một tên. Một hàm đợc định nghĩa
chồng còn gọi là hàm tải bội (dịch từ cụm từ overloading).
Ngoài hàm tải bội, C++ còn cho phép toán tử tải bội
để bình thờng hóa các toán tử quen thuộc đối với một kiểu
dữ liệu mới do ngời lập trình định nghĩa.
20
Nguyễn Văn Tân

Khi các hàm đợc tải bội thì phải tuân theo 2 nguyên
tắc sau đây:
+ Các hàm phải khác nhau về số lợng tham số hoặc
kiểu dữ liệu của các tham số.
+ Kiểu giá trị của hàm không cho phép phân biệt các
hàm đợc tải bội nếu các hàm này có danh sách tham số nh
nhau.
Khi gọi các hàm tải bội, C++ sẽ đối sánh danh sách
tham số và kiểu thích hợp với các tham số thực sự để cho
thực hiện hàm thích hợp trong số các hàm tải bội.
Ví dụ 1: Tải bội hàm abs để tính giá trị tuyệt đối của

một số bất kỳ:
int abs(int i);
long abs(long l);
double abs(double d);
Khi đó, xét lời gọi các hàm sau đây:
abs(-10); // gọi hàm int abs(int i);
abs(-100000); // gọi hàm long abs(long l);
abs(-34.12); // gọi hàm double abs(double d);
abs('a'); // gọi hàm int abs(int i);
Ví dụ 2: Tải bội hàm tự định nghĩa display để hiển
thị một giá trị có kiểu đơn giản bất kỳ:
void Display(char *string) {puts(string); }
void Display(long value) { printf("%ld",value); }
void Display(double value) { printf("%lf",value); }
void main(){ Display("\n Hello Mumy");
Display(123456789);
Display(3.1416);
Display(123); // sai }
21
Lập trình hớng đối tợng với C++
11. Hàm inline
11.1. Nhắc lại macro trong ngôn ngữ C
- Macro (dịch theo nghĩa thông thờng là vĩ mô) hiểu
theo nghĩa tin học là một lệnh riêng lẻ viết bằng một ngôn
ngữ lập trình nhng kết quả là một chuỗi lệnh bằng ngôn
ngữ máy tính.
- Trong ngôn ngữ C cho phép tạo ra các macro để
đúng nh ý nghĩa của nó: cho phép tạo ra một hằng hoặc
thậm chí tạo ra một hàm và nói chung tạo ra một tên thay
thế (tên macro) để ghép macro này nh một đoạn trình mã

máy vào chơng trình nguồn lúc thực hiện.
- Trớc hết macro đợc định nghĩa bằng chỉ thị #define
(và đợc hủy bỏ bằng #undef). Các chỉ thị #define (phép
thay thế lệnh), #include (phép chèn tệp), #if (phép lựa chọn
các dòng lệnh để biên dịch): đều là các chỉ thị tiền xử lý,
không phải là các câu lệnh thông thờng của chơng trình.
Khi một chơng trình C đợc biên dịch, trớc hết các chỉ thị
tiền xử lý sẽ chỉnh lý văn bản chơng trình nguồn, sau đó
bản chỉnh lý này mới đợc dịch. Các chỉ thị tiền xử lý, do đó
làm cho chơng trình ngắn gọn hơn, giúp cho việc tổ chức
biên dịch, gỡ rối chơng trình hiệu quả hơn.
- Có 2 loại macro: macro thay thế đơn giản và macro
thay thế theo đối (nh các hàm)
a)Macro đơn giản
#define tên_macro biểu_thức
có tác dụng thay thế tên_macro bằng biểu_thức
đứng sau nó. Khi biểu thức ký tự dài có thể thêm một dấu \
trớc khi xuống dòng. Trớc khi dùng lại tên_macro cho một
biểu_thức khác thì phải destructor nó bằng chỉ thị.
#undef tên_macro
Ví dụ 1:
22
Nguyễn Văn Tân

#include <stdio.h>
#define In printf
#define N 100
void main(){
int M = 200;
In("\nN = %d, M= %d",N,M);

#define M 300
In("\nN = %d, M= %d",N,M);
#undef M
#define M (N+400)
In("\nN = %d, M= %d",N,M); }
Chú ý:
(1) Khi định nghĩa biểu thức chứa phép toán cần đặt
biểu thức trong dấu ngặc đơn.
Ví dụ 3: nếu viết
#define P 3+5
thì định nghĩa này sẽ đúng với câu lệnh
Q=P;
sẽ không đúng với câu lệnh
Q=2*P; /* khi đó Q = 2*3 + 5
vậy cần phải định nghĩa là
#define (3+5)
(2) Khi định nghĩa một macro nh một đoạn chơng
trình thì phải viết đoạn chơng trình trong khối lệnh {... }
Ví dụ 4:
#define Check { printf("\nStop ! "); exit(1); }
void main(){
...
if (x > MAX_VALUE) Check;
}
b)Macro định nghĩa hàm
23
Lập trình hớng đối tợng với C++
Có thể dùng macro có đối để định nghĩa hàm. Khi
đó có một số điểm chú ý sau đây:
- Đối của macro có thể không cần khai báo kiểu cụ

thể.
- Tên hàm (macro) phải viết liền với dấu mở ngoặc
bắt đầu khai báo đối.
- Tất cả các đối hình thức trong thân hàm (macro)
phải viết trong ngoặc đơn.
Ví dụ 5:
#define MAX(A,B) (A)>(B)?(A):(B)
khi đó dòng lệnh:
x = max(p+q,r+s);
sẽ đợc thay thế bằng dòng lệnh
x=(p+q)>(r+s)?(p+q):(r+s);
Ví dụ 6:
#include "stdio.h"
#define tich(a,b) a*b
void main(){
int x=10, y=20, z=5;
printf("\ntich = %d ", tich(x+y,z)); }
Khi biên dịch, chơng trình sẽ biên dịch sẽ thay
tich(x+y,z) bằng x+y*z nh vậy sẽ không nhận đợc
tích (x+y)*z nh mong muốn. Vậy phải khai báo lại là:
#define tich(a,b) (a)*(b)
11.2. Hàm inline
Trong ngôn ngữ C++, hàm inline có cách hoạt động
giống nh một macro trong ngôn ngữ C. Nghĩa là các hàm
inline sẽ đợc thay thế trực tiếp thành dãy lệnh mã máy tại
chỗ gọi nó trong chơng trình lúc thực hiện.
Ưu điểm của macro và inline là ở chỗ nó cho phép
trình bầy chơng trình ngắn gọn và quan trọng là cho phép
thực hiện nhanh hơn các hàm thông thờng. Bởi vì mỗi lần
gọi hàm inline (hoặc macro), trình biên dịch sẽ ghép trực

24
Nguyễn Văn Tân

tiếp câu lệnh mã máy của nó tại vị trí gọi nó trong chơng
trình (mã máy) lúc thực hiện và không đòi hỏi các thủ tục
bổ sung khi gọi hàm và trả giá trị về, nói cách khác không
có cơ chế quản lý lời gọi và trả về (không cần lu ngữ cảnh)
nh đối với các hàm thông thờng.
Nhợc điểm của inline (và macro) là khi chúng quá
lớn và gọi thờng xuyên thì kích thớc chơng trình sẽ tăng
lên rất nhanh. Vì mỗi lần gọi inline (hoặc macro) thì các
chỉ thị tơng ứng sẽ đợc sinh ra (không có cơ chế lu ngữ
cảnh để giải phóng bộ nhớ) do đo chí phí lu trữ tăng lên khi
gọi hàm nhiều lần. Vậy inline và macro tiết kiệm thời gian
nhng không tiết kiệm bộ nhớ, cho nên thân các hàm inline
không nên chứa các cấu trúc lặp.
Việc sử dụng inline trong C++ tốt hơn macro trong
C ở chỗ hàm inline không cần phải viết các tham số trong
dấu ngoặc nh đối với các hàm mà macro mô tả.
Ví dụ nếu định nghĩa một macro
#define square(x) { x++*x++;}
thì lời gọi square (a) sẽ sinh ra biểu thức a++*a++
làm thay đổi giá trị của a tới hai lần và hơn nữa lời gọi
square(3) sẽ không đợc chấp nhận vì không đợc phép tăng
giảm đối với hằng số.
Hàm inline đợc định nghĩa và sử dụng nh một hàm
bình thờng, điểm khác nhau duy nhất là phải đặt mô tả
inline trớc khai báo hàm.
Cuối cùng, cần nhớ rằng giống nh macro, đặc tả
inline là một yêu cầu chứ không phải là một chỉ thị. Nếu vì

một lý do nào đó mà trình biên dịch không đáp ứng đợc
yêu cầu của inline (chẳng hạn nh bên trong inline có cấu
trúc lặp) thì yêu cầu của inline bị bỏ qua và nó đợc biên
dịch nh một hàm bình thờng (đây cũng là một điểm tiến bộ
của inline so với macro).
Ví dụ:
25

×