Giáo trình Lập trình hướng đối tượng C++ (Phần 1)

CodeBlocks

Trước tiên, chúng ta sẽ tìm hiểu cách tạo dự án, biên dịch một tập tin C++

trên CodeBlocks. Độc giả cũng cần lưu ý rằng, CodeBlocks tổ chức công việc

theo các dự án. Chúng ta có thể biên dịch từng tập tin cpp một c|ch đơn lẻ.

Tuy nhiên, làm việc theo dự án sẽ giúp ích cho chúng ta rất nhiều khi làm

việc với những tác vụ lớn.

Đầu tiên chúng ta khởi động codeblocks, sau đó v{o File > New > Project.

Trong hộp thoại hiện ra, chúng ta chọn console application (Hình 1).

Và nhấp Go, sau đó nhấp Next. Trong hộp thoại tiếp theo, ta chọn C++ và

nhấp Next

CodeBlocks

Trước tiên, chúng ta sẽ tìm hiểu cách tạo dự án, biên dịch một tập tin C++

trên CodeBlocks. Độc giả cũng cần lưu ý rằng, CodeBlocks tổ chức công việc

theo các dự án. Chúng ta có thể biên dịch từng tập tin cpp một c|ch đơn lẻ.

Tuy nhiên, làm việc theo dự án sẽ giúp ích cho chúng ta rất nhiều khi làm

việc với những tác vụ lớn.

Đầu tiên chúng ta khởi động codeblocks, sau đó v{o File > New > Project.

Trong hộp thoại hiện ra, chúng ta chọn console application (Hình 1).

Và nhấp Go, sau đó nhấp Next. Trong hộp thoại tiếp theo, ta chọn C++ và

nhấp Next

iên dịch chương trình:

+ Nhấp vào Build > Build and Run.

+ Hoặc nhấp phím F9.

Tự động định dạng mã. Khi viết một chương trình C++ hay bất kì một

chương trình trên một ngôn ngữ lập trình nào

khác, ta cần tuân thủ quy phạm định dạng mã

nguồn. Có nhiều chuẩn mực cho c|c định dạng

mã nguồn: chuẩn Hungary, chuẩn lạc đ{. Dù

rằng, chúng không ảnh hưởng đến việc biên dịch

chương trình, nhưng chúng giúp người đọc có

thể dễ hiểu chương trình của chúng ta hơn. Nếu

ta không nắm vững các quy phạm này thì có thể

sử dụng chức năng định dạng mã nguồn tự động

của CodeBlocks. Hãy kích chuột phải vào vùng soạn thảo, sau đó chọn

Format this file (Astyle).

Tự động khởi tạo phần thân các phương thức của lớp. Để hỗ trợ cho

việc soạn thảo, CodeBlocks cũng hỗ trợ chức năng khởi tạo nhanh mã

nguồn. Để khởi tạo nhanh phần khai b|o th}n phương thức của lớp từ khai

báo prototype của nó, chúng ta đặt trỏ chuột vào sau khai báo lớp (tức vị trí

sẽ chèn khai b|o th}n phương thức), sau đó, kích chuột phải, chọn Insert >

All class methods without implementation.

Giáo trình Lập trình hướng đối tượng C++ (Phần 1) trang 1

Trang 1

Giáo trình Lập trình hướng đối tượng C++ (Phần 1) trang 2

Trang 2

Giáo trình Lập trình hướng đối tượng C++ (Phần 1) trang 3

Trang 3

Giáo trình Lập trình hướng đối tượng C++ (Phần 1) trang 4

Trang 4

Giáo trình Lập trình hướng đối tượng C++ (Phần 1) trang 5

Trang 5

Giáo trình Lập trình hướng đối tượng C++ (Phần 1) trang 6

Trang 6

Giáo trình Lập trình hướng đối tượng C++ (Phần 1) trang 7

Trang 7

Giáo trình Lập trình hướng đối tượng C++ (Phần 1) trang 8

Trang 8

Giáo trình Lập trình hướng đối tượng C++ (Phần 1) trang 9

Trang 9

Giáo trình Lập trình hướng đối tượng C++ (Phần 1) trang 10

Trang 10

Tải về để xem bản đầy đủ

pdf 119 trang duykhanh 5800
Bạn đang xem 10 trang mẫu của tài liệu "Giáo trình Lập trình hướng đối tượng C++ (Phần 1)", để tải tài liệu gốc về máy hãy click vào nút Download ở trên

Tóm tắt nội dung tài liệu: Giáo trình Lập trình hướng đối tượng C++ (Phần 1)

Giáo trình Lập trình hướng đối tượng C++ (Phần 1)
 thất bại v{ ngược lại thì thành công. Đ}y l{ c|ch 
làm thủ công, chúng ta có thể sử dụng cấu trúc try catch để xử lý tình 
Chương 10. Bộ nhớ động 
T r a n g | 109 
C
+
+
huống này. Chi tiết, chúng ta sẽ thảo luận kĩ hơn trong phần sau. Lưu ý rằng, 
hai ngoại lệ này nằm trong thư viện . 
Toán tử delete và delete[] 
Nếu khi biến trỏ không cần dùng đến nữa, chúng ta có thể xóa bỏ các 
ô nhớ của nó để giải phóng bộ nhớ động. Qu| trình n{y được thực thi bởi 
toán tử delete. 
delete num; 
delete[] nums; 
Cách viết thứ nhất dùng để giải phóng con trỏ đơn tầng. Cách thứ hai 
dùng cho con trỏ đa tầng (hai tầng trở lên). 
Toán tử delete chỉ có tác dụng với con trỏ được khởi tạo bởi toán tử 
new hoặc con trỏ null. 
Chương trình Kết quả 
#include 
#include 
using namespace std; 
int main() 
{ 
 double *ptr[ 5]; 
 for ( int i = 0; i < 5; i++ ) 
 { 
 ptr[ i ] = new (nothrow) double[ 50000000 ]; 
 if (ptr[i]!=0) 
 cout << "Allocation is ok"; 
 else 
 cout<<”Allocation fail”; 
 } 
 delete[] ptr; 
 return 0; 
} 
Allocation is ok 
Allocation is ok 
Allocation is ok 
Allocation is ok 
Allocation fail 
Trong ANSI-C, hàm malloc, calloc, realloc v{ free được dùng thay cho 
new và delete và hiển nhiên chúng cũng hoạt động trên C++. Chúng ta 
không thảo luận chi tiết về h{m n{y, vì theo xu hướng của lập trình C++ 
hiện đại, người ta sử dụng hai toán tử new và delete mà ta thảo luận ở trên. 
Chương 11. Kiểu dữ liệu struct và Con trỏ struct 
Trang | 110 
CHƯƠNG 11. KIỂU DỮ LIỆU STRUCT VÀ CON 
TRỎ STRUCT 
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 kiể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 
Một dữ liệu struct là một nhóm các phần tử có thể có kiểu dữ liệu 
kh|c nhau được đặt cùng một tên. Các phần tử dữ liệu n{y được gọi là các 
thành viên của struct. Cấu trúc khai b|o struct trong C++ như sau 
struct tên_struct{ 
 type thành_viên_1; 
 type thành_viên_2; 
} [tên_đối_tượng_struct]; 
Trong đó: 
struct là một từ khóa. 
tên_struct là tên kiểu dữ liệu struct m{ ta định nghĩa. 
thành_viên_1,  l{ c|c phần tử thành viên của struct. 
tên_đối_tượng_struct: là tên biến thuộc kiểu tên_struct. 
Hai c|ch khai b|o sau đ}y l{ tương đương. 
Ví dụ 1 Ví dụ 2 
struct product{ 
 int weight; 
 float price; 
} 
product apple; 
product banana, melon; 
struct product{ 
 int weight; 
 float price; 
}apple, banana, melon; 
Giải thích: trong hai ví dụ trên product là kiểu dữ liệu struct mà ta tạo ra. 
Nó gồm có hai th{nh viên l{ weight v{ price. Tương ứng với kiểu dữ liệu 
Chương 11. Kiểu dữ liệu struct và Con trỏ struct 
T r a n g | 111 
C
+
+
này, ta có các biến apple, banana, melon. Để khai báo nó, ta có thể sử dụng 
một trong hai c|ch theo như hai ví dụ này. Cần lưu ý rằng, các biến apple, 
banana, melon là các biến thuộc kiểu dữ liệu product. 
Để truy cập đến các thành viên của biến struct, ta sử dụng toán tử chấm (.). 
apple.weight = 200; 
apple.price = 2; 
banana.weight = 150; 
banana.price = 1; 
Chúng ta có thể hiểu apple.weight là khối lượng của táo, apple.price là 
giá tiền của táo... Có thể xem mỗi thành viên của một biến struct như là một 
biến độc lập mà ta có thể truy cập bằng cách 
tên_biến.phần_tử_thành_viên. Ta hoàn toàn có thể thực hiện các phép 
to|n tương ứng với mỗi dữ liệu th{nh viên n{y (nghĩa l{ c|c phép to|n đó 
tương ứng với các phép toán trên kiểu dữ liệu của các phần tử thành viên 
đó: nếu phần tử thành viên là kiểu nguyên thì ta có thể thực hiện các phép 
toán số nguyên với thành viên này, nếu phần tử thành viên là kiểu xâu thì 
có thể thực thi các phép toán với xâu cho biến thành viên này,). 
Ví dụ, tôi mua 400gam táo, và 300g chuối. Giá của mỗi 100g táo là 1$, và 
100g chuối là 0.7$. Cần tính toán số tiền mà tôi cần trả. 
Ví dụ 
apple.weight = 400; 
apple.price = (float)apple.weight*1/100; 
banana.weight = 300; 
banana.price = banana.weight*0.7/100; 
float money = apple.price+banana.price; 
Như trong ví dụ trên, tôi có thể thực hiện các phép toán số học với các phần 
tử thành viên là kiểu số như c|c biến số bình thường. 
Struct là một kiểu dữ liệu do người dùng định nghĩa (nhờ vào từ khóa 
struct – từ khóa struct viết thường). Ta cũng có thể khai báo một mảng các 
phần tử thuộc kiểu struct. Ví dụ sau đ}y minh họa cho việc mua bán khi đi 
siêu thị. 
Chương 11. Kiểu dữ liệu struct và Con trỏ struct 
T r a n g | 112 
C
+
+
Bài toán: Trong siêu thị, giá táo và chuối được ấn định trên từng sản phẩm 
tùy thuộc chất lượng của sản phẩm, không phụ thuộc vào khối lượng của nó. 
a. Tính khối lượng hàng hóa (kg) mà người tiêu dùng mua. 
b. Nếu khách hàng mua hàng trị giá trên 10$, thì người tiêu dùng sẽ được 
khuyến mãi. Hãy kiểm tra xem người tiêu dùng có được khuyến mãi 
hay không. 
Biết rằng số lượng táo và chuối do người tiêu dùng lựa chọn. 
Chương trình Kết quả 
#include 
using namespace std; 
#define MAX 10 
struct product{ 
 int weight; 
 float price; 
}apples[MAX], bananas[MAX]; 
/* 
Khai báo hàm 
*/ 
void buyProducts(product pd, string 
name, int &weight, float &price) 
{ 
 int i = 0; 
 cout<<”Buy “<<name<<endl; 
 while (true){ 
 cout<<”weight and price: <<endl; 
 cin>>pd[i].weight; 
 cin>>pd[i].price; 
 weight += pd[i].weight; 
 price += pd[i].price; 
 cout<<”Continue to buy “<<name<<” 
(y/n) ?”; 
 char yn; 
 cin>>yn; 
 if((yn==’n’)||(yn==’N’)) break; 
 else i++; 
 } 
} 
int main() 
{ 
Buy apples 
weight and price 
100 
2 
Continue to buy apples (y/n) ?y 
weight and price 
200 
3 
Continue to buy apples (y/n) ?n 
Buy bananas 
weight and price 
150 
1 
Continue to buy bananas (y/n) 
?y 
weight and price 
250 
3 
Continue to buy bananas (y/n) 
?n 
Total weight: 0.7 
No Promotion 
Chương 11. Kiểu dữ liệu struct và Con trỏ struct 
T r a n g | 113 
C
+
+
 int weight = 0; 
 float price = 0; 
 buyProducts(apples, “apples”, weight, 
price); 
 buyProducts(bananas, “bananas”, 
weight, price); 
 cout<<”Total weight: 
“<<(float)weight/1000<<endl; 
 if(price>10) 
 cout<<”Promotion”; 
 else 
 cout<<”No Promotion”; 
 return 0; 
} 
Giải thích: 
Mảng apples và bananas thuộc kiểu dữ liệu product. Hàm 
buyProducts dùng để tính toán khối lượng và tổng giá hàng hóa mỗi loại 
mua được. Nếu tham số products trong hàm là apples, thì nó sẽ tính tương 
ứng với khối lượng tổng cộng và tổng đơn giá của apples. Tương tự cho 
bananas. Ta sử dụng vòng lặp while vì ta không biết chính xác số lượng 
h{ng hóa m{ người dùng lựa chọn. Tuy nhiên, con số tổng không thể vượt 
hằng số MAX m{ ta đ~ định nghĩa. Biến weight v{ price được truyền theo 
tham biến, do đó, trước phép cộng dồn (toán tử cộng đồng nhất +=), ta 
không cần khởi tạo giá trị 0 cho chúng, ta có thể khởi tạo cho chúng ngay 
đầu hàm main. Nếu khởi tạo đầu hàm buyProducts, thì ta chỉ có thể tính 
được khối lượng và giá của từng loại một mà thôi (khi đó, nếu muốn tính 
toán cho cả hai sản phẩm, ta bổ sung thêm biến để lưu lại các giá trị này). 
Sau khi gọi hàm này hai lần, tương ứng với apples và bananas, thì biến 
weight lưu lại tổng khối lượng của cả hai sản phẩm, biến price lưu lại giá 
của cả hai sản phẩm. Khối lượng được quy đổi sang kg, nên ta sẽ chia cho 
1000. Nếu quy định đơn vị là kg, thì điều này là không cần thiết. 
Trong phần thực thi chương trình, người tiêu dùng đ~ mua hai quả 
táo và hai quả chuối. Quả táo thứ nhất có khối lượng 100g và giá 2$, quả táo 
thứ hai – 200g và 3$. Quả chuối thứ nhất có khối lượng 150g và giá 1$, quả 
chuối thứ hai – 250g và 3$. 
Chương 11. Kiểu dữ liệu struct và Con trỏ struct 
T r a n g | 114 
C
+
+
Con trỏ struct 
Tương ứng với mảng struct, 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 con trỏ struct: có hai cách khai báo 
struct product{ 
 int weight; 
 float price; 
}*apples; 
struct product{ 
 int weight; 
 float price; 
}; 
product* apples; 
Tham chiếu: ho{n to{n tương tự như con trỏ trỏ vào kiểu dữ liệu nguyên 
thủy. 
struct product{ 
 int weight; 
 float price; 
}apples1; 
products* apples2; 
apples2 = &apples1; 
Truy cập giá trị của phần tử thành viên: có hai cách truy cập 
apples2->weight 
apples2->price 
*(apples2.weight) 
*(apples2.price) 
Ta cần lưu ý sự khác nhau giữa apples2->weight và *apples2.weight. 
Trường hợp đầu nó sẽ tương đương với *(apples2.weight) v{ theo độ ưu 
tiên của toán tử, dấu () sẽ thực hiện trước tiên, nghĩa l{ th{nh viên weight 
của apples2. Tiếp đó l{ phép to|n *. Điều này có thể hiểu là con trỏ apples2 
trỏ v{o địa chỉ của biến thành viên weight của apples 2. Trường hợp thứ 
hai, là giá trị trỏ bởi biến thành viên weight của apples2 (toán tử . có độ ưu 
tiên cao hơn to|n tử *). 
struct product{ 
 int weight; 
 float price; 
}*apples2; 
. 
apples2->weight 
struct product{ 
 int *weight; 
 float price; 
}apples2; 
*apples2.weight 
Chương 11. Kiểu dữ liệu struct và Con trỏ struct 
T r a n g | 115 
C
+
+
Struct lồng nhau 
Struct có thể được khai báo lồng nhau. Chúng ta có thể khai báo lồng bên 
trong, hoặc khai báo tuần tự 
Khai báo lồng nhau Khai báo tuần tự 
struct family{ 
 string father; 
 string mother; 
 struct children{ 
 string son; 
 string daughter; 
 }first, second; 
}myfamily; 
struct children{ 
 string son; 
 string daughter; 
}; 
struct family{ 
 string father; 
 string mother; 
 children first, second; 
}myfamily; 
Khi đó, việc truy cập cũng tương tự như trên 
myfamily.first.son 
. 
Nếu là con trỏ, ta cũng có c|ch truy cập tương tự. 
struct family{ 
 string father; 
 string mother; 
 struct children{ 
 string son; 
 string daughter; 
 }*first, second; 
}*myfamily; 
myfamily->first->son 
myfamily->second.son 
Kích thước bộ nhớ của struct 
Kích thước của struct theo lý thuyết nó sẽ l{ kích thước tổng cộng của 
các dữ liệu thành viên. Tuy nhiên, theo cách thức tổ chức bộ nhớ, các dữ 
liệu thành viên của một struct sẽ được sắp xếp liền kề nhau. Việc tổ chức bộ 
nhớ của hệ thống máy tính 32bit sẽ theo xu hướng là nhóm 4 bytes. Điều 
n{y có nghĩa l{, nếu dữ liệu thành viên thứ nhất đ~ lấp vào một số byte bộ 
nhớ, và nếu còn thừa, thì hệ điều hành sẽ xem xét để đưa dữ liệu thành viên 
tiếp theo vào. Nếu dữ liệu thành viên tiếp theo chiếm một số lượng các byte 
bộ nhớ còn thừa, thì nó sẽ được lấp vào phần bộ nhớ còn thừa đó. Nhưng 
nếu vượt quá, thì hệ thống sẽ được định hướng bổ sung thêm một nhóm 4 
byte bộ nhớ mới để chứa dữ liệu thành viên tiếp theo (nếu cần nhiều hơn 
Chương 11. Kiểu dữ liệu struct và Con trỏ struct 
T r a n g | 116 
C
+
+
thì cung cấp tiếp nhóm 4 byte nữa). Còn đối với số byte còn thừa, nó vẫn 
được để trống. 
Ví dụ 1 Ví dụ 2 
struct number{ 
char a; 
int b; 
char c; 
}mynum; 
cout<<sizeof(mynum); 
//12 
struct number{ 
char a; 
char c; 
int b; 
}mynum; 
cout<<sizeof(mynum); 
//8 
Giải thích: 
Trong ví dụ 1: 
char – 1byte 
int – 4byte 
Đầu tiên, dữ liệu thành viên char sẽ 
lấp đầy nhóm 4byte, char chiếm 
1byte nên còn thừa 3 byte. Dữ liệu 
thành viên tiếp theo là int. Vì int 
chiếm 4byte, nhưng chỉ thừa có 3 
byte, nó không đủ chỗ cho int. Do đó, 
hệ thống sẽ tự động cung cấp thêm 3 
byte để lấp đầy phần còn thừa. Tiếp 
theo, sẽ cung cấp nhóm 4 byte cho 
int. Không có byte thừa trong trường 
hợp này. Kiểu dữ liệu thành viên 
char còn lại chiếm 1byte, nhưng hệ 
thống cung cấp cho nó 4 byte, còn 
thừa 3 byte. Số byte thừa này sẽ 
được lấp đầy. Như vậy, số byte trong 
trường hợp này là 
(1+3)+4+(1+3)=12 byte. 
Trong ví dụ 2: 
Cũng tương tự ví dụ 1. Dữ liệu thành 
viên char thứ nhất chiếm 1 byte, hệ 
thống cung cấp 4 byte, như vậy thừa 
3 byte. Kiểu dữ liệu thành viên tiếp 
theo cũng kiểu char, chiếm 1byte, nó 
có thể lấp vào số byte còn thừa. Tại 
thời điểm này, còn thừa 2 byte. Tiếp 
theo, ta xét đến dữ liệu thành viên 
int. Dữ liệu này chiếm 4 byte, nhưng 
chỉ còn thừa 2 byte, do đó hệ thống 
sẽ lấp đầy 2 byte này và cung cấp 
thêm 4 byte mới cho int. Như vậy, số 
byte trong trường hợp này là 
(1+1+2)+4=8 byte. 
Như vậy, tùy vào kiểu dữ liệu thành viên, ta cần chọn một cách sắp xếp hợp 
lý để có một kết quả tối ưu về bộ nhớ. Hãy quan sát lược đồ sắp xếp bộ nhớ 
hướng tối ưu như bên dưới (trường hợp 2). 
char char int 
Chương 12. C|c kiểu dữ liệu khác 
Trang | 117 
CHƯƠNG 12. CÁC KIỂU DỮ LIỆU KHÁC 
Kiểu dữ liệu tự định nghĩa 
Ta có thể định nghĩa một kiểu dữ liệu mới thông qua một kiểu dữ liệu 
đ~ tồn tại 
typedef kiểu_dữ_liệu_tồn_tại tên_kiểu_dữ_liệu_mới; 
Sau khi khai báo kiểu dữ liệu mới, ta có thể sử dụng nó như kiểu dữ liệu 
thông thường 
typedef char C; 
typedef char* pchar; 
typedef char string50[50]; 
C mychar; 
pchar p; 
string50 str; 
Kiểu dữ liệu union thường 
Với từ khóa union, ta có thể khai báo một kiểu dữ liệu mới từ các kiểu 
dữ liệu đ~ tồn tại. 
union Tên_kiểu_union{ 
 kiểu_dữ_liệu_thành_viên_1 tên_thành_viên_1; 
 kiểu_dữ_liệu_thành_viên_2 tên_thành_viên_2; 
}biến_kiểu_dữ_liệu_union; 
Khi khái báo kiểu union, các dữ liệu thành viên sẽ được khai báo trên cùng 
một không gian bộ nhớ vật lý. Kích thước của nó l{ kích thước của kiểu dữ 
liệu thành viên lớn nhất. 
Ví dụ 
union myType{ 
 char C; 
 double Db; 
} newType; 
//8 byte, vì double = 8byte, char = 1byte. 
Chương 12. C|c kiểu dữ liệu khác 
T r a n g | 118 
C
+
+
//Sử dụng 
biến_kiểu_dữ_liệu_union.tên_thành_viên 
Kiểu dữ liệu union ẩn danh 
Ho{n to{n tương tự với kiểu dữ liệu union thường, nhưng ta không 
cần chỉ định tên biến union mới tạo ra 
union Tên_kiểu_union{ 
 kiểu_dữ_liệu_thành_viên_1 tên_thành_viên_1; 
 kiểu_dữ_liệu_thành_viên_2 tên_thành_viên_2; 
}; 
Khi khai báo biến thuộc kiểu union ẩn danh, ta có thể khai b|o như sau 
Ví dụ 
union Tên_kiểu_union tên_biến; 
tên_biến.tên_thành_viên 
Kiểu dữ liệu enum 
Kiểu dữ liệu enum được tạo mới có thể chứa nhiều dữ liệu khác nhau 
mà không có sự giới hạn về giá trị. 
enum Tên_kiểu_enum{ 
 giá_trị_1; 
 giá_trị_2; 
}Tên_thể_hiện_enum; 
Chúng ta có thể tham khảo thêm các ví dụ sau đ}y 
enum colors{black=0, blue, green, cyan, red} mycolor; 
enum colors{black=0, blue, green, cyan, red}; 
colors mycolor; 
 Enum là một loại dữ liệu chứa các biến được đ|nh chỉ số. Do đó, các giá trị 
hằng của nó luôn là số nguyên (để làm chỉ số). 
Chương 12. C|c kiểu dữ liệu khác 
T r a n g | 119 
C
+
+
Ví dụ 
#include 
using namespace std; 
enum colors{red, green, blue}; 
int main() 
{ 
 for(int i=0; i<3; i++) 
 if (colors(i)==green) cout<<"green"<<endl; 
 return 0; 
} 

File đính kèm:

  • pdfgiao_trinh_lap_trinh_huong_doi_tuong_c_phan_1.pdf