24/05/2018, 21:10

Toán tử gán

(cho lớp) là một trường hợp đặc biệt so với các toán tử khác. Nếu trong lớp chưa định nghĩa một phương thức toán tử gán thì trình biên dịch sẽ phát sinh một toán tử gán mặc định để thực hiện câu lệnh gán 2 đối tượng của lớp, ví du: ...

(cho lớp) là một trường hợp đặc biệt so với các toán tử khác. Nếu trong lớp chưa định nghĩa một phương thức toán tử gán thì trình biên dịch sẽ phát sinh một toán tử gán mặc định để thực hiện câu lệnh gán 2 đối tượng của lớp, ví du:

Ht h1, h2(100,6);
    H1 = h2 ; // gán h2 cho h1
    

mặc định sẽ sẽ sao chép đối tượng nguồn (h2) vào đối tượng đích (h1) theo từng bit một.

Trong đa số các trường hợp khi lớp không có các thành phần con trỏ hay tham chiếu thì toán tử gán mặc định là đủ dùng và không cần định nghĩa một phương thức toán tử gán cho lớp. Nhưng đối với các lớp có thuộc tính con trỏ như lớp dt (đa thức), lớp ht (hình tròn) thì toán tử gán mặc định không thích hợp và việc xây dựng toán tử gán là cần thiết.

Cũng giống như các phương thức khác, phương thức toán tử gán dùng đối con trỏ this để biểu thị đối tượng đích và dùng một đối tường minh để biểu thị đối tượng nguồn. Vì trong thân của toán tử gán không nên làm việc với bản sao của đối tượng nguồn, mà phải làm việc trực tiếp với đối tượng nguồn, nên kiểu đối tường minh nhất thiết phải là kiểu tham chiếu đối tượng.

Phương thức toán tử gán có thể có hoặc không có giá trị trả về. Nếu không có giá trị trả về (kiểu void), thì khi viết chương trình không được phép viết câu lệnh gán liên tiếp nhiều đối tượng, như:

U = v = k = h ;
    

Nếu phương thức toán tử gán trả về tham chiếu của đối tượng nguồn, thì có thể dùng toán tử gán thể thực hiện các phép gán liên tiếp nhiều đối tượng.

Đối với lớp ht (trong mục trước), có thể xây dựng toán tử gán như sau:

Void ht::operator=(const ht &h)
    {
    R = h.r ; m = h.m ;
    Xhien = yhien = 0;
    Hienmh = 0 ;
    If (h.pht==null)
    Pht = null;
    Else
    {
    Int size;
    Size = imagesize(0,0,r+r,r+r);
    Pht = new char[size];
    Memcpy(pht,h.pht,size);
    }
    }
    

Với toán tử gán này, chỉ cho phép gán đối tượng nguồn cho một đối tượng đích.Như vậy câu lệnh sau là sai:

Ht u, v, h ;
    U = v = h ;
    

Bây giờ ta sửa lại toán gán để nó trả về tham chiếu đối tượng nguồn như sau:

Const ht & ht::operator=(const ht &h)
    {
    R = h.r ; m = h.m ;
    Xhien = yhien = 0;
    Hienmh = 0 ;
    If (h.pht==null)
    pht = null;
    Else
    {
    Int size;
    Size = imagesize(0,0,r+r,r+r);
    Pht = new char[size];
    Memcpy(pht,h.pht,size);
    }
    Return h ;
    }
    

Với toán tử gán mới này, ta có thể viết câu lệnh để gán đối tượng nguồn cho nhiều đối tượng đích. Như vậy các câu lệnh sau là được:

Ht u, v, h ;
    U = v = h ;
    

+ không tạo ra đối tượng mới, chỉ thực hiện phép gán giữa 2 đối tượng đã tồn tại.

+ Hàm tạo sao chép được dùng để tạo một đối tượng mới và gán nội dung của một đối tượng đã tồn tại cho đối tượng mới vừa tạo.

+ Nếu đã xây dựng toán tử gán mà lại dùng hàm tạo sao chép mặc định thì chưa đủ, vì việc khởi gán trong câu lệnh khai báo sẽ không gọi tới toán tử gán mà lại gọi tới hàm tạo sao chép.

