Lập trình hướng đối tượng - 8

cout<<Toa do : <<x<<" "<<y<<"n";

}

void main()

{

clrscr();

point a(5,2); //Hợp lệ

a.display();

a.move(-2,4); a.display();

point b[10]; /*Hềt lỗi vì hàm tạo không tham số được gọi để tạo các đối tượng thành phần cđa */

getch();

Có thể bạn quan tâm!

Xem toàn bộ 256 trang tài liệu này.

}

Còn một giải pháp khác không cần định nghĩa thêm hàm tạo không tham số. Khi

Lập trình hướng đối tượng - 8

đó cần khai báo giá trị ngầm định cho các tham số cđa hàm tạo hai tham số: Ví dụ 2.9:

#include <iostream.h> class point

{

int x; int y; public:

point(int ox = 0,int oy = 0) {x=ox; y=oy;} void move(int dx,int dy) ;

void display();

};

void point::move(int dx, int dy)

{

x+=dx; y+=dy;

}

void point::display()

{

cout<<Toa do : <<x<<" "<<y<<"n";

}

void main()

{

clrscr();

point a(5,2); //Hợp lệ

a.display();

a.move(-2,4); a.display(); point b[10]; //Hợp lệ getch();

}


2.4.5. Hàm tạo sao chép

Xét các câu lệnh khai báo và khởi tạo giá trị cho một biến nguyên: int p;

int x = p;

Câu lệnh thứ hai khai báo một biến nguyên x và gán cho nó giá trị cđa biến nguyên p. Tương tự, ta cũng có thể khai báo một đối tượng và gán cho nó nội dung cđa một đối tượng cùng lớp đã tồn tại trước đó. Chẳng hạn:

point p(2,3);/*giả thiết lớp point có hàm tạo hai tham số*/

point q =p;

Dĩ nhiên hai đối tượng, mới q và cũ p có cùng nội dung. Khi một đối tượng được tạo ra (khai báo) thì một hàm tạo cđa lớp tương ứng sẽ được gọi. Hàm tạo được gọi khi khai báo và khởi tạo nội dung một đối tượng thông qua một đối tượng khác, gọi là hàm tạo sao chép. Nhiệm vụ cđa hàm tạo sao chép là tạo ra một đối tượng giống hệt một đối tượng đã có. Hàm tạo sao chép có vẻ thực hiện các công việc giống như phép gán, nhưng nếu để ý sẽ thấy giữa chúng có chút ít khác biệt; phép gán thực hiện việc sao chép nội dung từ đối tượng này sang đối tượng khác, do vậy cả hai đối tượng trong phép gán đều đã tồn tại:

point p(2,3);//giả thiết lớp point có hàm tạo hai tham số

point q;//giả thiết lớp point có hàm tạo không tham số

q = p;

Ngược lại, hàm tạo thực hiện đồng thời hai nhiệm vụ: tạo đối tượng và sao chép nội dung từ một đối tượng đã có sang đối tượng mới tạo ra đó.

Ngoài tình huống trên đây, còn có hai trường hợp cần dùng hàm tạo sao chép: truyền đối tượng cho hàm bằng tham trị hoặc hàm trả về một đối tượng nhằm tạo một đối tượng giống hệt một đối tượng cùng lớp đã có trước đó.

Hàm tạo sao chép ngầm định

Giống như hàm tạo ngầm định (hàm tạo không tham số), nếu không được mô tả tường minh, sẽ có một hàm tạo sao chép ngầm định do chương trình dịch cung cấp nhằm đảm bảo tính đúng đắn cđa chương trình trong các tình huống cần đến hàm tạo. Như vậy, trong khai báo cđa một lớp có ít nhất hai hàm tạo ngầm định: hàm tạo ngầm định và hàm tạo sao chép ngầm định.

Do là một hàm được tạo ra tự động nên hàm tạo sao chép ngầm định cũng chỉ thực hiện những thao tác tối thiểu (ngầm định): tạo giá trị cđa các thuộc tính trong đối tượng mới bằng các giá trị cđa các thuộc tính tương ứng trong đối tượng cũ. Nói chung, với các lớp không khai báo các thành phần dữ liệu động thì chỉ cần dùng hàm tạo sao chép ngầm định là đđ. Vấn đề sẽ khác đi khi cần đến các thao tác quản lý bộ nhớ động trong các đối tượng. Trong trường hợp này không được dùng hàm tạo sao chép ngầm định mà phải gọi hàm tạo sao chép tường minh.

Khai báo và định nghĩa hàm tạo sao chép tường minh

Xét các đối tượng thuộc lớp point. Câu lệnh: point q=p;

sẽ gọi đến hàm tạo sao chép. Ta cũng có thể viết theo cách khác như sau: point q(p);

từ cách viết trên có thể cho rằng dạng cđa hàm tạo sao chép cho lớp point có thể là: point (point);

hoặc

point(point &);

Ta nhận thấy dạng thứ nhất không dùng được vì việc gọi nó đòi hỏi phải truyền cho hàm một đối tượng như một tham trị, do đó gây ra đệ quy vô hạn lần.

Với dạng thứ hai ta đã thiết lập một tham chiều tới đối tượng như một tham số hình thức truyền cho hàm, nên có thể chấp nhận được.

Dạng khai báo cđa hàm tạo là:

point (point &); hoặc point(const point &);

trong đó từ khoá const trong khai báo tham số hình thức chỉ nhằm ngăn cấm mọi thay đổi nội dung cđa tham số truyền cho hàm.

Ví dụ 2.10:

Chương trình sau đây bổ sung thêm hàm tạo sao chép vào lớp point.

#include <conio.h>

