2.2.9 Toán tử newdelete

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()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 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. Đoạn chương trình sau dùng để cấp phát vùng nhớ động theo lối cổ điển của C.

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<<"Khong con du bo nho de cap phat\n";

else

{

*P = 290;

cout<<*P<<"\n";

delete P;

}

Chúng ta nhận thấy rằng, cách viết của C++ sáng sủa và dễ sử dụng hơn nhiều. Toán tử new thay thế cho hàm malloc() hay calloc() của C có cú pháp như sau :

new type_name

new ( type_name )

new type_name initializer

new ( type_name ) initializer

Trong đó :

type_name: Mô tả kiểu dữ liệu được cấp phát. Nếu kiểu dữ liệu mô tả phức tạp, nó có thể được đặt bên trong các dấu ngoặc.

initializer: Giá trị khởi động của vùng nhớ được cấp phát.

Nếu toán tử new cấp phát không thành công thì nó sẽ trả về giá trị NULL.

Còn toán tử delete thay thế hàm free() của C, nó có cú pháp như sau :

delete pointer

delete [] pointer

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<<*P<<"\n";

delete P;

}

else

cout<<"Khong con du bo nho de cap phat\n";

Để cấp phát một mảng, chúng ta làm như sau :

int *P;

P = new int[10]; //Cấp phát mảng 10 số nguyên

if (P!=NULL)

{

for(int I = 0;I<10;++)

P[I]= I;

for(I = 0;I<10;++)

cout<<P[I]<<"\n";

delete []P;

}

else

cout<<"Khong con du bo nho de cap phat\n";

Chú ý: Đối với việc cấp phát mảng chúng ta không thể vừa cấp phát vừa khởi động giá trị cho chúng, chẳng hạn đoạn chương trình sau là sai :

int *P;

P = new (int[10])(3); //Sai !!!

Ví dụ 2.6: Chương trình tạo một mảng động, khởi động mảng này với các giá trị ngẫu nhiên và sắp xếp chúng.

CT2_6.CPP

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:  }

Chúng ta chạy ví dụ 2.6, kết quả ở hình 2.7

Hình 2.7: Kết quả của ví dụ 2.6

Ví dụ 2.7: Chương trình cộng hai ma trận trong đó mỗi ma trận được cấp phát động.

Chúng ta có thể xem mảng hai chiều như mảng một chiều như hình 2.8

Hình 2.8: Mảng hai chiều có thể xem như mảng một chiều.

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 :

CT2_7.CPP

1: #include <iostream.h>

2: #include <conio.h>

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:     cout<<"Nhap so dong cua ma tran:";

17:     cin>>M;

18:     cout<<"Nhap so cot cua ma tran:";

19:     cin>>N;

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<<"Khong con du bo nho!"<<endl;

24:         return 1;

25:     }

26:     //Cấp phát vùng nhớ cho ma trận B

27:     if (!AllocMatrix(&B,M,N))

28:     {

29:         cout<<"Khong con du bo nho!"<<endl;

30:         FreeMatrix(A);//Giải phóng vùng nhớ A

31:         return 1;

32:     }

33:     //Cấp phát vùng nhớ cho ma trận C

34:     if (!AllocMatrix(&C,M,N))

35:     {

36:         cout<<"Khong con du bo nho!"<<endl;

37:         FreeMatrix(A);//Giải phóng vùng nhớ A

38:         FreeMatrix(B);//Giải phóng vùng nhớ B

39:         return 1;

40:     }

41:     cout<<"Nhap ma tran thu 1"<<endl;

42:     InputMatrix(A,M,N,'A');

43:     cout<<"Nhap ma tran thu 2"<<endl;

44:     InputMatrix(B,M,N,'B');

45:     clrscr();

46:     cout<<"Ma tran thu 1"<<endl;

47:     DisplayMatrix(A,M,N);

48:     cout<<"Ma tran thu 2"<<endl;

