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.
Trang 1
Trang 2
Trang 3
Trang 4
Trang 5
Trang 6
Trang 7
Trang 8
Trang 9
Trang 10
Tải về để xem bản đầy đủ
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)
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:
- giao_trinh_lap_trinh_huong_doi_tuong_c_phan_1.pdf