#include <iostream.h>

class point

{

int x; int y; public:

point(int ox = 1,int oy =0)

{

cout<<"Tao doi tuong : "<<this<<endl; cout<<"Dung ham thiet lap hai tham son"; x=ox;y=oy;

}

/*Hàm tạo sao chép*/

point(point &p)

{

cout<<"Tao doi tuong : "<<this<<endl; cout<<"Dung ham thiet lap sao chepn"; x = p.x; y = p.y;

}

void move(int dx, int dy)

{

x+=dx; y+=dy;

}

void display();

};

void point::display()

{

cout<<"Toa do : "<<x<<" "<<y<<"n";

}

point fct(point a)

{

point b=a; b.move(2,3); return b;

}

void main()

{

clrscr();

point a(5,2);

a.display(); point b=fct(a); b.display();

getch(); }


2.4.6. Hàm huỷ

Hàm huỷ là một phương thức đặc biệt cđa lớp có chức năng ngược lại với hàm tạo. Hàm huỷ được gọi trước khi giải phóng (huỷ bỏ) một đối tượng. 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 đối tượng cục bộ khi kết thúc hàm, phương thức nếu trong lớp không khai báo hàm huỷ thì một hàm huỷ mặc định (không làm gì

cả) sẽ được phát sinh.

Một lớp chỉ có một hàm huỷ tường minh và được khai báo theo quy tắc sau:

Hàm huỷ không trả về giá trị nên không cần khai báo kiểu giá trị trả về cho hàm

Tên hàm huỷ được đặt gồm dấu ~ và tên lớp

Hàm huỷ không có tham số

Chú ý: Đối với các lớp đối tượng có các thuộc tính dạng con trỏ (thuộc tính cần

được cấp phát bộ nhớ trước khi lưu trữ giá trị) ta phải khai báo hàm tạo và hàm huỷ tường minh để cáp phát bộ nhớ khi đối tượng mới được tạo ra và thu hồi bộ nhớ khi

đối tượng bị huỷ.

Ví dụ 2.11:

#include<iostream.h>

#include<conio.h> class stack

{

private:

unsigned int max; unsigned int top; int *a;

public: stack();

~stack();//khai báo hàm huỷ void push(int x);

int full(); int empty(); int pop();

};

stack::stack()

{

top=0;

cout<<"KT stack:";cin>>max; a = new int[max];

}

stack::~stack()

{

top = 0;

max = 0; delete a;

}

int stack::empty()

{

if(top<=0) return 1;

else return 0;

}

int stack::full()


{

if(top>=max) return 1;

else return 0;

}

void stack::push(int x)

{

if(full())

{

cout<<"stack day"; return;

}

top=top+1; a[top]=x;

}

int stack::pop()

{

if(empty())

{

cout<<"Stack rong"; return 0;

}

top=top-1; return a[top+1];

}

void main()

{

stack s;//su dung lop stack int n; cout<<"n=";cin>>n; while(n>0)

{

s.push(n % 2); n = n / 2;

}


while(!s.empty()) cout<<s.pop(); } getch();


2.4.7. Toán tử gán

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 khai báo lớp ta chưa định nghĩa 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 hai đối tượng cđa lớp. Ví dụ:

point A,B(0,0); //A, B là hai đối tượng cđa lớp point A = B; //câu lệnh gán hai đối tượng cđa lớp point

Toán tử gán mặc định sẽ sao chép đối tượng nguồn B vào đối tượng đích A theo từng bit một.

Trong đa số trường hợp khi lớp không có thuộc tính kiểu con trỏ hay tham chiều thì ta có thể sử dụng toán tử gán mặc định, trong trường hợp ngược lại, ta nên khai báo toán tử gán tường minh theo quy tắc sau:

Toán tử gán dùng đối con trỏ this để biểu thị đối tượng đích (đối tượng ở về trái cđa phép gán) và dùng một đối tường minh có kiểu tham chiều

để biểu thị đối tượng nguồn (đối tượng ở về phải cđa phép gán).

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 sử dụng toán tử gán không

được phép viết câu lệnh gán nhiều đối tượng liên tiếp như x = y = z; Ví dụ 2.12:

Sử dụng toán tử gán cho lớp stack để gán hai stack cho nhau như sau:

#include<iostream.h>

#include<conio.h> class stack

{

private:

unsigned int max; unsigned int top; int *a;

public: stack();

~stack();

void push(int x);

int full(); int empty(); int pop();

void operator=(stack &x)

{

top = x.top; max = x.max;

a = new int[max]; a = x.a;

}

};

stack::stack()


{

top=0;

cout<<"KT stack:";cin>>max; a = new int[max];

}

stack::~stack()

{

top = 0;

max = 0; delete a;

}

int stack::empty()

{

if(top<=0) return 1;

else return 0;

}

int stack::full()

{

if(top>=max) return 1;

else return 0;

}

void stack::push(int x)

{

if(full())

{

cout<<"stack day"; return;

}

top=top+1; a[top]=x;

}

int stack::pop()

{

if(empty())

{

cout<<"Stack rong"; return 0;

}

top=top-1; return a[top+1];

}

void main()

{

stack s,t;//su dung lop stack int n;

cout<<"n=";cin>>n; while(n>0)

{

s.push(n % 2); n = n / 2;

}

t = s; //sử dụng toán tử gán while(!t.empty()) cout<<t.pop(); getch();

}


2.4.8. Phân loại các phương thức

Có thể phân chia các phương thức thành các nhóm

Nhóm các phương thức thông thường:

..... Xem trang tiếp theo?
⇦ Trang trước - Trang tiếp theo ⇨

Ngày đăng: 03/07/2022