49:     DisplayMatrix(B,M,N);

50:     AddMatrix(A,B,C,M,N);

51:     cout<<"Tong hai ma tran"<<endl;

52:     DisplayMatrix(C,M,N);

53:     FreeMatrix(A);//Giải phóng vùng nhớ A

54:     FreeMatrix(B);//Giải phóng vùng nhớ B

55:     FreeMatrix(C);//Giải phóng vùng nhớ C

56:     return 0;

57: }

68: //Cộng hai ma trận

69: void AddMatrix(int *A,int *B,int*C,int M,int N)

70: {

71:     for(int I=0;I<M*N;++I)

72:     C[I] = A[I] + B[I];

73: }

74: //Cấp phát vùng nhớ cho ma trận

75: int AllocMatrix(int **A,int M,int N)

76: {

77:     *A = new int [M*N];

78:     if (*A == NULL)

79:         return 0;

80:     return 1;

81: }

82: //Giải phóng vùng nhớ

83: void FreeMatrix(int *A)

84: {

85:     if (A!=NULL)

86:         delete [] A;

87: }

88: //Nhập các giá trị của ma trận

89: void InputMatrix(int *A,int M,int N,char Symbol)

90: {

91:     for(int I=0;I<M;++I)

92:     for(int J=0;J<N;++J)

93     {

94:         cout<<Symbol<<"["<<I<<"]["<<J<<"]=";

95:         cin>>A[I*N+J];

96:     }

97: }

100: //Hiển thị ma trận

101: void DisplayMatrix(int *A,int M,int N)

102: {

103:    for(int I=0;I<M;++I)

104:    {

105:      for(int J=0;J<N;++J)

106:     {

107:        out.width(7);//Hien thi canh le phai voi chieu dai 7 ky tu

108:        cout<<A[I*N+J];

109:       }

110:    cout<<endl;

111:    }

112: }

Chúng ta chạy ví du 2.7 , kết quả ở hình 2.9

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<M;++I)

{

A[I]=Tmp;

Tmp+=N;

}

//Thao tác trên mảng hai chiều A

…………………..

delete [] *A;

delete [] A;

Toán tử new còn có một thuận lợi khác, đó là tất cả các lỗi cấp phát động đều có thể bắt được bằng một hàm xử lý lỗi do người dùng tự định nghĩa. C++ có định nghĩa một con trỏ (pointer) trỏ đến hàm đặc biệt. Khi toán tử new được sử dụng để cấp phát động và một lỗi xảy ra do cấp phát, C++ tự gọi đến hàm được chỉ bởi con trỏ này. Định nghĩa của con trỏ này như sau:

typedef void (*pvf)();

pvf _new_handler(pvf p);

Điều này có nghĩa là con trỏ _new_handler là con trỏ trỏ đến hàm không có tham số và không trả về giá trị. Sau khi chúng ta định nghĩa hàm như vậy và gán địa chỉ của nó cho _new_handler chúng ta có thể bắt được tất cả các lỗi do cấp phát động.

Ví dụ 2.8:

CT2_8.CPP

1: #include <iostream.h>

2: #include <stdlib.h>

3: #include <new.h>

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<<"Lan cap phat thu "<<I<<endl;

20:    cout<<"Khong con du bo nho!"<<endl;

21:    exit(1);

22: }

Sử dụng con trỏ _new_handler chúng ta phải include file new.h như ở dòng 3. Chúng ta chạy ví dụ 2.8, kết quả ở hình 2.10.

Hình 2.10: Kết quả của ví dụ 2.8

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:

CT2_9.CPP

1: #include <iostream.h>

2: #include <new.h>

3: #include <stdlib.h>

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 <<endl<<"Khong con du bo nho";

21:    exit(1);

22 }

Chúng ta chạy ví dụ 2.9, kết quả ở hình 2.11

Hình 2.11: Kết quả của ví dụ 2.9

Tiếp theo phần 2