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 /*…*/.
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
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:
#include <iostream.h>
int main()
{
int X = 200;
long Y = (long) X; //Chuyen doi kieu theo cach cua C long
Z = long(X); //Chuyen doi kieu theo cach moi cua C++
cout<< "X = "<<X<<"\n";
cout<< "Y = "<<Y<<"\n";
cout<< "Z = "<<Z<<"\n";
return 0;
}
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.
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.
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 <iostream.h>
2: int X = 5;
3: int main()
4: {
5: int X = 16;
6: cout<< "Bien X ben trong = "<<X<<"\n";
7: cout<< "Bien X ben ngoai = "<<::X<<"\n";
8: return 0;
9: }
Toán tử định phạm vi còn được dùng trong các định nghĩa hàm của các phương thức trong các lớp, để khai báo lớp chủ của các phương thức đang được định nghĩa đó. Toán tử định phạm vi còn có thể được dùng để phân biệt các thành phần trùng tên của các lớp cơ sở khác nhau
II.9. Toán tử new và delete
Trong các chương trình C, tất cả các cấp phát động bộ nhớ đều được xử lý thông qua các hàm thư viện như malloc(), calloc() và free(). C++ định nghĩa một phương thức mới để thực hiện việc cấp phát động bộ nhớ bằng cách dùng hai toán tử new và delete. Sử dụng hai toán tử này sẽ linh hoạt hơn rất nhiều so với các hàm thư viện của C.
1: #include <iostream.h>
2: #include <time.h>
3: #include <stdlib.h>
4: int main()
5: {
6: int N;
7: cout<<"Nhap vao so phan tu cua mang:";
8: cin>>N;
9: int *P=new int[N];
10: if (P==NULL)
11: {
12: cout<<"Khong con bo nho de cap phat\n";
13: return 1;
14: }
15: srand((unsigned)time(NULL));
16: for(int I=0;I<N;++I)
17: P[I]=rand()%100; //Tạo các số ngẫu nhiên từ 0 đến 99
18: cout<<"Mang truoc khi sap xep\n";
19: for(I=0;I<N;++I)
20: cout<<P[I]<<" ";
21: for(I=0;I<N-1;++I)
22: for(int J=I+1;J<N;++J)
23: if (P[I]>P[J])
24: {
25: int Temp=P[I];
26: P[I]=P[J];
27: P[J]=Temp;
28: }
29: cout<<"\nMang sau khi sap xep\n";
30: for(I=0;I<N;++I)
31: cout<<P[I]<<" ";
32: delete []P;
33: return 0;
34: }
II.10. Hàm inline
Một chương trình có cấu trúc tốt sử dụng các hàm để chia chương trình thành các đơn vị độc lập có logic riêng. Tuy nhiên, các hàm thường phải chứa một loạt các xử lý điểm vào (entry point): tham số phải được đẩy vào stack, một lệnh gọi phải được thực hiện và sau đó việc quay trở về cũng phải được thực hiện bằng cách giải phóng các tham số ra khỏi stack. Khi các xử lý điểm vào chậm chạp thường các lập trình viên C phải sử dụng cách chép lập lại các đoạn chương trình nếu muốn tăng hiệu quả. Để tránh khỏi phải xử lý điểm vào, C++ trang bị thêm từ khóa inline để loại việc gọi hàm. Khi đó trình biên dịch sẽ không biên dịch hàm này như một đoạn chương trình riêng biệt mà nó sẽ được chèn thẳng vào các chỗ mà hàm này được gọi. Điều này làm giảm việc xử lý điểm vào mà vẫn cho phép một chương trình được tổ chức dưới dạng có cấu trúc. Cú pháp của hàm inline như sau :
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.
Sử dụng hàm inline sẽ làm cho chương trình lớn lên vì trình biên dịch chèn đoạn chương trình vào các chỗ mà hàm này được gọi. Do đó thường các hàm inline thường là các hàm nhỏ, ít phức tạp
II.13.1. Đa năng hóa các hàm (Functions overloading)
Trong ngôn ngữ C cũng như mọi ngôn ngữ máy tính khác, mỗi hàm đều phải có một tên phân biệt. Đôi khi đây là một điều phiền toái. Chẳng hạn như trong ngôn ngữ C, có rất nhiều hàm trả về trị tuyệt đối của một tham số là số, vì cần thiết phải có tên phân biệt nên C phải có hàm riêng cho mỗi kiểu dữ liệu số, do vậy chúng ta có tới ba hàm khác nhau để trả về trị tuyệt đối của một tham số: int abs(int i); long labs(long l); double fabs(double d); Tất cả các hàm này đều cùng thực hiện một chứa năng nên chúng ta thấy điều này nghịch lý khi phải có ba tên khác nhau. C++ giải quyết điều này bằng cách cho phép chúng ta tạo ra các hàm khác nhau có cùng một tên. Đây chính là đa năng hóa hàm. Do đó trong C++ chúng ta có thể định nghĩa lại các hàm trả về trị tuyệt đối để thay thế các hàm trên như sau : int abs(int i); long abs(long l); double abs(double d);