24/05/2018, 14:35

Hàm huỷ (destructor)

Hàm huỷ là một hàm thành viên của lớp (phương thức) có chức năng ngược với hàm tạo. Hàm huỷ được gọi trước khi giải phóng (xoá bỏ) một đối tượng để thực hiện một số công việc có tính “dọn dẹp” trước khi đối tượng được huỷ bỏ, ví dụ ...

Hàm huỷ là một hàm thành viên của lớp (phương thức) có chức năng ngược với hàm tạo. Hàm huỷ được gọi trước khi giải phóng (xoá bỏ) một đối tượng để thực hiện một số công việc có tính “dọn dẹp” trước khi đối tượng được huỷ bỏ, ví dụ như giải phóng một vùng nhớ mà đối tượng đang quản lý, xoá đối tượng khỏi màn hình nếu như nó đang hiển thị, ...

Việc huỷ bỏ một đối tượng thường xẩy ra trong 2 trường hợp sau:

+ Trong các toán tử và các hàm giải phóng bộ nhớ, như delete, free,...

+ Giải phóng các biến, mảng cục bộ khi thoát khỏi hàm, phương thức.

Nếu trong lớp không định nghĩa hàm huỷ, thì một hàm huỷ mặc định không làm gì cả được phát sinh. Đối với nhiều lớp thì hàm huỷ mặc định là đủ, và không cần đưa vào một hàm huỷ mới.

Mỗi lớp chỉ có một hàm huỷ viết theo các quy tắc sau:

+ Kiểu của hàm: hàm huỷ cũng giống như hàm tạo là hàm không có kiểu, không có giá trị trả về.

+ Tên hàm: tên của hàm huỷ gồm một dẫu ngã (đứng trước) và tên lớp:

~tên_lớp

+ Đối: hàm huỷ không có đối

Ví dụ có thể xây dựng hàm huỷ cho lớp dt (đa thức) ở §3 như sau:

Class dt
    {
    Private:
    Int n; // bac da thuc
    Double *a; // tro toi vung nho chua cac he so da thuc 
    // a0, a1,...
    Public:
    ~dt()
    {
    This->n=0; 
    Delete this->a;
    }
    ...
    } ;
    

Khiếm khuyết của chương trình trong §3

Chương trình trong §3 định nghĩa lớp dt (đa thức) khá đầy đủ gồm:

+ Các hàm tạo

+ Các hàm toán tử nhập >>, xuất <<

+ Các hàm toán tử thực hiện các phép tính + - *

Tuy nhiên vẫn còn thiếu hàm huỷ để giải phóng vùng nhớ mà đối tượng kiểu dt (cần huỷ) đang quản lý.

Chúng ta hãy phân tích các khiếm khuyết của chương trình này:

+ Khi chương trình gọi tới một phương thức toán tử để thực hiện các phép tính cộng, trừ, nhân đa thức, thì một đối tượng trung gian được tạo ra. Một vùng nhớ được cấp phát và giao cho nó (đối tượng trung gian) quản lý.

+ Khi thực hiện xong phép tính sẽ ra khỏi phương thức. Đối tượng trung gian bị xoá, tuy nhiên chỉ vùng nhớ của các thuộc tính của đối tượng này được giải phóng. Còn vùng nhớ (chứa các hệ số của đa thức) mà đối tượng trung gian đang quản lý thì không hề bị giải phóng. Như vậy số vùng nhớ bị chiếm dụng vô ích sẽ tăng lên.

Cách khắc phục

Nhược điểm trên dễ dàng khắc phục bằng cách đưa vào lớp dt hàm huỷ viết trong 5.3 (mục trên).

Chương trình dưới đây gồm:

Lớp ht (hình tròn) với các thuộc tính:

Int r; // bán kính
    Int m ; // mầu hình tròn
    Int xhien,yhien; // vị trí hiển thị hình tròn trên màn hình
    Char *pht; // con trỏ trỏ tới vùng nhớ chứa ảnh hình tròn 
    Int hienmh; // trạng thái hiện (hienmh=1), ẩn (hienmh=0)
    

Các phương thức:

+ Hàm tạo không đối

Ht();
    

Thực hiện việc gán giá trị bằng 0 cho các thuộc tính của lớp.

+ Hàm tạo có đối

Ht(int r1,int m1=15);
    

Thực hiện các việc:

- Gán r1 cho r, m1 cho m

- Cấp phát bộ nhớ cho pht

- Vẽ hình tròn và lưu ảnh hình tròn vào vùng nhớ của pht

+ Hàm huỷ

~ht();
    

Thực hiện các việc:

- Xoá hình tròn khỏi màn hình (nếu đang hiển thị)

- Giải phóng bộ nhớ đã cấp cho pht

+ Phương thức

Void hien(int x, int y);

Có nhiệm vụ hiển thị hình tròn tại (x,y)

+ Phương thức

Void an();