+ Như vậy đối với lớp có thuộc tính con trỏ, thì ngoài hàm tạo, cần xây dựng thêm:

- Hàm huỷ

- Hàm tạo sao chép

- Phương thức toán tử gán

Không phải mọi câu lệnh chứa có dấu = đều gọi đến toán tử gán. Cần phân biệt 3 trường hợp: 1. Câu lệnh new (chứa dấu =) sẽ gọi đến hàm tạo, ví dụ:

Ht *h= new ht(50,6); // gọi đến hàm tạo có đối
2. Câu lệnh khai báo và khởi gán (dùng dấu =) sẽ gọi đến hàm tạo sao chép, ví dụ:

Ht k=*h; // gọi đến hàm tạo sao chep
3. Câu lệnh gán sẽ gọi đến toán tử gán, ví dụ:

Ht u;

U=*h; // gọi đến phương thức toán tử gán

Chương trình dưới đây định nghĩa lớp ht (hình tròn) và minh hoạ:

+ Hàm tạo và hàm huỷ

+ Phương thức toán tử gán có kiểu tham chiếu

+ Hàm tạo sao chép

+ Cách dùng con trỏ this trong hàm tạo sao chép

+ Cách dùng con trỏ _new_handler để kiểm tra việc cấp phát bộ nhớ.

//ct4_10.cpp
    // lop do hoa
    // ham huy
    // toan tu gan - tra ve tham chieu
    // ham tao sao chep
    // trong ham huy co the goi pt khac
    #include <conio.h>
    #include <iostream.h>
    #include <stdlib.h>
    #include <graphics.h>
    #include <new.h>
    #include <mem.h>
    Static void kiem_tra_bo_nho() ;
    Void ktdh();
    int xmax,ymax;
    Void kiem_tra_bo_nho()
    {
    Outtextxy(1,1,"loi bo nho");
    Getch();
    Closegraph();
    Exit(1);
    }
    Class ht
    {
    Private:
    Int r,m ;
    Int xhien,yhien;
    Char *pht;
    Int hienmh;
    Public:
    Ht();
    Ht(int r1,int m1=15);
    Ht(const ht &h);
    ~ht();
    Void hien(int x, int y);
    Void an();
    Const ht &operator=(const ht &h);
    };
    Const ht & ht::operator=(const ht &h)
    {
    // outtextxy(1,1,"gan"); getch();
    R = h.r ; m = h.m ;
    Xhien = yhien = 0;
    Hienmh = 0 ;
    If (h.pht==null)
    Pht = null;
    Else
    {
    Int size;
    Size = imagesize(0,0,r+r,r+r);
    Pht = new char[size];
    Memcpy(pht,h.pht,size);
    }
    Return h;
    }
    Ht::ht(const ht &h)
    {
    //outtextxy(300,1,"constructor sao chep"); getch();
    *this = h;
    }
    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 main()
    {
    _new_handler = kiem_tra_bo_nho ;
    Ktdh();
    ht *h= new ht(50,6); // gọi hàm tạo có đối
    H->hien(100,200);
    Ht k=*h; // gọi hàm tạo sao chép
    K.hien(200,200);
    Ht t,v,u;
    T = v = u = *h; // gọi toán tử gán
    U.hien(300,200);
    V.hien(400,200);
    T.hien(500,200);
    Getch();
    Closegraph();
    }
    

Chương trình trên sẽ vẽ 5 hình tròn trên màn hình. Điều gì sẽ xẩy ra nếu bỏ đi phương thức toán tử gán và hàm tạo sao chép?

+ Nếu bỏ cả hai, thì chỉ xuất hiên một hình tròn tại vị trí (100,200).

+ Nếu bỏ toán tử gán (giữ hàm tạo sao chép) thì chỉ xuất hiện 2 hình tròn tại các vị trí (100,200) và (200,200).

+ Nếu bỏ hàm tạo sao chép (giữ toán tử gán) thì xuất hiện 4 hình tròn.

0