intTypePromotion=1
zunia.vn Tuyển sinh 2024 dành cho Gen-Z zunia.vn zunia.vn
ADSENSE

CHƯƠNG 2 CÁC MỞ RỘNG CỦA C++

Chia sẻ: Hiếu Đại Ca | Ngày: | Loại File: PDF | Số trang:33

98
lượt xem
4
download
 
  Download Vui lòng tải xuống để xem tài liệu đầy đủ

Vào những năm đầu thập niên 1980, người dùng biết C++ với tên gọi "C with Classes" được mô tả trong hai bài báo của Bjarne Stroustrup (thuộc AT&T Bell Laboratories) với nhan đề "Classes: An Abstract Data Type Facility for the C Language" và "Adding Classes to C : AnExercise in Language Evolution".

Chủ đề:
Lưu

Nội dung Text: CHƯƠNG 2 CÁC MỞ RỘNG CỦA C++

  1. CHƯƠNG 2 CÁC MỞ RỘNG CỦA C++ I. LỊCH SỬ CỦA C++ Vào những năm đầu thập niên 1980, người dùng biết C++ với tên gọi "C with Classes" được mô tả trong hai bài báo của Bjarne Stroustrup (thuộc AT&T Bell Laboratories) với nhan đề "Classes: An Abstract Data Type Facility for the C Language" và "Adding Classes to C : AnExercise in Language Evolution". Trong công trình này, tác giả đã đề xuất khái niệm lớp, bổ sung việc kiểm tra kiểu tham số của hàm, các chuyển đổi kiểu và một số mở rộng khác vào ngôn ngữ C. Bjarne Stroustrup nghiên cứu mở rộng ngôn ngữ C nhằm đạt đến một ngôn ngữ mô phỏng (simulation language) với những tính năng hướng đối tượng. Trong năm 1983, 1984, ngôn ngữ "C with Classes" được thiết kế lại, mở rộng hơn rồi một trình biên dịch ra đời. Và chính từ đó, xuất hiện tên gọi "C++". Bjarne Stroustrup mô tả ngôn ngữ C++ lần đầu tiên trong bài báo có nhan đề "Data Abstraction in C". Sau một vài hiệu chỉnh C++ được công bố rộng rãi trong quyển "The C++ Programming Language" của Bjarne Stroustrup xuất hiện đánh dấu sự hiện diện thực sự của C++, người lập tình chuyên nghiệp từ đây đã có một ngôn ngữ đủ mạnh cho các dữ án thực tiễn của mình. Về thực chất C++ giống như C nhưng bổ sung thêm một số mở rộng quan trọng, đặc biệt là ý tưởng về đối tượng, lập trình định hướng đối tượng.Thật ra các ý tưởng về cấu trúc trong C++ đã xuất phát vào các năm 1970 từ Simula 70 và Algol 68. Các ngôn ngữ này đã đưa ra các khái niệm về lớp và đơn thể. Ada là một ngôn ngữ phát triển từ đó, nhưng C++ đã khẳng định vai trò thực sự của mình. II. CÁC MỞ RỘNG CỦA C++ II.1. Các từ khóa mới của C++ Để bổ sung các tính năng mới vào C, một số từ khóa (keyword) mới đã được đưa vào C++ ngoài các từ khóa có trong C. Các chương trình bằng C nào sử dụng các tên trùng với các từ khóa cần phải thay đổi trước khi chương trình được dịch lại bằng C++. Các từ khóa mới này là : asm catch class delete friend inline new operator private protected public template this throw try virtual II.2. Cách ghi chú thích C++ chấp nhận hai kiểu chú thích. Các lập trình viên bằng C đã quen với cách chú thích bằng /*…*/. Trình biên dịch sẽ bỏ qua mọi thứ nằm giữa /*…*/. Ví dụ 2.1: Trong chương trình sau :
  2. Mọi thứ nằm giữa /*…*/ từ dòng 1 đến dòng 3 đều được chương trình bỏ qua. Chương trình này còn minh họa cách chú thích thứ hai. Đó là cách chú thích bắt đầu bằng // ở dòng 8 và dòng 9. Chúng ta chạy ví dụ 2.1, kết quả ở hình 2.1. Hình 2.1: Kết quả của ví dụ 2.1 Nói chung, kiểu chú thích /*…*/ được dùng cho các khối chú thích lớn gồm nhiều dòng, còn kiểu // được dùng cho các chú thích một dòng. II.3. Dòng nhập/xuất chuẩn Trong chương trình C, chúng ta thường sử dụng các hàm nhập/xuất dữ liệu là printf() và scanf(). Trong C++ chúng ta có thể dùng dòng nhập/xuất chuẩn (standard input/output stream) để nhập/xuất dữ liệu thông qua hai biến đối tượng của dòng (stream object) là cout và cin. Ví dụ 2.2: Chương trình nhập vào hai số. Tính tổng và hiệu của hai số vừa nhập.
  3. Để thực hiện dòng xuất chúng ta sử dụng biến cout (console output) kết hợp với toán tử chèn (insertion operator) > như ở các dòng 6 và 8. Khi sử dụng cout hay cin, chúng ta phải kéo file iostream.h như dòng 1. Chúng ta sẽ tìm hiểu kỹ về dòng nhập/xuất ở chương 8. Chúng ta chạy ví dụ 2.2 , kết quả ở hình 2.2. Hình 2.2: Kết quả của ví dụ 2.2
  4. Hình 2.3: Dòng nhập/xuất dữ liệu II.4. Cách chuyển đổi kiểu dữ liệu Hình thức chuyển đổi kiểu trong C tương đối tối nghĩa, vì vậy C++ trang bị thêm một cách chuyển đổi kiểu giống như một lệnh gọi hàm. Ví dụ 2.3: Chúng ta chạy ví dụ 2.3 , kết quả ở hình 2.4.
  5. Hình 2.4: Kết quả của ví dụ 2.3 II.5. Vị trí khai báo biến Trong chương trình C đòi hỏi tất cả các khai báo bên trong một phạm vi cho trước phải được đặt ở ngay đầu của phạm vi đó. Điều này có nghĩa là tất cả các khai báo toàn cục phải đặt trước tất cả các hàm và các khai báo cục bộ phải được tiến hành trước tất cả các lệnh thực hiện. Ngược lại C++ cho phép chúng ta khai báo linh hoạt bất kỳ vị trí nào trong một phạm vi cho trước (không nhất thiết phải ngay đầu của phạm vi), chúng ta xen kẽ việc khai báo dữ liệu với các câu lệnh thực hiện. Ví dụ 2.4: Chương trình mô phỏng một máy tính đơn giản 1: #include 2: int main() 3: { 4: int X; 5: cout>X; 7: int Y; 8: cout>Y; 10: char Op; 11: coutOp; 13: switch(Op) 14: { 15: case ‘+’: 16: cout
  6. Hình 2.5: Kết quả của ví dụ 2.4 Khi khai báo một biến trong chương trình, biến đó sẽ có hiệu lực trong phạm vi của chương trình đó kể từ vị trí nó xuất hiện. Vì vậy chúng ta không thể sử dụng một biến được khai báo bên dưới nó. II.6. Các biến const Trong ANSI C, muốn định nghĩa một hằng có kiểu nhất định thì chúng ta dùng biến const (vì nếu dùng #define thì tạo ra các hằng không có chứa thông tin về kiểu). Trong C++, các biến const linh hoạt hơn một cách đáng kể: C++ xem const cũng như #define nếu như chúng ta muốn dùng hằng có tên trong chương trình. Chính vì vậy chúng ta có thể dùng const để quy định kích thước của một mảng như đoạn mã sau: const int ArraySize = 100; int X[ArraySize]; Khi khai báo một biến const trong C++ thì chúng ta phải khởi tạo một giá trị ban đầu nhưng đối với ANSI C thì không nhất thiết phải làm như vậy (vì trình biên dịch ANSI C tự động gán trị zero cho biến const nếu chúng ta không khởi tạo giá trị ban đầu cho nó). Phạm vi của các biến const giữa ANSI C và C++ khác nhau. Trong ANSI C, các biến const được khai báo ở bên ngoài mọi hàm thì chúng có phạm vi toàn cục, điều này nghĩa là chúng có thể nhìn thấy cả ở bên ngoài file mà chúng được định nghĩa, trừ khi chúng được khai báo là static. Nhưng trong C++, các biến const được hiểu mặc định là static. II.7. Về struct, union và enum Trong C++, các struct và union thực sự các các kiểu class. Tuy nhiên có sự thay đổi đối với C++. Đó là tên của struct và union được xem luôn là tên kiểu giống như khai báo bằng lệnh typedef vậy. Trong C, chúng ta có thể có đoạn mã sau : struct Complex { float Real; float Imaginary; }; ………………….. struct Complex C; Trong C++, vấn đề trở nên đơn giản hơn: struct Complex { float Real; float Imaginary; }; ………………….. Complex C;
  7. Quy định này cũng áp dụng cho cả union và enum. Tuy nhiên để tương thích với C, C++ vẫn chấp nhận cú pháp cũ. Một kiểu union đặc biệt được thêm vào C++ gọi là union nặc danh (anonymous union). Nó chỉ khai báo một loạt các trường(field) dùng chung một vùng địa chỉ bộ nhớ. Một union nặc danh không có tên tag, các trường có thể được truy xuất trực tiếp bằng tên của chúng. Chẳng hạn như đoạn mã sau: union { int Num; float Value; }; Cả hai Num và Value đều dùng chung một vị trí và không gian bộ nhớ. Tuy nhiên không giống như kiểu union có tên, các trường của union nặc danh thì được truy xuất trực tiếp, chẳng hạn như sau: Num = 12; Value = 30.56; II.8. Toán tử định phạm vi Toán tử định phạm vi (scope resolution operator) ký hiệu là ::, nó được dùng truy xuất một phần tử bị che bởi phạm vi hiện thời. Ví dụ 2.5 : 1: #include 2: int X = 5; 3: int main() 4: { 5: int X = 16; 6: cout
  8. int *P; P = malloc(sizeof(int)); if (P==NULL) printf("Khong con du bo nho de cap phat\n"); else { *P = 290; printf("%d\n", *P); free(P); } Trong C++, chúng ta có thể viết lại đoạn chương trình trên như sau: int *P; P = new int; if (P==NULL) cout
  9. Chúng ta có thể vừa cấp phát vừa khởi động như sau : int *P; P = new int(100); if (P!=NULL) { cout
  10. 12: cout
  11. Gọi X là mảng hai chiều có kích thước m dòng và n cột. A là mảng một chiều tương ứng. Nếu X[i][j] chính là A[k] thì k = i*n + j Chúng ta có chương trình như sau : 1: #include 2: #include 3: //prototype 4: void AddMatrix(int * A,int *B,int*C,int M,int N); 5: int AllocMatrix(int **A,int M,int N); 6: void FreeMatrix(int *A); 7: void InputMatrix(int *A,int M,int N,char Symbol); 8: void DisplayMatrix(int *A,int M,int N); 9: 10: int main() 11: { 12: int M,N; 13: int *A = NULL,*B = NULL,*C = NULL; 14: 15: clrscr(); 16: coutM; 18: coutN; 20: //Cấp phát vùng nhớ cho ma trận A 21: if (!AllocMatrix(&A,M,N)) 22: { //endl: Xuất ra kí tự xuống dòng (‘\n’) 23: cout
  12. 50: AddMatrix(A,B,C,M,N); 51: cout
  13. Hình 2.9: Kết quả của ví dụ 2.7 Một cách khác để cấp phát mảng hai chiều A gồm M dòng và N cột như sau: int ** A = new int *[M]; int * Tmp = new int[M*N]; for(int I=0;I
  14. 4: 5: void MyHandler(); 6: 7: unsigned long I = 0; 9; 8: void main() 9: { 10: int *A; 11: _new_handler = MyHandler; 12: for( ; ; ++I) 13: A = new int; 14: 15: } 16: 17: void MyHandler() 18: { 19: cout
  15. Thư viện cũng còn có một hàm được định nghĩa trong new.h là hàm có prototype sau : void ( * set_new_handler(void (* my_handler)() ))(); Hàm set_new_handler() dùng để gán một hàm cho _new_handler. Ví dụ 2.9: 1: #include 2: #include 3: #include 4: 5: void MyHandler(); 6: 7: int main(void) 8: { 9: 10: char *Ptr; 11: 12: set_new_handler(MyHandler); 13: Ptr = new char[64000u]; 14: set_new_handler(0); //Thiết lập lại giá trị mặc định 15: return 0; 16: } 17: 18: void MyHandler() 19: { 20: cout
  16. inline data_type function_name ( parameters ) { …………………………….. } Trong đó: data_type: Kiểu trả về của hàm. Function_name:Tên của hàm. Parameters: Các tham số của hàm. Ví dụ 2.10: Tính thể tích của hình lập phương 1: #include 2: inline float Cube(float S) 3: { 4: return S*S*S; 5: } 6: 7: int main() 8: { 9: coutSide; 12: cout
  17. 12: inline float Cube(float S) 13: { 14: return S*S*S; 15: } Các hàm đệ quy không được là hàm inline. II.11. Các giá trị tham số mặc định Một trong các đặc tính nổi bật nhất của C++ là khả năng định nghĩa các giá trị tham số mặc định cho các hàm. Bình thường khi gọi một hàm, chúng ta cần gởi một giá trị cho mỗi tham số đã được định nghĩa trong hàm đó, chẳng hạn chúng ta có đoạn chương trình sau: void MyDelay(long Loops); //prototype ……………………………….. void MyDelay(long Loops) { for(int I = 0; I < Loops; ++I) ; } Mỗi khi hàm MyDelay() được gọi chúng ta phải gởi cho nó một giá trị cho tham số Loops. Tuy nhiên, trong nhiều trường hợp chúng ta có thể nhận thấy rằng chúng ta luôn luôn gọi hàm MyDelay() với cùng một giá trị Loops nào đó. Muốn vậy chúng ta sẽ dùng giá trị mặc định cho tham số Loops, giả sử chúng ta muốn giá trị mặc định cho tham số Loops là 1000. Khi đó đoạn mã trên được viết lại như sau : void MyDelay(long Loops = 1000); //prototype ……………………………….. void MyDelay(long Loops) { for(int I = 0; I < Loops; ++I) ; } Mỗi khi gọi hàm MyDelay() mà không gởi một tham số tương ứng thì trình biên dịch sẽ tự động gán cho tham số Loops giá trị 1000. MyDelay(); // Loops có giá trị là 1000 MyDelay(5000); // Loops có giá trị là 5000 Giá trị mặc định cho tham số có thể là một hằng, một hàm, một biến hay một biểu thức. Ví dụ 2.11: Tính thể tích của hình hộp 1: #include 2: int BoxVolume(int Length = 1, int Width = 1, int Height = 1); 3: 4: int main() 5: { 6: cout
  18. 13:
  19. } Để hoán đổi giá trị hai biến A và B thì chúng ta gọi hàm như sau: Swap(&A, &B); Rõ ràng cách viết này không được thuận tiện lắm. Trong trường hợp này, C++ đưa ra một kiểu biến rất đặc biệt gọi là biến tham chiếu (reference variable). Một biến tham chiếu giống như là một bí danh của biến khác. Biến tham chiếu sẽ làm cho các hàm có thay đổi nội dung các tham số của nó được viết một cách thanh thoát hơn. Khi đó hàm Swap() được viết như sau: void Swap(int &X, int &Y); { int Temp = X; X = Y; Y = Temp ; } Chúng ta gọi hàm như sau : Swap(A, B); Với cách gọi hàm này, C++ tự gởi địa chỉ của A và B làm tham số cho hàm Swap(). Cách dùng biến tham chiếu cho tham số của C++ tương tự như các tham số được khai báo là Var trong ngôn ngữ Pascal. Tham số này được gọi là tham số kiểu tham chiếu (reference parameter). Như vậy biến tham chiếu có cú pháp như sau : data_type & variable_name; Trong đó: data_type: Kiểu dữ liệu của biến. variable_name: Tên của biến Khi dùng biến tham chiếu cho tham số chỉ có địa chỉ của nó được gởi đi chứ không phải là toàn bộ cấu trúc hay đối tượng đó như hình 2.14, điều này rất hữu dụng khi chúng ta gởi cấu trúc và đối tượng lớn cho một hàm. Hình 2.14: Một tham số kiểu tham chiếu nhận một tham chiếu tới một biến được chuyển cho tham số của hàm. Ví dụ 2.12: Chương trình hoán đổi giá trị của hai biến. 1: #include 2: //prototype
  20. 3 void Swap(int &X,int &Y); 4: 5: int main() 6: { 7: int X = 10, Y = 5; 8: cout
ADSENSE

CÓ THỂ BẠN MUỐN DOWNLOAD

 

Đồng bộ tài khoản
2=>2