Có nhiệm vụ làm ẩn hình tròn

Các hàm độc lập:

Void ktdh(); //khởi tạo đồ hoạ
    Void ve_bau_troi(); // vẽ bầu trời đầy sao
    Void ht_di_dong_xuong(); // vẽ một cặp 2 hình tròn di 
    // chuyển xuống
    Void ht_di_dong_len();// vẽ một cặp 2 hình tròn di 
    // chuyển lên trên
    

Nội dung chương trình là tạo ra các chuyển động xuống và lên của các hình tròn.

//ct4_09.cpp
    // lop do hoa
    // ham huy
    // trong ham huy co the goi pt khac
    #include <conio.h>
    #include <iostream.h>
    #include <math.h>
    #include <stdlib.h>
    #include <graphics.h>
    #include <dos.h>
    Void ktdh();
    Void ve_bau_troi();
    Void ht_di_dong_xuong();
    Void ht_di_dong_len();
    Int xmax,ymax;
    Class ht
    {
    Private:
    Int r,m ;
    Int xhien,yhien;
    Char *pht;
    Int hienmh;
    Public:
    Ht();
    Ht(int r1,int m1=15);
    ~ht();
    Void hien(int x, int y);
    Void an();
    };
    Ht:: ht()
    {
    R=m=hienmh=0;
    Xhien=yhien=0;
    Pht=null;
    }
    
    ht::ht(int r1,int m1)
    {
    R=r1; m=m1; hienmh=0;
    Xhien=yhien=0;
    If (r<0) r=0;
    If (r==0)
    {
    Pht=null;
    }
    Else
    {
    Int size; char *pmh;
    Size = imagesize(0,0,r+r,r+r);
    Pmh = new char[size];
    Getimage(0,0,r+r,r+r,pmh);
    Setcolor(m);
    Circle(r,r,r);
    Setfillstyle(1,m);
    Floodfill(r,r,m);
    Pht = new char[size];
    Getimage(0,0,r+r,r+r,pht);
    Putimage(0,0,pmh,copy_put);
    Delete pmh;
    Pmh=null;
    }
    }
    Void ht::hien(int x, int y)
    {
    If (pht!=null && !hienmh) // chua hien
    {
    Hienmh=1;
    Xhien=x; yhien=y;
    Putimage(x,y,pht,xor_put);
    }
    }
    Void ht::an()
    {
    If (hienmh) // dang hien
    {
    Hienmh=0;
    Putimage(xhien,yhien,pht,xor_put);
    }
    }
    Ht::~ht()
    {
    An();
    If (pht!=null)
    {
    Delete pht;
    Pht=null;
    }
    }
    Void ktdh()
    {
    Int mh=0,mode=0;
    Initgraph(&mh,&mode,"");
    Xmax = getmaxx();
    Ymax = getmaxy();
    }
    
    Void ve_bau_troi()
    {
    For (int i=0;i<2000;++i)
    Putpixel(random(xmax), random(ymax), 1+random(15));
    }
    Void ht_di_dong_xuong()
    {
    Ht h(50,4);
    Ht u(60,15);
    H.hien(0,0);
    U.hien(40,0);
    For (int x=0;x<=340;x+=10)
    {
    H.an();
    U.an();
    H.hien(x,x);
    Delay(200);
    U.hien(x+40,x);
    Delay(200);
    }
    }
    Void ht_di_dong_len()
    {
    Ht h(50,4);
    Ht u(60,15);
    H.hien(340,340);
    U.hien(380,340);
    For (int x=340;x>=0;x-=10)
    {
    H.an();
    U.an();
    H.hien(x,x);
    Delay(200);
    U.hien(x+40,x);
    Delay(200);
    }
    }
    Void main()
    {
    Ktdh();
    Ve_bau_troi();
    Ht_di_dong_xuong();
    Ht_di_dong_len();
    Getch();
    Closegraph();
    }
    

1. Trong thân hàm huỷ gọi tới phương thức an().

2. Điều gì xẩy ra khi bỏ đi hàm huỷ:

+ Khi gọi hàm ht_di_dong_xuong() thì có 2 đối tượng kiểu ht được tạo ra. Trong thân hàm sử dụng các đối tượng này để vẽ các hình tròn di chuyển xuống. Khi thoát khỏi hàm thì 2 đối tượng (tạo ra ở trên) được giải phóng. Vùng nhớ của các thuộc tính của chúng bị thu hồi, nhưng vùng nhớ cấp phát cho thuộc tính pht chưa được giải phóng và ảnh của 2 hình tròn (ở phía dưới màn hình) vẫn không được cất đi.

+ Điều tương tự xẩy ra sau khi ra khỏi hàm ht_di_dong_len() : vùng nhớ cấp phát cho thuộc tính pht chưa được giải phóng và ảnh của 2 hình tròn (ở phía trên màn hình) vẫn không được thu dọn.

0